Register LCN devices in device registry (#53143)
This commit is contained in:
parent
8d05813c97
commit
b34eb53914
9 changed files with 258 additions and 28 deletions
|
@ -8,6 +8,8 @@ import pypck
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
CONF_ADDRESS,
|
||||||
|
CONF_DOMAIN,
|
||||||
CONF_IP_ADDRESS,
|
CONF_IP_ADDRESS,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
|
@ -16,16 +18,27 @@ from homeassistant.const import (
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import CONF_DIM_MODE, CONF_SK_NUM_TRIES, CONNECTION, DOMAIN, PLATFORMS
|
from .const import (
|
||||||
|
CONF_DIM_MODE,
|
||||||
|
CONF_DOMAIN_DATA,
|
||||||
|
CONF_SK_NUM_TRIES,
|
||||||
|
CONNECTION,
|
||||||
|
DOMAIN,
|
||||||
|
PLATFORMS,
|
||||||
|
)
|
||||||
from .helpers import (
|
from .helpers import (
|
||||||
|
AddressType,
|
||||||
DeviceConnectionType,
|
DeviceConnectionType,
|
||||||
InputType,
|
InputType,
|
||||||
|
async_update_config_entry,
|
||||||
generate_unique_id,
|
generate_unique_id,
|
||||||
|
get_device_model,
|
||||||
import_lcn_config,
|
import_lcn_config,
|
||||||
|
register_lcn_address_devices,
|
||||||
|
register_lcn_host_device,
|
||||||
)
|
)
|
||||||
from .schemas import CONFIG_SCHEMA # noqa: F401
|
from .schemas import CONFIG_SCHEMA # noqa: F401
|
||||||
from .services import SERVICES
|
from .services import SERVICES
|
||||||
|
@ -96,12 +109,12 @@ async def async_setup_entry(
|
||||||
hass.data[DOMAIN][config_entry.entry_id] = {
|
hass.data[DOMAIN][config_entry.entry_id] = {
|
||||||
CONNECTION: lcn_connection,
|
CONNECTION: lcn_connection,
|
||||||
}
|
}
|
||||||
|
# Update config_entry with LCN device serials
|
||||||
|
await async_update_config_entry(hass, config_entry)
|
||||||
|
|
||||||
# remove orphans from entity registry which are in ConfigEntry but were removed
|
# register/update devices for host, modules and groups in device registry
|
||||||
# from configuration.yaml
|
register_lcn_host_device(hass, config_entry)
|
||||||
if config_entry.source == config_entries.SOURCE_IMPORT:
|
register_lcn_address_devices(hass, config_entry)
|
||||||
entity_registry = await er.async_get_registry(hass)
|
|
||||||
entity_registry.async_clear_config_entry(config_entry.entry_id)
|
|
||||||
|
|
||||||
# forward config_entry to components
|
# forward config_entry to components
|
||||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||||
|
@ -150,17 +163,38 @@ class LcnEntity(Entity):
|
||||||
self._unregister_for_inputs: Callable | None = None
|
self._unregister_for_inputs: Callable | None = None
|
||||||
self._name: str = config[CONF_NAME]
|
self._name: str = config[CONF_NAME]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def address(self) -> AddressType:
|
||||||
|
"""Return LCN address."""
|
||||||
|
return (
|
||||||
|
self.device_connection.seg_id,
|
||||||
|
self.device_connection.addr_id,
|
||||||
|
self.device_connection.is_group,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self) -> str:
|
def unique_id(self) -> str:
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
unique_device_id = generate_unique_id(
|
return generate_unique_id(
|
||||||
(
|
self.entry_id, self.address, self.config[CONF_RESOURCE]
|
||||||
self.device_connection.seg_id,
|
|
||||||
self.device_connection.addr_id,
|
|
||||||
self.device_connection.is_group,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return f"{self.entry_id}-{unique_device_id}-{self.config[CONF_RESOURCE]}"
|
|
||||||
|
@property
|
||||||
|
def device_info(self) -> DeviceInfo | None:
|
||||||
|
"""Return device specific attributes."""
|
||||||
|
address = f"{'g' if self.address[2] else 'm'}{self.address[0]:03d}{self.address[1]:03d}"
|
||||||
|
model = f"LCN {get_device_model(self.config[CONF_DOMAIN], self.config[CONF_DOMAIN_DATA])}"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"identifiers": {(DOMAIN, self.unique_id)},
|
||||||
|
"name": f"{address}.{self.config[CONF_RESOURCE]}",
|
||||||
|
"model": model,
|
||||||
|
"manufacturer": "Issendorff",
|
||||||
|
"via_device": (
|
||||||
|
DOMAIN,
|
||||||
|
generate_unique_id(self.entry_id, self.config[CONF_ADDRESS]),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self) -> bool:
|
def should_poll(self) -> bool:
|
||||||
|
|
|
@ -15,6 +15,7 @@ from homeassistant.const import (
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import CONF_DIM_MODE, CONF_SK_NUM_TRIES, DOMAIN
|
from .const import CONF_DIM_MODE, CONF_SK_NUM_TRIES, DOMAIN
|
||||||
|
@ -93,6 +94,14 @@ class LcnFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
entry = get_config_entry(self.hass, data)
|
entry = get_config_entry(self.hass, data)
|
||||||
if entry:
|
if entry:
|
||||||
entry.source = config_entries.SOURCE_IMPORT
|
entry.source = config_entries.SOURCE_IMPORT
|
||||||
|
|
||||||
|
# Cleanup entity and device registry, if we imported from configuration.yaml to
|
||||||
|
# remove orphans when entities were removed from configuration
|
||||||
|
entity_registry = er.async_get(self.hass)
|
||||||
|
entity_registry.async_clear_config_entry(entry.entry_id)
|
||||||
|
device_registry = dr.async_get(self.hass)
|
||||||
|
device_registry.async_clear_config_entry(entry.entry_id)
|
||||||
|
|
||||||
self.hass.config_entries.async_update_entry(entry, data=data)
|
self.hass.config_entries.async_update_entry(entry, data=data)
|
||||||
return self.async_abort(reason="existing_configuration_updated")
|
return self.async_abort(reason="existing_configuration_updated")
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Helpers for LCN component."""
|
"""Helpers for LCN component."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from itertools import chain
|
||||||
import re
|
import re
|
||||||
from typing import Tuple, Type, Union, cast
|
from typing import Tuple, Type, Union, cast
|
||||||
|
|
||||||
|
@ -22,19 +24,23 @@ from homeassistant.const import (
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_SENSORS,
|
CONF_SENSORS,
|
||||||
|
CONF_SOURCE,
|
||||||
CONF_SWITCHES,
|
CONF_SWITCHES,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
BINSENSOR_PORTS,
|
||||||
CONF_CLIMATES,
|
CONF_CLIMATES,
|
||||||
CONF_CONNECTIONS,
|
CONF_CONNECTIONS,
|
||||||
CONF_DIM_MODE,
|
CONF_DIM_MODE,
|
||||||
CONF_DOMAIN_DATA,
|
CONF_DOMAIN_DATA,
|
||||||
CONF_HARDWARE_SERIAL,
|
CONF_HARDWARE_SERIAL,
|
||||||
CONF_HARDWARE_TYPE,
|
CONF_HARDWARE_TYPE,
|
||||||
|
CONF_OUTPUT,
|
||||||
CONF_RESOURCE,
|
CONF_RESOURCE,
|
||||||
CONF_SCENES,
|
CONF_SCENES,
|
||||||
CONF_SK_NUM_TRIES,
|
CONF_SK_NUM_TRIES,
|
||||||
|
@ -42,6 +48,13 @@ from .const import (
|
||||||
CONNECTION,
|
CONNECTION,
|
||||||
DEFAULT_NAME,
|
DEFAULT_NAME,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
LED_PORTS,
|
||||||
|
LOGICOP_PORTS,
|
||||||
|
OUTPUT_PORTS,
|
||||||
|
S0_INPUTS,
|
||||||
|
SETPOINTS,
|
||||||
|
THRESHOLDS,
|
||||||
|
VARIABLES,
|
||||||
)
|
)
|
||||||
|
|
||||||
# typing
|
# typing
|
||||||
|
@ -92,10 +105,43 @@ def get_resource(domain_name: str, domain_data: ConfigType) -> str:
|
||||||
raise ValueError("Unknown domain")
|
raise ValueError("Unknown domain")
|
||||||
|
|
||||||
|
|
||||||
def generate_unique_id(address: AddressType) -> str:
|
def get_device_model(domain_name: str, domain_data: ConfigType) -> str:
|
||||||
|
"""Return the model for the specified domain_data."""
|
||||||
|
if domain_name in ("switch", "light"):
|
||||||
|
return "Output" if domain_data[CONF_OUTPUT] in OUTPUT_PORTS else "Relay"
|
||||||
|
if domain_name in ("binary_sensor", "sensor"):
|
||||||
|
if domain_data[CONF_SOURCE] in BINSENSOR_PORTS:
|
||||||
|
return "Binary Sensor"
|
||||||
|
if domain_data[CONF_SOURCE] in chain(
|
||||||
|
VARIABLES, SETPOINTS, THRESHOLDS, S0_INPUTS
|
||||||
|
):
|
||||||
|
return "Variable"
|
||||||
|
if domain_data[CONF_SOURCE] in LED_PORTS:
|
||||||
|
return "Led"
|
||||||
|
if domain_data[CONF_SOURCE] in LOGICOP_PORTS:
|
||||||
|
return "Logical Operation"
|
||||||
|
return "Key"
|
||||||
|
if domain_name == "cover":
|
||||||
|
return "Motor"
|
||||||
|
if domain_name == "climate":
|
||||||
|
return "Regulator"
|
||||||
|
if domain_name == "scene":
|
||||||
|
return "Scene"
|
||||||
|
raise ValueError("Unknown domain")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_unique_id(
|
||||||
|
entry_id: str,
|
||||||
|
address: AddressType,
|
||||||
|
resource: str | None = None,
|
||||||
|
) -> str:
|
||||||
"""Generate a unique_id from the given parameters."""
|
"""Generate a unique_id from the given parameters."""
|
||||||
|
unique_id = entry_id
|
||||||
is_group = "g" if address[2] else "m"
|
is_group = "g" if address[2] else "m"
|
||||||
return f"{is_group}{address[0]:03d}{address[1]:03d}"
|
unique_id += f"-{is_group}{address[0]:03d}{address[1]:03d}"
|
||||||
|
if resource:
|
||||||
|
unique_id += f"-{resource}".lower()
|
||||||
|
return unique_id
|
||||||
|
|
||||||
|
|
||||||
def import_lcn_config(lcn_config: ConfigType) -> list[ConfigType]:
|
def import_lcn_config(lcn_config: ConfigType) -> list[ConfigType]:
|
||||||
|
@ -200,6 +246,109 @@ def import_lcn_config(lcn_config: ConfigType) -> list[ConfigType]:
|
||||||
return list(data.values())
|
return list(data.values())
|
||||||
|
|
||||||
|
|
||||||
|
def register_lcn_host_device(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||||
|
"""Register LCN host for given config_entry in device registry."""
|
||||||
|
device_registry = dr.async_get(hass)
|
||||||
|
|
||||||
|
device_registry.async_get_or_create(
|
||||||
|
config_entry_id=config_entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, config_entry.entry_id)},
|
||||||
|
manufacturer="Issendorff",
|
||||||
|
name=config_entry.title,
|
||||||
|
model="PCHK",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register_lcn_address_devices(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Register LCN modules and groups defined in config_entry as devices in device registry.
|
||||||
|
|
||||||
|
The name of all given device_connections is collected and the devices
|
||||||
|
are updated.
|
||||||
|
"""
|
||||||
|
device_registry = dr.async_get(hass)
|
||||||
|
|
||||||
|
host_identifiers = (DOMAIN, config_entry.entry_id)
|
||||||
|
|
||||||
|
for device_config in config_entry.data[CONF_DEVICES]:
|
||||||
|
address = device_config[CONF_ADDRESS]
|
||||||
|
device_name = device_config[CONF_NAME]
|
||||||
|
identifiers = {(DOMAIN, generate_unique_id(config_entry.entry_id, address))}
|
||||||
|
|
||||||
|
if device_config[CONF_ADDRESS][2]: # is group
|
||||||
|
device_model = f"LCN group (g{address[0]:03d}{address[1]:03d})"
|
||||||
|
sw_version = None
|
||||||
|
else: # is module
|
||||||
|
hardware_type = device_config[CONF_HARDWARE_TYPE]
|
||||||
|
if hardware_type in pypck.lcn_defs.HARDWARE_DESCRIPTIONS:
|
||||||
|
hardware_name = pypck.lcn_defs.HARDWARE_DESCRIPTIONS[hardware_type]
|
||||||
|
else:
|
||||||
|
hardware_name = pypck.lcn_defs.HARDWARE_DESCRIPTIONS[-1]
|
||||||
|
device_model = f"{hardware_name} (m{address[0]:03d}{address[1]:03d})"
|
||||||
|
sw_version = f"{device_config[CONF_SOFTWARE_SERIAL]:06X}"
|
||||||
|
|
||||||
|
device_registry.async_get_or_create(
|
||||||
|
config_entry_id=config_entry.entry_id,
|
||||||
|
identifiers=identifiers,
|
||||||
|
via_device=host_identifiers,
|
||||||
|
manufacturer="Issendorff",
|
||||||
|
sw_version=sw_version,
|
||||||
|
name=device_name,
|
||||||
|
model=device_model,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_update_device_config(
|
||||||
|
device_connection: DeviceConnectionType, device_config: ConfigType
|
||||||
|
) -> None:
|
||||||
|
"""Fill missing values in device_config with infos from LCN bus."""
|
||||||
|
is_group = device_config[CONF_ADDRESS][2]
|
||||||
|
|
||||||
|
# fetch serial info if device is module
|
||||||
|
if not is_group: # is module
|
||||||
|
await device_connection.serial_known
|
||||||
|
if device_config[CONF_HARDWARE_SERIAL] == -1:
|
||||||
|
device_config[CONF_HARDWARE_SERIAL] = device_connection.hardware_serial
|
||||||
|
if device_config[CONF_SOFTWARE_SERIAL] == -1:
|
||||||
|
device_config[CONF_SOFTWARE_SERIAL] = device_connection.software_serial
|
||||||
|
if device_config[CONF_HARDWARE_TYPE] == -1:
|
||||||
|
device_config[CONF_HARDWARE_TYPE] = device_connection.hardware_type.value
|
||||||
|
|
||||||
|
# fetch name if device is module
|
||||||
|
if device_config[CONF_NAME] != "":
|
||||||
|
return
|
||||||
|
|
||||||
|
device_name = ""
|
||||||
|
if not is_group:
|
||||||
|
device_name = await device_connection.request_name()
|
||||||
|
if is_group or device_name == "":
|
||||||
|
module_type = "Group" if is_group else "Module"
|
||||||
|
device_name = (
|
||||||
|
f"{module_type} "
|
||||||
|
f"{device_config[CONF_ADDRESS][0]:03d}/"
|
||||||
|
f"{device_config[CONF_ADDRESS][1]:03d}"
|
||||||
|
)
|
||||||
|
device_config[CONF_NAME] = device_name
|
||||||
|
|
||||||
|
|
||||||
|
async def async_update_config_entry(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Fill missing values in config_entry with infos from LCN bus."""
|
||||||
|
coros = []
|
||||||
|
for device_config in config_entry.data[CONF_DEVICES]:
|
||||||
|
device_connection = get_device_connection(
|
||||||
|
hass, device_config[CONF_ADDRESS], config_entry
|
||||||
|
)
|
||||||
|
coros.append(async_update_device_config(device_connection, device_config))
|
||||||
|
|
||||||
|
await asyncio.gather(*coros)
|
||||||
|
|
||||||
|
# schedule config_entry for save
|
||||||
|
hass.config_entries.async_update_entry(config_entry)
|
||||||
|
|
||||||
|
|
||||||
def has_unique_host_names(hosts: list[ConfigType]) -> list[ConfigType]:
|
def has_unique_host_names(hosts: list[ConfigType]) -> list[ConfigType]:
|
||||||
"""Validate that all connection names are unique.
|
"""Validate that all connection names are unique.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Support for LCN sensors."""
|
"""Support for LCN sensors."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from itertools import chain
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
import pypck
|
import pypck
|
||||||
|
@ -38,9 +39,8 @@ def create_lcn_sensor_entity(
|
||||||
hass, entity_config[CONF_ADDRESS], config_entry
|
hass, entity_config[CONF_ADDRESS], config_entry
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if entity_config[CONF_DOMAIN_DATA][CONF_SOURCE] in chain(
|
||||||
entity_config[CONF_DOMAIN_DATA][CONF_SOURCE]
|
VARIABLES, SETPOINTS, THRESHOLDS, S0_INPUTS
|
||||||
in VARIABLES + SETPOINTS + THRESHOLDS + S0_INPUTS
|
|
||||||
):
|
):
|
||||||
return LcnVariableSensor(
|
return LcnVariableSensor(
|
||||||
entity_config, config_entry.entry_id, device_connection
|
entity_config, config_entry.entry_id, device_connection
|
||||||
|
|
|
@ -21,8 +21,14 @@ class MockModuleConnection(ModuleConnection):
|
||||||
status_request_handler = AsyncMock()
|
status_request_handler = AsyncMock()
|
||||||
activate_status_request_handler = AsyncMock()
|
activate_status_request_handler = AsyncMock()
|
||||||
cancel_status_request_handler = AsyncMock()
|
cancel_status_request_handler = AsyncMock()
|
||||||
|
request_name = AsyncMock(return_value="TestModule")
|
||||||
send_command = AsyncMock(return_value=True)
|
send_command = AsyncMock(return_value=True)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Construct ModuleConnection instance."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.serials_request_handler.serial_known.set()
|
||||||
|
|
||||||
|
|
||||||
class MockGroupConnection(GroupConnection):
|
class MockGroupConnection(GroupConnection):
|
||||||
"""Fake a LCN group connection."""
|
"""Fake a LCN group connection."""
|
||||||
|
|
|
@ -76,8 +76,7 @@ async def test_step_import_existing_host(hass):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_step_import_error(hass, error, reason):
|
async def test_step_import_error(hass, error, reason):
|
||||||
"""Test for authentication error is handled correctly."""
|
"""Test for error in import is handled correctly."""
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"pypck.connection.PchkConnectionManager.async_connect", side_effect=error
|
"pypck.connection.PchkConnectionManager.async_connect", side_effect=error
|
||||||
):
|
):
|
||||||
|
|
|
@ -10,7 +10,7 @@ from pypck.connection import (
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.lcn.const import DOMAIN
|
from homeassistant.components.lcn.const import DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
|
|
||||||
from .conftest import MockPchkConnectionManager, init_integration, setup_component
|
from .conftest import MockPchkConnectionManager, init_integration, setup_component
|
||||||
|
|
||||||
|
@ -53,19 +53,31 @@ async def test_async_setup_entry_update(hass, entry):
|
||||||
"""Test a successful setup entry if entry with same id already exists."""
|
"""Test a successful setup entry if entry with same id already exists."""
|
||||||
# setup first entry
|
# setup first entry
|
||||||
entry.source = config_entries.SOURCE_IMPORT
|
entry.source = config_entries.SOURCE_IMPORT
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
# create dummy entity for LCN platform as an orphan
|
# create dummy entity for LCN platform as an orphan
|
||||||
entity_registry = await er.async_get_registry(hass)
|
entity_registry = er.async_get(hass)
|
||||||
dummy_entity = entity_registry.async_get_or_create(
|
dummy_entity = entity_registry.async_get_or_create(
|
||||||
"switch", DOMAIN, "dummy", config_entry=entry
|
"switch", DOMAIN, "dummy", config_entry=entry
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# create dummy device for LCN platform as an orphan
|
||||||
|
device_registry = dr.async_get(hass)
|
||||||
|
dummy_device = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=entry.entry_id,
|
||||||
|
identifiers={(DOMAIN, entry.entry_id, 0, 7, False)},
|
||||||
|
via_device=(DOMAIN, entry.entry_id),
|
||||||
|
)
|
||||||
|
|
||||||
assert dummy_entity in entity_registry.entities.values()
|
assert dummy_entity in entity_registry.entities.values()
|
||||||
|
assert dummy_device in device_registry.devices.values()
|
||||||
|
|
||||||
# add entity to hass and setup (should cleanup dummy entity)
|
# setup new entry with same data via import step (should cleanup dummy device)
|
||||||
entry.add_to_hass(hass)
|
await hass.config_entries.flow.async_init(
|
||||||
await hass.config_entries.async_setup(entry.entry_id)
|
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=entry.data
|
||||||
await hass.async_block_till_done()
|
)
|
||||||
|
|
||||||
|
assert dummy_device not in device_registry.devices.values()
|
||||||
assert dummy_entity not in entity_registry.entities.values()
|
assert dummy_entity not in entity_registry.entities.values()
|
||||||
|
|
||||||
|
|
||||||
|
|
5
tests/fixtures/lcn/config.json
vendored
5
tests/fixtures/lcn/config.json
vendored
|
@ -25,6 +25,11 @@
|
||||||
"name": "Switch_Output1",
|
"name": "Switch_Output1",
|
||||||
"address": "s0.m7",
|
"address": "s0.m7",
|
||||||
"output": "output1"
|
"output": "output1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Switch_Group5",
|
||||||
|
"address": "s0.g5",
|
||||||
|
"output": "relay1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
16
tests/fixtures/lcn/config_entry_pchk.json
vendored
16
tests/fixtures/lcn/config_entry_pchk.json
vendored
|
@ -13,6 +13,13 @@
|
||||||
"hardware_serial": -1,
|
"hardware_serial": -1,
|
||||||
"software_serial": -1,
|
"software_serial": -1,
|
||||||
"hardware_type": -1
|
"hardware_type": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": [0, 5, true],
|
||||||
|
"name": "",
|
||||||
|
"hardware_serial": -1,
|
||||||
|
"software_serial": -1,
|
||||||
|
"hardware_type": -1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"entities": [
|
"entities": [
|
||||||
|
@ -24,6 +31,15 @@
|
||||||
"domain_data": {
|
"domain_data": {
|
||||||
"output": "OUTPUT1"
|
"output": "OUTPUT1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": [0, 5, true],
|
||||||
|
"name": "Switch_Group5",
|
||||||
|
"resource": "relay1",
|
||||||
|
"domain": "switch",
|
||||||
|
"domain_data": {
|
||||||
|
"output": "RELAY1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue