Use DeviceInfo in velbus (#58622)

This commit is contained in:
epenet 2021-11-08 17:26:00 +01:00 committed by GitHub
parent 0edb0c9bc9
commit 089353e949
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 142 additions and 36 deletions

View file

@ -9,8 +9,10 @@ import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.entity import DeviceInfo, Entity
from .const import (
CONF_INTERFACE,
@ -58,6 +60,22 @@ async def velbus_connect_task(
await controller.connect()
def _migrate_device_identifiers(hass: HomeAssistant, entry_id: str) -> None:
"""Migrate old device indentifiers."""
dev_reg = device_registry.async_get(hass)
devices: list[DeviceEntry] = device_registry.async_entries_for_config_entry(
dev_reg, entry_id
)
for device in devices:
old_identifier = list(next(iter(device.identifiers)))
if len(old_identifier) > 2:
new_identifier = {(old_identifier.pop(0), old_identifier.pop(0))}
_LOGGER.debug(
"migrate identifier '%s' to '%s'", device.identifiers, new_identifier
)
dev_reg.async_update_device(device.id, new_identifiers=new_identifier)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Establish connection with velbus."""
hass.data.setdefault(DOMAIN, {})
@ -72,6 +90,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
velbus_connect_task(controller, hass, entry.entry_id)
)
_migrate_device_identifiers(hass, entry.entry_id)
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
if hass.services.has_service(DOMAIN, SERVICE_SCAN):
@ -176,18 +196,14 @@ class VelbusEntity(Entity):
self.async_write_ha_state()
@property
def device_info(self):
def device_info(self) -> DeviceInfo:
"""Return the device info."""
return {
"identifiers": {
(
DOMAIN,
self._channel.get_module_address(),
self._channel.get_module_serial(),
)
return DeviceInfo(
identifiers={
(DOMAIN, self._channel.get_module_address()),
},
"name": self._channel.get_full_name(),
"manufacturer": "Velleman",
"model": self._channel.get_module_type_name(),
"sw_version": self._channel.get_module_sw_version(),
}
manufacturer="Velleman",
model=self._channel.get_module_type_name(),
name=self._channel.get_full_name(),
sw_version=self._channel.get_module_sw_version(),
)

View file

@ -0,0 +1,31 @@
"""Fixtures for the Velbus tests."""
from unittest.mock import AsyncMock, patch
import pytest
from homeassistant.components.velbus.const import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME, CONF_PORT
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
from tests.components.velbus.const import PORT_TCP
@pytest.fixture(name="controller")
def mock_controller():
"""Mock a successful velbus controller."""
controller = AsyncMock()
with patch("velbusaio.controller.Velbus", return_value=controller):
yield controller
@pytest.fixture(name="config_entry")
def mock_config_entry(hass: HomeAssistant) -> ConfigEntry:
"""Create and register mock config entry."""
config_entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_PORT: PORT_TCP, CONF_NAME: "velbus home"},
)
config_entry.add_to_hass(hass)
return config_entry

View file

@ -0,0 +1,3 @@
"""Constants for the Velbus tests."""
PORT_SERIAL = "/dev/ttyACME100"
PORT_TCP = "127.0.1.0.1:3788"

View file

@ -7,36 +7,36 @@ from velbusaio.exceptions import VelbusConnectionFailed
from homeassistant import data_entry_flow
from homeassistant.components.velbus import config_flow
from homeassistant.const import CONF_NAME, CONF_PORT
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
PORT_SERIAL = "/dev/ttyACME100"
PORT_TCP = "127.0.1.0.1:3788"
from .const import PORT_SERIAL, PORT_TCP
@pytest.fixture(name="controller_assert")
def mock_controller_assert():
@pytest.fixture(autouse=True)
def override_async_setup_entry() -> AsyncMock:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.velbus.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture(name="controller_connection_failed")
def mock_controller_connection_failed():
"""Mock the velbus controller with an assert."""
with patch("velbusaio.controller.Velbus", side_effect=VelbusConnectionFailed()):
yield
@pytest.fixture(name="controller")
def mock_controller():
"""Mock a successful velbus controller."""
controller = AsyncMock()
with patch("velbusaio.controller.Velbus", return_value=controller):
yield controller
def init_config_flow(hass):
def init_config_flow(hass: HomeAssistant):
"""Init a configuration flow."""
flow = config_flow.VelbusConfigFlow()
flow.hass = hass
return flow
async def test_user(hass, controller):
@pytest.mark.usefixtures("controller")
async def test_user(hass: HomeAssistant):
"""Test user config."""
flow = init_config_flow(hass)
@ -59,7 +59,8 @@ async def test_user(hass, controller):
assert result["data"][CONF_PORT] == PORT_TCP
async def test_user_fail(hass, controller_assert):
@pytest.mark.usefixtures("controller_connection_failed")
async def test_user_fail(hass: HomeAssistant):
"""Test user config."""
flow = init_config_flow(hass)
@ -76,7 +77,8 @@ async def test_user_fail(hass, controller_assert):
assert result["errors"] == {CONF_PORT: "cannot_connect"}
async def test_import(hass, controller):
@pytest.mark.usefixtures("controller")
async def test_import(hass: HomeAssistant):
"""Test import step."""
flow = init_config_flow(hass)
@ -85,12 +87,10 @@ async def test_import(hass, controller):
assert result["title"] == "velbus_import"
async def test_abort_if_already_setup(hass):
@pytest.mark.usefixtures("config_entry")
async def test_abort_if_already_setup(hass: HomeAssistant):
"""Test we abort if Daikin is already setup."""
flow = init_config_flow(hass)
MockConfigEntry(
domain="velbus", data={CONF_PORT: PORT_TCP, CONF_NAME: "velbus home"}
).add_to_hass(hass)
result = await flow.async_step_import(
{CONF_PORT: PORT_TCP, CONF_NAME: "velbus import test"}

View file

@ -0,0 +1,56 @@
"""Tests for the Velbus component initialisation."""
import pytest
from homeassistant.components.velbus.const import DOMAIN
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import HomeAssistant
from tests.common import mock_device_registry
@pytest.mark.usefixtures("controller")
async def test_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"""Test being able to unload an entry."""
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert config_entry.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.NOT_LOADED
assert not hass.data.get(DOMAIN)
@pytest.mark.usefixtures("controller")
async def test_device_identifier_migration(
hass: HomeAssistant, config_entry: ConfigEntry
):
"""Test being able to unload an entry."""
original_identifiers = {(DOMAIN, "module_address", "module_serial")}
target_identifiers = {(DOMAIN, "module_address")}
device_registry = mock_device_registry(hass)
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers=original_identifiers,
name="channel_name",
manufacturer="Velleman",
model="module_type_name",
sw_version="module_sw_version",
)
assert device_registry.async_get_device(original_identifiers)
assert not device_registry.async_get_device(target_identifiers)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert not device_registry.async_get_device(original_identifiers)
device_entry = device_registry.async_get_device(target_identifiers)
assert device_entry
assert device_entry.name == "channel_name"
assert device_entry.manufacturer == "Velleman"
assert device_entry.model == "module_type_name"
assert device_entry.sw_version == "module_sw_version"