Remove individual lcn devices for each entity (#136450)

echo
Andre Lengwenus 2025-02-23 14:42:54 +01:00 committed by GitHub
parent 15ca2fe489
commit 800fe1b01e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 23 additions and 76 deletions

View File

@ -49,6 +49,7 @@ from .helpers import (
InputType,
async_update_config_entry,
generate_unique_id,
purge_device_registry,
register_lcn_address_devices,
register_lcn_host_device,
)
@ -120,6 +121,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
register_lcn_host_device(hass, config_entry)
register_lcn_address_devices(hass, config_entry)
# clean up orphaned devices
purge_device_registry(hass, config_entry.entry_id, {**config_entry.data})
# forward config_entry to components
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)

View File

@ -3,19 +3,18 @@
from collections.abc import Callable
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ADDRESS, CONF_DOMAIN, CONF_NAME, CONF_RESOURCE
from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_RESOURCE
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType
from .const import CONF_DOMAIN_DATA, DOMAIN
from .const import DOMAIN
from .helpers import (
AddressType,
DeviceConnectionType,
InputType,
generate_unique_id,
get_device_connection,
get_device_model,
)
@ -36,6 +35,14 @@ class LcnEntity(Entity):
self.address: AddressType = config[CONF_ADDRESS]
self._unregister_for_inputs: Callable | None = None
self._name: str = config[CONF_NAME]
self._attr_device_info = DeviceInfo(
identifiers={
(
DOMAIN,
generate_unique_id(self.config_entry.entry_id, self.address),
)
},
)
@property
def unique_id(self) -> str:
@ -44,28 +51,6 @@ class LcnEntity(Entity):
self.config_entry.entry_id, self.address, 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 = (
"LCN resource"
f" ({get_device_model(self.config[CONF_DOMAIN], self.config[CONF_DOMAIN_DATA])})"
)
return DeviceInfo(
identifiers={(DOMAIN, self.unique_id)},
name=f"{address}.{self.config[CONF_RESOURCE]}",
model=model,
manufacturer="Issendorff",
via_device=(
DOMAIN,
generate_unique_id(
self.config_entry.entry_id, self.config[CONF_ADDRESS]
),
),
)
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
self.device_connection = get_device_connection(

View File

@ -4,7 +4,6 @@ from __future__ import annotations
import asyncio
from copy import deepcopy
from itertools import chain
import re
from typing import cast
@ -22,7 +21,6 @@ from homeassistant.const import (
CONF_NAME,
CONF_RESOURCE,
CONF_SENSORS,
CONF_SOURCE,
CONF_SWITCHES,
)
from homeassistant.core import HomeAssistant
@ -30,23 +28,14 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.typing import ConfigType
from .const import (
BINSENSOR_PORTS,
CONF_CLIMATES,
CONF_HARDWARE_SERIAL,
CONF_HARDWARE_TYPE,
CONF_OUTPUT,
CONF_SCENES,
CONF_SOFTWARE_SERIAL,
CONNECTION,
DEVICE_CONNECTIONS,
DOMAIN,
LED_PORTS,
LOGICOP_PORTS,
OUTPUT_PORTS,
S0_INPUTS,
SETPOINTS,
THRESHOLDS,
VARIABLES,
)
# typing
@ -96,31 +85,6 @@ def get_resource(domain_name: str, domain_data: ConfigType) -> str:
raise ValueError("Unknown domain")
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,
@ -169,13 +133,6 @@ def purge_device_registry(
) -> None:
"""Remove orphans from device registry which are not in entry data."""
device_registry = dr.async_get(hass)
entity_registry = er.async_get(hass)
# Find all devices that are referenced in the entity registry.
references_entities = {
entry.device_id
for entry in entity_registry.entities.get_entries_for_config_entry_id(entry_id)
}
# Find device that references the host.
references_host = set()
@ -198,7 +155,6 @@ def purge_device_registry(
entry.id
for entry in dr.async_entries_for_config_entry(device_registry, entry_id)
}
- references_entities
- references_host
- references_entry_data
)

View File

@ -45,9 +45,14 @@ async def test_get_triggers_module_device(
)
]
triggers = await async_get_device_automations(
hass, DeviceAutomationType.TRIGGER, device.id
)
triggers = [
trigger
for trigger in await async_get_device_automations(
hass, DeviceAutomationType.TRIGGER, device.id
)
if trigger[CONF_DOMAIN] == DOMAIN
]
assert triggers == unordered(expected_triggers)
@ -63,11 +68,8 @@ async def test_get_triggers_non_module_device(
identifiers={(DOMAIN, entry.entry_id)}
)
group_device = get_device(hass, entry, (0, 5, True))
resource_device = device_registry.async_get_device(
identifiers={(DOMAIN, f"{entry.entry_id}-m000007-output1")}
)
for device in (host_device, group_device, resource_device):
for device in (host_device, group_device):
triggers = await async_get_device_automations(
hass, DeviceAutomationType.TRIGGER, device.id
)