Add support for device sub units in AVM Fritz!SmartHome (#142845)

This commit is contained in:
Michael 2025-04-14 22:19:14 +02:00 committed by GitHub
parent a4f75ca249
commit e418491f19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 45 additions and 19 deletions

View File

@ -77,12 +77,11 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
self.configuration_url = self.fritz.get_prefixed_host()
await self.async_config_entry_first_refresh()
self.cleanup_removed_devices(
list(self.data.devices) + list(self.data.templates)
)
self.cleanup_removed_devices(self.data)
def cleanup_removed_devices(self, available_ains: list[str]) -> None:
def cleanup_removed_devices(self, data: FritzboxCoordinatorData) -> None:
"""Cleanup entity and device registry from removed devices."""
available_ains = list(data.devices) + list(data.templates)
entity_reg = er.async_get(self.hass)
for entity in er.async_entries_for_config_entry(
entity_reg, self.config_entry.entry_id
@ -91,8 +90,13 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
LOGGER.debug("Removing obsolete entity entry %s", entity.entity_id)
entity_reg.async_remove(entity.entity_id)
available_main_ains = [
ain
for ain, dev in data.devices.items()
if dev.device_and_unit_id[1] is None
]
device_reg = dr.async_get(self.hass)
identifiers = {(DOMAIN, ain) for ain in available_ains}
identifiers = {(DOMAIN, ain) for ain in available_main_ains}
for device in dr.async_entries_for_config_entry(
device_reg, self.config_entry.entry_id
):
@ -165,12 +169,26 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
"""Fetch all device data."""
new_data = await self.hass.async_add_executor_job(self._update_fritz_devices)
for device in new_data.devices.values():
# create device registry entry for new main devices
if (
device.ain not in self.data.devices
and device.device_and_unit_id[1] is None
):
dr.async_get(self.hass).async_get_or_create(
config_entry_id=self.config_entry.entry_id,
name=device.name,
identifiers={(DOMAIN, device.ain)},
manufacturer=device.manufacturer,
model=device.productname,
sw_version=device.fw_version,
configuration_url=self.configuration_url,
)
if (
self.data.devices.keys() - new_data.devices.keys()
or self.data.templates.keys() - new_data.templates.keys()
):
self.cleanup_removed_devices(
list(new_data.devices) + list(new_data.templates)
)
self.cleanup_removed_devices(new_data)
return new_data

View File

@ -58,11 +58,4 @@ class FritzBoxDeviceEntity(FritzBoxEntity):
@property
def device_info(self) -> DeviceInfo:
"""Return device specific attributes."""
return DeviceInfo(
name=self.data.name,
identifiers={(DOMAIN, self.ain)},
manufacturer=self.data.manufacturer,
model=self.data.productname,
sw_version=self.data.fw_version,
configuration_url=self.coordinator.configuration_url,
)
return DeviceInfo(identifiers={(DOMAIN, self.data.device_and_unit_id[0])})

View File

@ -60,6 +60,7 @@ class FritzEntityBaseMock(Mock):
"""base mock of a AVM Fritz!Box binary sensor device."""
ain = CONF_FAKE_AIN
device_and_unit_id = (CONF_FAKE_AIN, None)
manufacturer = CONF_FAKE_MANUFACTURER
name = CONF_FAKE_NAME
productname = CONF_FAKE_PRODUCTNAME

View File

@ -110,6 +110,7 @@ async def test_discover_new_device(hass: HomeAssistant, fritz: Mock) -> None:
new_device = FritzDeviceBinarySensorMock()
new_device.ain = "7890 1234"
new_device.device_and_unit_id = ("7890 1234", None)
new_device.name = "new_device"
set_devices(fritz, devices=[device, new_device])

View File

@ -85,8 +85,16 @@ async def test_coordinator_automatic_registry_cleanup(
) -> None:
"""Test automatic registry cleanup."""
fritz().get_devices.return_value = [
FritzDeviceSwitchMock(ain="fake ain switch", name="fake_switch"),
FritzDeviceCoverMock(ain="fake ain cover", name="fake_cover"),
FritzDeviceSwitchMock(
ain="fake ain switch",
device_and_unit_id=("fake ain switch", None),
name="fake_switch",
),
FritzDeviceCoverMock(
ain="fake ain cover",
device_and_unit_id=("fake ain cover", None),
name="fake_cover",
),
]
entry = MockConfigEntry(
domain=FB_DOMAIN,
@ -101,7 +109,11 @@ async def test_coordinator_automatic_registry_cleanup(
assert len(dr.async_entries_for_config_entry(device_registry, entry.entry_id)) == 2
fritz().get_devices.return_value = [
FritzDeviceSwitchMock(ain="fake ain switch", name="fake_switch")
FritzDeviceSwitchMock(
ain="fake ain switch",
device_and_unit_id=("fake ain switch", None),
name="fake_switch",
)
]
async_fire_time_changed(hass, utcnow() + timedelta(seconds=35))

View File

@ -108,6 +108,7 @@ async def test_discover_new_device(hass: HomeAssistant, fritz: Mock) -> None:
new_device = FritzDeviceSensorMock()
new_device.ain = "7890 1234"
new_device.device_and_unit_id = ("7890 1234", None)
new_device.name = "new_device"
set_devices(fritz, devices=[device, new_device])