Refactor eheimdigital platform async_setup_entry (#136745)

This commit is contained in:
Sid 2025-01-31 12:52:17 +01:00 committed by GitHub
parent d83c335ed6
commit cde59613a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 148 additions and 24 deletions

View File

@ -2,6 +2,7 @@
from typing import Any from typing import Any
from eheimdigital.device import EheimDigitalDevice
from eheimdigital.heater import EheimDigitalHeater from eheimdigital.heater import EheimDigitalHeater
from eheimdigital.types import EheimDigitalClientError, HeaterMode, HeaterUnit from eheimdigital.types import EheimDigitalClientError, HeaterMode, HeaterUnit
@ -39,17 +40,23 @@ async def async_setup_entry(
"""Set up the callbacks for the coordinator so climate entities can be added as devices are found.""" """Set up the callbacks for the coordinator so climate entities can be added as devices are found."""
coordinator = entry.runtime_data coordinator = entry.runtime_data
async def async_setup_device_entities(device_address: str) -> None: def async_setup_device_entities(
"""Set up the light entities for a device.""" device_address: str | dict[str, EheimDigitalDevice],
device = coordinator.hub.devices[device_address] ) -> None:
"""Set up the climate entities for one or multiple devices."""
entities: list[EheimDigitalHeaterClimate] = []
if isinstance(device_address, str):
device_address = {device_address: coordinator.hub.devices[device_address]}
for device in device_address.values():
if isinstance(device, EheimDigitalHeater):
entities.append(EheimDigitalHeaterClimate(coordinator, device))
coordinator.known_devices.add(device.mac_address)
if isinstance(device, EheimDigitalHeater): async_add_entities(entities)
async_add_entities([EheimDigitalHeaterClimate(coordinator, device)])
coordinator.add_platform_callback(async_setup_device_entities) coordinator.add_platform_callback(async_setup_device_entities)
for device_address in entry.runtime_data.hub.devices: async_setup_device_entities(coordinator.hub.devices)
await async_setup_device_entities(device_address)
class EheimDigitalHeaterClimate(EheimDigitalEntity[EheimDigitalHeater], ClimateEntity): class EheimDigitalHeaterClimate(EheimDigitalEntity[EheimDigitalHeater], ClimateEntity):

View File

@ -2,8 +2,7 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable, Coroutine from collections.abc import Callable
from typing import Any
from aiohttp import ClientError from aiohttp import ClientError
from eheimdigital.device import EheimDigitalDevice from eheimdigital.device import EheimDigitalDevice
@ -19,7 +18,9 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DOMAIN, LOGGER from .const import DOMAIN, LOGGER
type AsyncSetupDeviceEntitiesCallback = Callable[[str], Coroutine[Any, Any, None]] type AsyncSetupDeviceEntitiesCallback = Callable[
[str | dict[str, EheimDigitalDevice]], None
]
class EheimDigitalUpdateCoordinator( class EheimDigitalUpdateCoordinator(
@ -61,7 +62,7 @@ class EheimDigitalUpdateCoordinator(
if device_address not in self.known_devices: if device_address not in self.known_devices:
for platform_callback in self.platform_callbacks: for platform_callback in self.platform_callbacks:
await platform_callback(device_address) platform_callback(device_address)
async def _async_receive_callback(self) -> None: async def _async_receive_callback(self) -> None:
self.async_set_updated_data(self.hub.devices) self.async_set_updated_data(self.hub.devices)

View File

@ -3,6 +3,7 @@
from typing import Any from typing import Any
from eheimdigital.classic_led_ctrl import EheimDigitalClassicLEDControl from eheimdigital.classic_led_ctrl import EheimDigitalClassicLEDControl
from eheimdigital.device import EheimDigitalDevice
from eheimdigital.types import EheimDigitalClientError, LightMode from eheimdigital.types import EheimDigitalClientError, LightMode
from homeassistant.components.light import ( from homeassistant.components.light import (
@ -37,24 +38,28 @@ async def async_setup_entry(
"""Set up the callbacks for the coordinator so lights can be added as devices are found.""" """Set up the callbacks for the coordinator so lights can be added as devices are found."""
coordinator = entry.runtime_data coordinator = entry.runtime_data
async def async_setup_device_entities(device_address: str) -> None: def async_setup_device_entities(
"""Set up the light entities for a device.""" device_address: str | dict[str, EheimDigitalDevice],
device = coordinator.hub.devices[device_address] ) -> None:
"""Set up the light entities for one or multiple devices."""
entities: list[EheimDigitalClassicLEDControlLight] = [] entities: list[EheimDigitalClassicLEDControlLight] = []
if isinstance(device_address, str):
device_address = {device_address: coordinator.hub.devices[device_address]}
for device in device_address.values():
if isinstance(device, EheimDigitalClassicLEDControl):
for channel in range(2):
if len(device.tankconfig[channel]) > 0:
entities.append(
EheimDigitalClassicLEDControlLight(
coordinator, device, channel
)
)
coordinator.known_devices.add(device.mac_address)
if isinstance(device, EheimDigitalClassicLEDControl):
for channel in range(2):
if len(device.tankconfig[channel]) > 0:
entities.append(
EheimDigitalClassicLEDControlLight(coordinator, device, channel)
)
coordinator.known_devices.add(device.mac_address)
async_add_entities(entities) async_add_entities(entities)
coordinator.add_platform_callback(async_setup_device_entities) coordinator.add_platform_callback(async_setup_device_entities)
async_setup_device_entities(coordinator.hub.devices)
for device_address in entry.runtime_data.hub.devices:
await async_setup_device_entities(device_address)
class EheimDigitalClassicLEDControlLight( class EheimDigitalClassicLEDControlLight(

View File

@ -1,4 +1,80 @@
# serializer version: 1 # serializer version: 1
# name: test_dynamic_new_devices[climate.mock_heater_none-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.AUTO: 'auto'>,
]),
'max_temp': 32,
'min_temp': 18,
'preset_modes': list([
'none',
'bio_mode',
'smart_mode',
]),
'target_temp_step': 0.5,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.mock_heater_none',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'eheimdigital',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 401>,
'translation_key': 'heater',
'unique_id': '00:00:00:00:00:02',
'unit_of_measurement': None,
})
# ---
# name: test_dynamic_new_devices[climate.mock_heater_none-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 24.2,
'friendly_name': 'Mock Heater None',
'hvac_action': <HVACAction.HEATING: 'heating'>,
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.AUTO: 'auto'>,
]),
'max_temp': 32,
'min_temp': 18,
'preset_mode': 'none',
'preset_modes': list([
'none',
'bio_mode',
'smart_mode',
]),
'supported_features': <ClimateEntityFeature: 401>,
'target_temp_step': 0.5,
'temperature': 25.5,
}),
'context': <ANY>,
'entity_id': 'climate.mock_heater_none',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'auto',
})
# ---
# name: test_setup_heater[climate.mock_heater_none-entry] # name: test_setup_heater[climate.mock_heater_none-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View File

@ -56,6 +56,41 @@ async def test_setup_heater(
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_dynamic_new_devices(
hass: HomeAssistant,
eheimdigital_hub_mock: MagicMock,
heater_mock: MagicMock,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test light platform setup with at first no devices and dynamically adding a device."""
mock_config_entry.add_to_hass(hass)
eheimdigital_hub_mock.return_value.devices = {}
with patch("homeassistant.components.eheimdigital.PLATFORMS", [Platform.CLIMATE]):
await hass.config_entries.async_setup(mock_config_entry.entry_id)
assert (
len(
entity_registry.entities.get_entries_for_config_entry_id(
mock_config_entry.entry_id
)
)
== 0
)
eheimdigital_hub_mock.return_value.devices = {"00:00:00:00:00:02": heater_mock}
await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"](
"00:00:00:00:00:02", EheimDeviceType.VERSION_EHEIM_EXT_HEATER
)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.parametrize( @pytest.mark.parametrize(
("preset_mode", "heater_mode"), ("preset_mode", "heater_mode"),
[ [