Use DeviceInfo in velbus (#58622)
This commit is contained in:
parent
0edb0c9bc9
commit
089353e949
5 changed files with 142 additions and 36 deletions
|
@ -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(),
|
||||
)
|
||||
|
|
31
tests/components/velbus/conftest.py
Normal file
31
tests/components/velbus/conftest.py
Normal 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
|
3
tests/components/velbus/const.py
Normal file
3
tests/components/velbus/const.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""Constants for the Velbus tests."""
|
||||
PORT_SERIAL = "/dev/ttyACME100"
|
||||
PORT_TCP = "127.0.1.0.1:3788"
|
|
@ -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"}
|
||||
|
|
56
tests/components/velbus/test_init.py
Normal file
56
tests/components/velbus/test_init.py
Normal 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"
|
Loading…
Add table
Reference in a new issue