From c97437fbf33ad7f317f48920c02b855e458e11d2 Mon Sep 17 00:00:00 2001 From: Sid <27780930+autinerd@users.noreply.github.com> Date: Fri, 9 Jan 2026 21:24:01 +0100 Subject: [PATCH] Add the professionel5e filter series to eheimdigital (#155550) --- .../components/eheimdigital/entity.py | 6 +- .../components/eheimdigital/number.py | 37 ++ .../components/eheimdigital/select.py | 164 ++++- .../components/eheimdigital/sensor.py | 37 +- .../components/eheimdigital/strings.json | 34 ++ .../components/eheimdigital/switch.py | 15 +- homeassistant/components/eheimdigital/time.py | 25 + tests/components/eheimdigital/conftest.py | 17 + .../fixtures/filter/filter_data.json | 32 + .../eheimdigital/fixtures/filter/usrdta.json | 34 ++ .../snapshots/test_diagnostics.ambr | 79 +++ .../eheimdigital/snapshots/test_number.ambr | 176 ++++++ .../eheimdigital/snapshots/test_select.ambr | 565 ++++++++++++++++++ .../eheimdigital/snapshots/test_sensor.ambr | 121 +++- .../eheimdigital/snapshots/test_switch.ambr | 52 +- .../eheimdigital/snapshots/test_time.ambr | 96 +++ tests/components/eheimdigital/test_number.py | 46 +- tests/components/eheimdigital/test_select.py | 80 ++- tests/components/eheimdigital/test_sensor.py | 34 +- tests/components/eheimdigital/test_switch.py | 58 +- tests/components/eheimdigital/test_time.py | 39 +- 21 files changed, 1681 insertions(+), 66 deletions(-) create mode 100644 tests/components/eheimdigital/fixtures/filter/filter_data.json create mode 100644 tests/components/eheimdigital/fixtures/filter/usrdta.json diff --git a/homeassistant/components/eheimdigital/entity.py b/homeassistant/components/eheimdigital/entity.py index d28087ef82e3..aa92f8ede586 100644 --- a/homeassistant/components/eheimdigital/entity.py +++ b/homeassistant/components/eheimdigital/entity.py @@ -37,7 +37,7 @@ class EheimDigitalEntity[_DeviceT: EheimDigitalDevice]( name=device.name, connections={(CONNECTION_NETWORK_MAC, device.mac_address)}, manufacturer="EHEIM", - model=device.device_type.model_name, + model=device.model_name, identifiers={(DOMAIN, device.mac_address)}, suggested_area=device.aquarium_name, sw_version=device.sw_version, @@ -59,9 +59,9 @@ class EheimDigitalEntity[_DeviceT: EheimDigitalDevice]( def exception_handler[_EntityT: EheimDigitalEntity[EheimDigitalDevice], **_P]( func: Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, Any]], ) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]: - """Decorate AirGradient calls to handle exceptions. + """Decorate eheimdigital calls to handle exceptions. - A decorator that wraps the passed in function, catches AirGradient errors. + A decorator that wraps the passed in function, catches eheimdigital errors. """ async def handler(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None: diff --git a/homeassistant/components/eheimdigital/number.py b/homeassistant/components/eheimdigital/number.py index 53382e3aead2..bd8d8519653e 100644 --- a/homeassistant/components/eheimdigital/number.py +++ b/homeassistant/components/eheimdigital/number.py @@ -6,6 +6,7 @@ from typing import Any, override from eheimdigital.classic_vario import EheimDigitalClassicVario from eheimdigital.device import EheimDigitalDevice +from eheimdigital.filter import EheimDigitalFilter from eheimdigital.heater import EheimDigitalHeater from eheimdigital.types import HeaterUnit @@ -21,6 +22,7 @@ from homeassistant.const import ( PRECISION_WHOLE, EntityCategory, UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -42,6 +44,34 @@ class EheimDigitalNumberDescription[_DeviceT: EheimDigitalDevice]( uom_fn: Callable[[_DeviceT], str] | None = None +FILTER_DESCRIPTIONS: tuple[EheimDigitalNumberDescription[EheimDigitalFilter], ...] = ( + EheimDigitalNumberDescription[EheimDigitalFilter]( + key="high_pulse_time", + translation_key="high_pulse_time", + entity_category=EntityCategory.CONFIG, + native_step=PRECISION_WHOLE, + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=NumberDeviceClass.DURATION, + native_min_value=5, + native_max_value=200000, + value_fn=lambda device: device.high_pulse_time, + set_value_fn=lambda device, value: device.set_high_pulse_time(int(value)), + ), + EheimDigitalNumberDescription[EheimDigitalFilter]( + key="low_pulse_time", + translation_key="low_pulse_time", + entity_category=EntityCategory.CONFIG, + native_step=PRECISION_WHOLE, + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=NumberDeviceClass.DURATION, + native_min_value=5, + native_max_value=200000, + value_fn=lambda device: device.low_pulse_time, + set_value_fn=lambda device, value: device.set_low_pulse_time(int(value)), + ), +) + + CLASSICVARIO_DESCRIPTIONS: tuple[ EheimDigitalNumberDescription[EheimDigitalClassicVario], ... ] = ( @@ -145,6 +175,13 @@ async def async_setup_entry( ) for description in CLASSICVARIO_DESCRIPTIONS ) + if isinstance(device, EheimDigitalFilter): + entities.extend( + EheimDigitalNumber[EheimDigitalFilter]( + coordinator, device, description + ) + for description in FILTER_DESCRIPTIONS + ) if isinstance(device, EheimDigitalHeater): entities.extend( EheimDigitalNumber[EheimDigitalHeater]( diff --git a/homeassistant/components/eheimdigital/select.py b/homeassistant/components/eheimdigital/select.py index 5c42055441a6..5ba9de28e8da 100644 --- a/homeassistant/components/eheimdigital/select.py +++ b/homeassistant/components/eheimdigital/select.py @@ -2,13 +2,19 @@ from collections.abc import Awaitable, Callable from dataclasses import dataclass -from typing import Any, override +from typing import Any, Literal, override from eheimdigital.classic_vario import EheimDigitalClassicVario from eheimdigital.device import EheimDigitalDevice -from eheimdigital.types import FilterMode +from eheimdigital.filter import EheimDigitalFilter +from eheimdigital.types import ( + FilterMode, + FilterModeProf, + UnitOfMeasurement as EheimDigitalUnitOfMeasurement, +) from homeassistant.components.select import SelectEntity, SelectEntityDescription +from homeassistant.const import EntityCategory, UnitOfFrequency, UnitOfVolumeFlowRate from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -24,8 +30,109 @@ class EheimDigitalSelectDescription[_DeviceT: EheimDigitalDevice]( ): """Class describing EHEIM Digital select entities.""" + options_fn: Callable[[_DeviceT], list[str]] | None = None + use_api_unit: Literal[True] | None = None value_fn: Callable[[_DeviceT], str | None] - set_value_fn: Callable[[_DeviceT, str], Awaitable[None]] + set_value_fn: Callable[[_DeviceT, str], Awaitable[None] | None] + + +FILTER_DESCRIPTIONS: tuple[EheimDigitalSelectDescription[EheimDigitalFilter], ...] = ( + EheimDigitalSelectDescription[EheimDigitalFilter]( + key="filter_mode", + translation_key="filter_mode", + entity_category=EntityCategory.CONFIG, + options=[item.lower() for item in FilterModeProf._member_names_], + value_fn=lambda device: device.filter_mode.name.lower(), + set_value_fn=lambda device, value: device.set_filter_mode( + FilterModeProf[value.upper()] + ), + ), + EheimDigitalSelectDescription[EheimDigitalFilter]( + key="manual_speed", + translation_key="manual_speed", + entity_category=EntityCategory.CONFIG, + unit_of_measurement=UnitOfFrequency.HERTZ, + options_fn=lambda device: [str(i) for i in device.filter_manual_values], + value_fn=lambda device: str(device.manual_speed), + set_value_fn=lambda device, value: device.set_manual_speed(float(value)), + ), + EheimDigitalSelectDescription[EheimDigitalFilter]( + key="const_flow_speed", + translation_key="const_flow_speed", + entity_category=EntityCategory.CONFIG, + use_api_unit=True, + unit_of_measurement=UnitOfVolumeFlowRate.LITERS_PER_HOUR, + options_fn=lambda device: [str(i) for i in device.filter_const_flow_values], + value_fn=lambda device: str(device.filter_const_flow_values[device.const_flow]), + set_value_fn=( + lambda device, value: device.set_const_flow( + device.filter_const_flow_values.index(int(value)) + ) + ), + ), + EheimDigitalSelectDescription[EheimDigitalFilter]( + key="day_speed", + translation_key="day_speed", + entity_category=EntityCategory.CONFIG, + use_api_unit=True, + unit_of_measurement=UnitOfVolumeFlowRate.LITERS_PER_HOUR, + options_fn=lambda device: [str(i) for i in device.filter_const_flow_values], + value_fn=lambda device: str(device.filter_const_flow_values[device.day_speed]), + set_value_fn=( + lambda device, value: device.set_day_speed( + device.filter_const_flow_values.index(int(value)) + ) + ), + ), + EheimDigitalSelectDescription[EheimDigitalFilter]( + key="night_speed", + translation_key="night_speed", + entity_category=EntityCategory.CONFIG, + use_api_unit=True, + unit_of_measurement=UnitOfVolumeFlowRate.LITERS_PER_HOUR, + options_fn=lambda device: [str(i) for i in device.filter_const_flow_values], + value_fn=lambda device: str( + device.filter_const_flow_values[device.night_speed] + ), + set_value_fn=( + lambda device, value: device.set_night_speed( + device.filter_const_flow_values.index(int(value)) + ) + ), + ), + EheimDigitalSelectDescription[EheimDigitalFilter]( + key="high_pulse_speed", + translation_key="high_pulse_speed", + entity_category=EntityCategory.CONFIG, + use_api_unit=True, + unit_of_measurement=UnitOfVolumeFlowRate.LITERS_PER_HOUR, + options_fn=lambda device: [str(i) for i in device.filter_const_flow_values], + value_fn=lambda device: str( + device.filter_const_flow_values[device.high_pulse_speed] + ), + set_value_fn=( + lambda device, value: device.set_high_pulse_speed( + device.filter_const_flow_values.index(int(value)) + ) + ), + ), + EheimDigitalSelectDescription[EheimDigitalFilter]( + key="low_pulse_speed", + translation_key="low_pulse_speed", + entity_category=EntityCategory.CONFIG, + use_api_unit=True, + unit_of_measurement=UnitOfVolumeFlowRate.LITERS_PER_HOUR, + options_fn=lambda device: [str(i) for i in device.filter_const_flow_values], + value_fn=lambda device: str( + device.filter_const_flow_values[device.low_pulse_speed] + ), + set_value_fn=( + lambda device, value: device.set_low_pulse_speed( + device.filter_const_flow_values.index(int(value)) + ) + ), + ), +) CLASSICVARIO_DESCRIPTIONS: tuple[ @@ -34,11 +141,7 @@ CLASSICVARIO_DESCRIPTIONS: tuple[ EheimDigitalSelectDescription[EheimDigitalClassicVario]( key="filter_mode", translation_key="filter_mode", - value_fn=( - lambda device: device.filter_mode.name.lower() - if device.filter_mode is not None - else None - ), + value_fn=lambda device: device.filter_mode.name.lower(), set_value_fn=( lambda device, value: device.set_filter_mode(FilterMode[value.upper()]) ), @@ -68,6 +171,11 @@ async def async_setup_entry( ) for description in CLASSICVARIO_DESCRIPTIONS ) + if isinstance(device, EheimDigitalFilter): + entities.extend( + EheimDigitalFilterSelect(coordinator, device, description) + for description in FILTER_DESCRIPTIONS + ) async_add_entities(entities) @@ -82,6 +190,8 @@ class EheimDigitalSelect[_DeviceT: EheimDigitalDevice]( entity_description: EheimDigitalSelectDescription[_DeviceT] + _attr_options: list[str] + def __init__( self, coordinator: EheimDigitalUpdateCoordinator, @@ -91,13 +201,49 @@ class EheimDigitalSelect[_DeviceT: EheimDigitalDevice]( """Initialize an EHEIM Digital select entity.""" super().__init__(coordinator, device) self.entity_description = description + if description.options_fn is not None: + self._attr_options = description.options_fn(device) + elif description.options is not None: + self._attr_options = description.options self._attr_unique_id = f"{self._device_address}_{description.key}" @override @exception_handler async def async_select_option(self, option: str) -> None: - return await self.entity_description.set_value_fn(self._device, option) + if await_return := self.entity_description.set_value_fn(self._device, option): + return await await_return + return None @override def _async_update_attrs(self) -> None: self._attr_current_option = self.entity_description.value_fn(self._device) + + +class EheimDigitalFilterSelect(EheimDigitalSelect[EheimDigitalFilter]): + """Represent an EHEIM Digital Filter select entity.""" + + entity_description: EheimDigitalSelectDescription[EheimDigitalFilter] + _attr_native_unit_of_measurement: str | None + + @override + def _async_update_attrs(self) -> None: + if ( + self.entity_description.options is None + and self.entity_description.options_fn is not None + ): + self._attr_options = self.entity_description.options_fn(self._device) + if self.entity_description.use_api_unit: + if ( + self.entity_description.unit_of_measurement + == UnitOfVolumeFlowRate.LITERS_PER_HOUR + and self._device.usrdta["unit"] + == int(EheimDigitalUnitOfMeasurement.US_CUSTOMARY) + ): + self._attr_native_unit_of_measurement = ( + UnitOfVolumeFlowRate.GALLONS_PER_HOUR + ) + else: + self._attr_native_unit_of_measurement = ( + self.entity_description.unit_of_measurement + ) + super()._async_update_attrs() diff --git a/homeassistant/components/eheimdigital/sensor.py b/homeassistant/components/eheimdigital/sensor.py index b1ef70998104..9d9a50c9b8bd 100644 --- a/homeassistant/components/eheimdigital/sensor.py +++ b/homeassistant/components/eheimdigital/sensor.py @@ -6,6 +6,7 @@ from typing import Any, override from eheimdigital.classic_vario import EheimDigitalClassicVario from eheimdigital.device import EheimDigitalDevice +from eheimdigital.filter import EheimDigitalFilter from eheimdigital.types import FilterErrorCode from homeassistant.components.sensor import ( @@ -13,7 +14,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, ) -from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTime +from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfFrequency, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -33,6 +34,27 @@ class EheimDigitalSensorDescription[_DeviceT: EheimDigitalDevice]( value_fn: Callable[[_DeviceT], float | str | None] +FILTER_DESCRIPTIONS: tuple[EheimDigitalSensorDescription[EheimDigitalFilter], ...] = ( + EheimDigitalSensorDescription[EheimDigitalFilter]( + key="current_speed", + translation_key="current_speed", + value_fn=lambda device: device.current_speed, + device_class=SensorDeviceClass.FREQUENCY, + suggested_display_precision=1, + native_unit_of_measurement=UnitOfFrequency.HERTZ, + ), + EheimDigitalSensorDescription[EheimDigitalFilter]( + key="service_hours", + translation_key="service_hours", + value_fn=lambda device: device.service_hours, + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.HOURS, + suggested_unit_of_measurement=UnitOfTime.DAYS, + entity_category=EntityCategory.DIAGNOSTIC, + ), +) + + CLASSICVARIO_DESCRIPTIONS: tuple[ EheimDigitalSensorDescription[EheimDigitalClassicVario], ... ] = ( @@ -54,11 +76,7 @@ CLASSICVARIO_DESCRIPTIONS: tuple[ EheimDigitalSensorDescription[EheimDigitalClassicVario]( key="error_code", translation_key="error_code", - value_fn=( - lambda device: device.error_code.name.lower() - if device.error_code is not None - else None - ), + value_fn=lambda device: device.error_code.name.lower(), device_class=SensorDeviceClass.ENUM, options=[name.lower() for name in FilterErrorCode._member_names_], entity_category=EntityCategory.DIAGNOSTIC, @@ -80,6 +98,13 @@ async def async_setup_entry( """Set up the light entities for one or multiple devices.""" entities: list[EheimDigitalSensor[Any]] = [] for device in device_address.values(): + if isinstance(device, EheimDigitalFilter): + entities += [ + EheimDigitalSensor[EheimDigitalFilter]( + coordinator, device, description + ) + for description in FILTER_DESCRIPTIONS + ] if isinstance(device, EheimDigitalClassicVario): entities += [ EheimDigitalSensor[EheimDigitalClassicVario]( diff --git a/homeassistant/components/eheimdigital/strings.json b/homeassistant/components/eheimdigital/strings.json index 2b0268bf53d2..12b0f3b48daa 100644 --- a/homeassistant/components/eheimdigital/strings.json +++ b/homeassistant/components/eheimdigital/strings.json @@ -61,6 +61,12 @@ "day_speed": { "name": "Day speed" }, + "high_pulse_time": { + "name": "High pulse duration" + }, + "low_pulse_time": { + "name": "Low pulse duration" + }, "manual_speed": { "name": "Manual speed" }, @@ -78,13 +84,32 @@ } }, "select": { + "const_flow_speed": { + "name": "Constant flow speed" + }, + "day_speed": { + "name": "Day speed" + }, "filter_mode": { "name": "Filter mode", "state": { "bio": "Bio", + "constant_flow": "Constant flow", "manual": "Manual", "pulse": "Pulse" } + }, + "high_pulse_speed": { + "name": "High pulse speed" + }, + "low_pulse_speed": { + "name": "Low pulse speed" + }, + "manual_speed": { + "name": "Manual speed" + }, + "night_speed": { + "name": "Night speed" } }, "sensor": { @@ -99,8 +124,17 @@ "rotor_stuck": "Rotor stuck" } }, + "operating_time": { + "name": "Operating time" + }, "service_hours": { "name": "Remaining hours until service" + }, + "turn_feeding_time": { + "name": "Remaining off time after feeding" + }, + "turn_off_time": { + "name": "Remaining off time" } }, "time": { diff --git a/homeassistant/components/eheimdigital/switch.py b/homeassistant/components/eheimdigital/switch.py index 2a4f3df3861e..ccbaa4b4ed27 100644 --- a/homeassistant/components/eheimdigital/switch.py +++ b/homeassistant/components/eheimdigital/switch.py @@ -4,6 +4,7 @@ from typing import Any, override from eheimdigital.classic_vario import EheimDigitalClassicVario from eheimdigital.device import EheimDigitalDevice +from eheimdigital.filter import EheimDigitalFilter from homeassistant.components.switch import SwitchEntity from homeassistant.core import HomeAssistant @@ -30,8 +31,8 @@ async def async_setup_entry( """Set up the switch entities for one or multiple devices.""" entities: list[SwitchEntity] = [] for device in device_address.values(): - if isinstance(device, EheimDigitalClassicVario): - entities.append(EheimDigitalClassicVarioSwitch(coordinator, device)) # noqa: PERF401 + if isinstance(device, (EheimDigitalClassicVario, EheimDigitalFilter)): + entities.append(EheimDigitalFilterSwitch(coordinator, device)) # noqa: PERF401 async_add_entities(entities) @@ -39,10 +40,10 @@ async def async_setup_entry( async_setup_device_entities(coordinator.hub.devices) -class EheimDigitalClassicVarioSwitch( - EheimDigitalEntity[EheimDigitalClassicVario], SwitchEntity +class EheimDigitalFilterSwitch( + EheimDigitalEntity[EheimDigitalClassicVario | EheimDigitalFilter], SwitchEntity ): - """Represent an EHEIM Digital classicVARIO switch entity.""" + """Represent an EHEIM Digital classicVARIO or filter switch entity.""" _attr_translation_key = "filter_active" _attr_name = None @@ -50,9 +51,9 @@ class EheimDigitalClassicVarioSwitch( def __init__( self, coordinator: EheimDigitalUpdateCoordinator, - device: EheimDigitalClassicVario, + device: EheimDigitalClassicVario | EheimDigitalFilter, ) -> None: - """Initialize an EHEIM Digital classicVARIO switch entity.""" + """Initialize an EHEIM Digital classicVARIO or filter switch entity.""" super().__init__(coordinator, device) self._attr_unique_id = device.mac_address self._async_update_attrs() diff --git a/homeassistant/components/eheimdigital/time.py b/homeassistant/components/eheimdigital/time.py index f14a4150eff4..4a5ab7f8bd82 100644 --- a/homeassistant/components/eheimdigital/time.py +++ b/homeassistant/components/eheimdigital/time.py @@ -7,6 +7,7 @@ from typing import Any, final, override from eheimdigital.classic_vario import EheimDigitalClassicVario from eheimdigital.device import EheimDigitalDevice +from eheimdigital.filter import EheimDigitalFilter from eheimdigital.heater import EheimDigitalHeater from homeassistant.components.time import TimeEntity, TimeEntityDescription @@ -28,6 +29,23 @@ class EheimDigitalTimeDescription[_DeviceT: EheimDigitalDevice](TimeEntityDescri set_value_fn: Callable[[_DeviceT, time], Awaitable[None]] +FILTER_DESCRIPTIONS: tuple[EheimDigitalTimeDescription[EheimDigitalFilter], ...] = ( + EheimDigitalTimeDescription[EheimDigitalFilter]( + key="day_start_time", + translation_key="day_start_time", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: device.day_start_time, + set_value_fn=lambda device, value: device.set_day_start_time(value), + ), + EheimDigitalTimeDescription[EheimDigitalFilter]( + key="night_start_time", + translation_key="night_start_time", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: device.night_start_time, + set_value_fn=lambda device, value: device.set_night_start_time(value), + ), +) + CLASSICVARIO_DESCRIPTIONS: tuple[ EheimDigitalTimeDescription[EheimDigitalClassicVario], ... ] = ( @@ -79,6 +97,13 @@ async def async_setup_entry( """Set up the time entities for one or multiple devices.""" entities: list[EheimDigitalTime[Any]] = [] for device in device_address.values(): + if isinstance(device, EheimDigitalFilter): + entities.extend( + EheimDigitalTime[EheimDigitalFilter]( + coordinator, device, description + ) + for description in FILTER_DESCRIPTIONS + ) if isinstance(device, EheimDigitalClassicVario): entities.extend( EheimDigitalTime[EheimDigitalClassicVario]( diff --git a/tests/components/eheimdigital/conftest.py b/tests/components/eheimdigital/conftest.py index c05e95701e16..7b71092fdee4 100644 --- a/tests/components/eheimdigital/conftest.py +++ b/tests/components/eheimdigital/conftest.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch from eheimdigital.classic_led_ctrl import EheimDigitalClassicLEDControl from eheimdigital.classic_vario import EheimDigitalClassicVario +from eheimdigital.filter import EheimDigitalFilter from eheimdigital.heater import EheimDigitalHeater from eheimdigital.hub import EheimDigitalHub from eheimdigital.types import ( @@ -13,6 +14,7 @@ from eheimdigital.types import ( ClassicVarioDataPacket, ClockPacket, CloudPacket, + FilterDataPacket, MoonPacket, UsrDtaPacket, ) @@ -82,11 +84,25 @@ def classic_vario_mock(): return classic_vario +@pytest.fixture +def filter_mock(): + """Mock a filter device.""" + eheim_filter = EheimDigitalFilter( + MagicMock(spec=EheimDigitalHub), + UsrDtaPacket(load_json_object_fixture("filter/usrdta.json", DOMAIN)), + ) + eheim_filter.filter_data = FilterDataPacket( + load_json_object_fixture("filter/filter_data.json", DOMAIN) + ) + return eheim_filter + + @pytest.fixture def eheimdigital_hub_mock( classic_led_ctrl_mock: MagicMock, heater_mock: MagicMock, classic_vario_mock: MagicMock, + filter_mock: MagicMock, ) -> Generator[AsyncMock]: """Mock eheimdigital hub.""" with ( @@ -103,6 +119,7 @@ def eheimdigital_hub_mock( "00:00:00:00:00:01": classic_led_ctrl_mock, "00:00:00:00:00:02": heater_mock, "00:00:00:00:00:03": classic_vario_mock, + "00:00:00:00:00:04": filter_mock, } eheimdigital_hub_mock.return_value.main = classic_led_ctrl_mock yield eheimdigital_hub_mock diff --git a/tests/components/eheimdigital/fixtures/filter/filter_data.json b/tests/components/eheimdigital/fixtures/filter/filter_data.json new file mode 100644 index 000000000000..cd1176e597ee --- /dev/null +++ b/tests/components/eheimdigital/fixtures/filter/filter_data.json @@ -0,0 +1,32 @@ +{ + "title": "FILTER_DATA", + "from": "00:00:00:00:00:04", + "minFreq": 3500, + "maxFreq": 6700, + "maxFreqRglOff": 7200, + "freq": 7200, + "freqSoll": 7200, + "dfs": 0, + "dfsFaktor": 0, + "sollStep": 14, + "rotSpeed": 43, + "pumpMode": 16, + "sync": "", + "partnerName": "", + "filterActive": 1, + "runTime": 106435, + "actualTime": 106435, + "serviceHour": 36774, + "pm_dfs_soll_high": 10, + "pm_dfs_soll_low": 1, + "pm_time_high": 10, + "pm_time_low": 10, + "nm_dfs_soll_day": 10, + "nm_dfs_soll_night": 4, + "end_time_night_mode": 420, + "start_time_night_mode": 1140, + "version": 74, + "isEheim": 1, + "turnOffTime": 0, + "turnTimeFeeding": 0 +} diff --git a/tests/components/eheimdigital/fixtures/filter/usrdta.json b/tests/components/eheimdigital/fixtures/filter/usrdta.json new file mode 100644 index 000000000000..f4db2eccc97d --- /dev/null +++ b/tests/components/eheimdigital/fixtures/filter/usrdta.json @@ -0,0 +1,34 @@ +{ + "title": "USRDTA", + "from": "00:00:00:00:00:04", + "name": "Mock filter", + "aqName": "Mock Aquarium", + "version": 4, + "language": "DE", + "timezone": 60, + "tID": 30, + "dst": 1, + "tankconfig": "WITHOUT_THERMO", + "power": "", + "netmode": "ST", + "host": "eheimdigital", + "groupID": 0, + "meshing": 1, + "firstStart": 0, + "remote": 0, + "revision": [1026, 1030], + "build": ["1752053563000", "1752045985672"], + "latestAvailableRevision": [1026, 1030, 2038, 2038], + "firmwareAvailable": 0, + "emailAddr": "", + "stMail": 0, + "stMailMode": 0, + "fstTime": 720, + "sstTime": 0, + "liveTime": 103680, + "usrName": "", + "unit": 0, + "demoUse": 0, + "sysLED": 45, + "to": "USER" +} diff --git a/tests/components/eheimdigital/snapshots/test_diagnostics.ambr b/tests/components/eheimdigital/snapshots/test_diagnostics.ambr index a60952b0ef51..f8e2aa4a62e7 100644 --- a/tests/components/eheimdigital/snapshots/test_diagnostics.ambr +++ b/tests/components/eheimdigital/snapshots/test_diagnostics.ambr @@ -236,6 +236,85 @@ 'version': 18, }), }), + '00:00:00:00:00:04': dict({ + 'filter_data': dict({ + 'actualTime': 106435, + 'dfs': 0, + 'dfsFaktor': 0, + 'end_time_night_mode': 420, + 'filterActive': 1, + 'freq': 7200, + 'freqSoll': 7200, + 'from': '00:00:00:00:00:04', + 'isEheim': 1, + 'maxFreq': 6700, + 'maxFreqRglOff': 7200, + 'minFreq': 3500, + 'nm_dfs_soll_day': 10, + 'nm_dfs_soll_night': 4, + 'partnerName': '', + 'pm_dfs_soll_high': 10, + 'pm_dfs_soll_low': 1, + 'pm_time_high': 10, + 'pm_time_low': 10, + 'pumpMode': 16, + 'rotSpeed': 43, + 'runTime': 106435, + 'serviceHour': 36774, + 'sollStep': 14, + 'start_time_night_mode': 1140, + 'sync': '', + 'title': 'FILTER_DATA', + 'turnOffTime': 0, + 'turnTimeFeeding': 0, + 'version': 74, + }), + 'usrdta': dict({ + 'aqName': 'Mock Aquarium', + 'build': list([ + '1752053563000', + '1752045985672', + ]), + 'demoUse': 0, + 'dst': 1, + 'emailAddr': '', + 'firmwareAvailable': 0, + 'firstStart': 0, + 'from': '00:00:00:00:00:04', + 'fstTime': 720, + 'groupID': 0, + 'host': 'eheimdigital', + 'language': 'DE', + 'latestAvailableRevision': list([ + 1026, + 1030, + 2038, + 2038, + ]), + 'liveTime': 103680, + 'meshing': 1, + 'name': 'Mock filter', + 'netmode': 'ST', + 'power': '', + 'remote': 0, + 'revision': list([ + 1026, + 1030, + ]), + 'sstTime': 0, + 'stMail': 0, + 'stMailMode': 0, + 'sysLED': 45, + 'tID': 30, + 'tankconfig': 'WITHOUT_THERMO', + 'timezone': 60, + 'title': 'USRDTA', + 'to': 'USER', + 'unit': 0, + 'usrName': '', + 'version': 4, + }), + }), }), 'entry': dict({ 'data': dict({ diff --git a/tests/components/eheimdigital/snapshots/test_number.ambr b/tests/components/eheimdigital/snapshots/test_number.ambr index 4f3b0e462875..1c354d12169c 100644 --- a/tests/components/eheimdigital/snapshots/test_number.ambr +++ b/tests/components/eheimdigital/snapshots/test_number.ambr @@ -289,6 +289,182 @@ 'state': 'unknown', }) # --- +# name: test_setup[number.mock_filter_high_pulse_duration-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 200000, + 'min': 5, + 'mode': , + 'step': 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.mock_filter_high_pulse_duration', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'High pulse duration', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'high_pulse_time', + 'unique_id': '00:00:00:00:00:04_high_pulse_time', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[number.mock_filter_high_pulse_duration-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Mock filter High pulse duration', + 'max': 200000, + 'min': 5, + 'mode': , + 'step': 1, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.mock_filter_high_pulse_duration', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[number.mock_filter_low_pulse_duration-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 200000, + 'min': 5, + 'mode': , + 'step': 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.mock_filter_low_pulse_duration', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Low pulse duration', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'low_pulse_time', + 'unique_id': '00:00:00:00:00:04_low_pulse_time', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[number.mock_filter_low_pulse_duration-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Mock filter Low pulse duration', + 'max': 200000, + 'min': 5, + 'mode': , + 'step': 1, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.mock_filter_low_pulse_duration', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[number.mock_filter_system_led_brightness-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.mock_filter_system_led_brightness', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'System LED brightness', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'system_led', + 'unique_id': '00:00:00:00:00:04_system_led', + 'unit_of_measurement': '%', + }) +# --- +# name: test_setup[number.mock_filter_system_led_brightness-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter System LED brightness', + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1, + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'number.mock_filter_system_led_brightness', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup[number.mock_heater_night_temperature_offset-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/eheimdigital/snapshots/test_select.ambr b/tests/components/eheimdigital/snapshots/test_select.ambr index e7e0fee16c58..c23041c5a26d 100644 --- a/tests/components/eheimdigital/snapshots/test_select.ambr +++ b/tests/components/eheimdigital/snapshots/test_select.ambr @@ -58,3 +58,568 @@ 'state': 'unknown', }) # --- +# name: test_setup[select.mock_filter_constant_flow_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_filter_constant_flow_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Constant flow speed', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'const_flow_speed', + 'unique_id': '00:00:00:00:00:04_const_flow_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[select.mock_filter_constant_flow_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Constant flow speed', + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'select.mock_filter_constant_flow_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[select.mock_filter_day_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_filter_day_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Day speed', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'day_speed', + 'unique_id': '00:00:00:00:00:04_day_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[select.mock_filter_day_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Day speed', + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'select.mock_filter_day_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[select.mock_filter_filter_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'manual', + 'constant_flow', + 'pulse', + 'bio', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_filter_filter_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Filter mode', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'filter_mode', + 'unique_id': '00:00:00:00:00:04_filter_mode', + 'unit_of_measurement': None, + }) +# --- +# name: test_setup[select.mock_filter_filter_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Filter mode', + 'options': list([ + 'manual', + 'constant_flow', + 'pulse', + 'bio', + ]), + }), + 'context': , + 'entity_id': 'select.mock_filter_filter_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[select.mock_filter_high_pulse_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_filter_high_pulse_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'High pulse speed', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'high_pulse_speed', + 'unique_id': '00:00:00:00:00:04_high_pulse_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[select.mock_filter_high_pulse_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter High pulse speed', + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'select.mock_filter_high_pulse_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[select.mock_filter_low_pulse_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_filter_low_pulse_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Low pulse speed', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'low_pulse_speed', + 'unique_id': '00:00:00:00:00:04_low_pulse_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[select.mock_filter_low_pulse_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Low pulse speed', + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'select.mock_filter_low_pulse_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[select.mock_filter_manual_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + '35', + '37.5', + '40.5', + '43', + '45.5', + '48', + '51', + '53.5', + '56', + '59', + '61.5', + '64', + '66.5', + '69.5', + '72', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_filter_manual_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Manual speed', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'manual_speed', + 'unique_id': '00:00:00:00:00:04_manual_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[select.mock_filter_manual_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Manual speed', + 'options': list([ + '35', + '37.5', + '40.5', + '43', + '45.5', + '48', + '51', + '53.5', + '56', + '59', + '61.5', + '64', + '66.5', + '69.5', + '72', + ]), + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'select.mock_filter_manual_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[select.mock_filter_night_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_filter_night_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Night speed', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'night_speed', + 'unique_id': '00:00:00:00:00:04_night_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[select.mock_filter_night_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Night speed', + 'options': list([ + '400', + '440', + '480', + '515', + '550', + '585', + '620', + '650', + '680', + '710', + '740', + '770', + '800', + '830', + '860', + ]), + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'select.mock_filter_night_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/eheimdigital/snapshots/test_sensor.ambr b/tests/components/eheimdigital/snapshots/test_sensor.ambr index 7f12e9fbf9b4..25ec5c10a510 100644 --- a/tests/components/eheimdigital/snapshots/test_sensor.ambr +++ b/tests/components/eheimdigital/snapshots/test_sensor.ambr @@ -1,5 +1,5 @@ # serializer version: 1 -# name: test_setup_classic_vario[sensor.mock_classicvario_current_speed-entry] +# name: test_setup[sensor.mock_classicvario_current_speed-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -34,7 +34,7 @@ 'unit_of_measurement': '%', }) # --- -# name: test_setup_classic_vario[sensor.mock_classicvario_current_speed-state] +# name: test_setup[sensor.mock_classicvario_current_speed-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'friendly_name': 'Mock classicVARIO Current speed', @@ -48,7 +48,7 @@ 'state': 'unknown', }) # --- -# name: test_setup_classic_vario[sensor.mock_classicvario_error_code-entry] +# name: test_setup[sensor.mock_classicvario_error_code-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -89,7 +89,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_setup_classic_vario[sensor.mock_classicvario_error_code-state] +# name: test_setup[sensor.mock_classicvario_error_code-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'enum', @@ -108,7 +108,7 @@ 'state': 'unknown', }) # --- -# name: test_setup_classic_vario[sensor.mock_classicvario_remaining_hours_until_service-entry] +# name: test_setup[sensor.mock_classicvario_remaining_hours_until_service-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -149,7 +149,7 @@ 'unit_of_measurement': , }) # --- -# name: test_setup_classic_vario[sensor.mock_classicvario_remaining_hours_until_service-state] +# name: test_setup[sensor.mock_classicvario_remaining_hours_until_service-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'duration', @@ -164,3 +164,112 @@ 'state': 'unknown', }) # --- +# name: test_setup[sensor.mock_filter_current_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.mock_filter_current_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Current speed', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_speed', + 'unique_id': '00:00:00:00:00:04_current_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[sensor.mock_filter_current_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'frequency', + 'friendly_name': 'Mock filter Current speed', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.mock_filter_current_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[sensor.mock_filter_remaining_hours_until_service-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_filter_remaining_hours_until_service', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Remaining hours until service', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'service_hours', + 'unique_id': '00:00:00:00:00:04_service_hours', + 'unit_of_measurement': , + }) +# --- +# name: test_setup[sensor.mock_filter_remaining_hours_until_service-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Mock filter Remaining hours until service', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.mock_filter_remaining_hours_until_service', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/eheimdigital/snapshots/test_switch.ambr b/tests/components/eheimdigital/snapshots/test_switch.ambr index 5c5456d88406..6c126adeb010 100644 --- a/tests/components/eheimdigital/snapshots/test_switch.ambr +++ b/tests/components/eheimdigital/snapshots/test_switch.ambr @@ -1,5 +1,5 @@ # serializer version: 1 -# name: test_setup_classic_vario[switch.mock_classicvario-entry] +# name: test_setup[switch.mock_classicvario-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -34,7 +34,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_setup_classic_vario[switch.mock_classicvario-state] +# name: test_setup[switch.mock_classicvario-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'friendly_name': 'Mock classicVARIO', @@ -47,3 +47,51 @@ 'state': 'on', }) # --- +# name: test_setup[switch.mock_filter-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.mock_filter', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'filter_active', + 'unique_id': '00:00:00:00:00:04', + 'unit_of_measurement': None, + }) +# --- +# name: test_setup[switch.mock_filter-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter', + }), + 'context': , + 'entity_id': 'switch.mock_filter', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- diff --git a/tests/components/eheimdigital/snapshots/test_time.ambr b/tests/components/eheimdigital/snapshots/test_time.ambr index 754846b4d2b2..df5c9cd589e1 100644 --- a/tests/components/eheimdigital/snapshots/test_time.ambr +++ b/tests/components/eheimdigital/snapshots/test_time.ambr @@ -95,6 +95,102 @@ 'state': 'unknown', }) # --- +# name: test_setup[time.mock_filter_day_start_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'time', + 'entity_category': , + 'entity_id': 'time.mock_filter_day_start_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Day start time', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'day_start_time', + 'unique_id': '00:00:00:00:00:04_day_start_time', + 'unit_of_measurement': None, + }) +# --- +# name: test_setup[time.mock_filter_day_start_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Day start time', + }), + 'context': , + 'entity_id': 'time.mock_filter_day_start_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_setup[time.mock_filter_night_start_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'time', + 'entity_category': , + 'entity_id': 'time.mock_filter_night_start_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Night start time', + 'platform': 'eheimdigital', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'night_start_time', + 'unique_id': '00:00:00:00:00:04_night_start_time', + 'unit_of_measurement': None, + }) +# --- +# name: test_setup[time.mock_filter_night_start_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock filter Night start time', + }), + 'context': , + 'entity_id': 'time.mock_filter_night_start_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_setup[time.mock_heater_day_start_time-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/eheimdigital/test_number.py b/tests/components/eheimdigital/test_number.py index 5235dfcdb751..51492737b65e 100644 --- a/tests/components/eheimdigital/test_number.py +++ b/tests/components/eheimdigital/test_number.py @@ -19,7 +19,6 @@ from .conftest import init_integration from tests.common import MockConfigEntry, snapshot_platform -@pytest.mark.usefixtures("classic_vario_mock", "heater_mock") async def test_setup( hass: HomeAssistant, eheimdigital_hub_mock: MagicMock, @@ -48,7 +47,6 @@ async def test_setup( await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) -@pytest.mark.usefixtures("classic_vario_mock", "heater_mock") @pytest.mark.parametrize( ("device_name", "entity_list"), [ @@ -104,6 +102,23 @@ async def test_setup( ), ], ), + ( + "filter_mock", + [ + ( + "number.mock_filter_low_pulse_duration", + 20, + "time_low", + 20, + ), + ( + "number.mock_filter_high_pulse_duration", + 20, + "time_high", + 20, + ), + ], + ), ], ) async def test_set_value( @@ -135,7 +150,6 @@ async def test_set_value( assert calls[-1][1][0][item[2]] == item[3] -@pytest.mark.usefixtures("classic_vario_mock", "heater_mock") @pytest.mark.parametrize( ("device_name", "entity_list"), [ @@ -198,6 +212,32 @@ async def test_set_value( ), ], ), + ( + "filter_mock", + [ + ( + "number.mock_filter_low_pulse_duration", + "filter_data", + "pm_time_low", + 20, + 20, + ), + ( + "number.mock_filter_high_pulse_duration", + "filter_data", + "pm_time_high", + 20, + 20, + ), + ( + "number.mock_filter_system_led_brightness", + "usrdta", + "sysLED", + 20, + 20, + ), + ], + ), ], ) async def test_state_update( diff --git a/tests/components/eheimdigital/test_select.py b/tests/components/eheimdigital/test_select.py index ab577bbe0aa0..f4deac6665c4 100644 --- a/tests/components/eheimdigital/test_select.py +++ b/tests/components/eheimdigital/test_select.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, MagicMock, patch -from eheimdigital.types import FilterMode +from eheimdigital.types import FilterMode, FilterModeProf import pytest from syrupy.assertion import SnapshotAssertion @@ -20,7 +20,6 @@ from .conftest import init_integration from tests.common import MockConfigEntry, snapshot_platform -@pytest.mark.usefixtures("classic_vario_mock") async def test_setup( hass: HomeAssistant, eheimdigital_hub_mock: MagicMock, @@ -49,7 +48,6 @@ async def test_setup( await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) -@pytest.mark.usefixtures("classic_vario_mock") @pytest.mark.parametrize( ("device_name", "entity_list"), [ @@ -64,6 +62,28 @@ async def test_setup( ), ], ), + ( + "filter_mock", + [ + ( + "select.mock_filter_filter_mode", + "constant_flow", + "title", + "START_FILTER_NORMAL_MODE_WITH_COMP", + ), + ( + "select.mock_filter_manual_speed", + "61.5", + "frequency", + int(61.5 * 100), + ), + ("select.mock_filter_constant_flow_speed", "440", "flow_rate", 1), + ("select.mock_filter_day_speed", "480", "dfs_soll_day", 2), + ("select.mock_filter_night_speed", "860", "dfs_soll_night", 14), + ("select.mock_filter_high_pulse_speed", "620", "dfs_soll_high", 6), + ("select.mock_filter_low_pulse_speed", "770", "dfs_soll_low", 11), + ], + ), ], ) async def test_set_value( @@ -111,6 +131,60 @@ async def test_set_value( ), ], ), + ( + "filter_mock", + [ + ( + "select.mock_filter_filter_mode", + "filter_data", + "pumpMode", + int(FilterModeProf.CONSTANT_FLOW), + "constant_flow", + ), + ( + "select.mock_filter_manual_speed", + "filter_data", + "freqSoll", + int(61.5 * 100), + "61.5", + ), + ( + "select.mock_filter_constant_flow_speed", + "filter_data", + "sollStep", + 1, + "440", + ), + ( + "select.mock_filter_day_speed", + "filter_data", + "nm_dfs_soll_day", + 2, + "480", + ), + ( + "select.mock_filter_night_speed", + "filter_data", + "nm_dfs_soll_night", + 14, + "860", + ), + ( + "select.mock_filter_high_pulse_speed", + "filter_data", + "pm_dfs_soll_high", + 6, + "620", + ), + ( + "select.mock_filter_low_pulse_speed", + "filter_data", + "pm_dfs_soll_low", + 11, + "770", + ), + ], + ), ], ) async def test_state_update( diff --git a/tests/components/eheimdigital/test_sensor.py b/tests/components/eheimdigital/test_sensor.py index a2c0fae5b166..a7c1f6ab7648 100644 --- a/tests/components/eheimdigital/test_sensor.py +++ b/tests/components/eheimdigital/test_sensor.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, MagicMock, patch -from eheimdigital.types import EheimDeviceType, FilterErrorCode +from eheimdigital.types import FilterErrorCode import pytest from syrupy.assertion import SnapshotAssertion @@ -15,8 +15,7 @@ from .conftest import init_integration from tests.common import MockConfigEntry, get_sensor_display_state, snapshot_platform -@pytest.mark.usefixtures("classic_vario_mock") -async def test_setup_classic_vario( +async def test_setup( hass: HomeAssistant, eheimdigital_hub_mock: MagicMock, mock_config_entry: MockConfigEntry, @@ -35,15 +34,15 @@ async def test_setup_classic_vario( ): await hass.config_entries.async_setup(mock_config_entry.entry_id) - await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"]( - "00:00:00:00:00:03", EheimDeviceType.VERSION_EHEIM_CLASSIC_VARIO - ) - await hass.async_block_till_done() + for device in eheimdigital_hub_mock.return_value.devices: + await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"]( + device, eheimdigital_hub_mock.return_value.devices[device].device_type + ) + await hass.async_block_till_done() await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) -@pytest.mark.usefixtures("classic_vario_mock") @pytest.mark.parametrize( ("device_name", "entity_list"), [ @@ -73,6 +72,25 @@ async def test_setup_classic_vario( ), ], ), + ( + "filter_mock", + [ + ( + "sensor.mock_filter_current_speed", + "filter_data", + "freq", + 7200, + str(round(72.0, 1)), + ), + ( + "sensor.mock_filter_remaining_hours_until_service", + "filter_data", + "serviceHour", + 100, + str(round(100 / 24, 2)), + ), + ], + ), ], ) async def test_state_update( diff --git a/tests/components/eheimdigital/test_switch.py b/tests/components/eheimdigital/test_switch.py index 4195c0595043..86beb7b6a36f 100644 --- a/tests/components/eheimdigital/test_switch.py +++ b/tests/components/eheimdigital/test_switch.py @@ -2,7 +2,6 @@ from unittest.mock import AsyncMock, MagicMock, patch -from eheimdigital.types import EheimDeviceType import pytest from syrupy.assertion import SnapshotAssertion @@ -21,8 +20,7 @@ from .conftest import init_integration from tests.common import MockConfigEntry, snapshot_platform -@pytest.mark.usefixtures("classic_vario_mock") -async def test_setup_classic_vario( +async def test_setup( hass: HomeAssistant, eheimdigital_hub_mock: MagicMock, mock_config_entry: MockConfigEntry, @@ -41,10 +39,11 @@ async def test_setup_classic_vario( ): await hass.config_entries.async_setup(mock_config_entry.entry_id) - await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"]( - "00:00:00:00:00:03", EheimDeviceType.VERSION_EHEIM_CLASSIC_VARIO - ) - await hass.async_block_till_done() + for device in eheimdigital_hub_mock.return_value.devices: + await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"]( + device, eheimdigital_hub_mock.return_value.devices[device].device_type + ) + await hass.async_block_till_done() await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) @@ -52,37 +51,45 @@ async def test_setup_classic_vario( @pytest.mark.parametrize( ("service", "active"), [(SERVICE_TURN_OFF, False), (SERVICE_TURN_ON, True)] ) +@pytest.mark.parametrize( + ("device_name", "entity_id", "property_name"), + [ + ("classic_vario_mock", "switch.mock_classicvario", "filterActive"), + ("filter_mock", "switch.mock_filter", "active"), + ], +) async def test_turn_on_off( hass: HomeAssistant, eheimdigital_hub_mock: MagicMock, mock_config_entry: MockConfigEntry, - classic_vario_mock: MagicMock, + device_name: str, + entity_id: str, + property_name: str, service: str, active: bool, + request: pytest.FixtureRequest, ) -> None: """Test turning on/off the switch.""" + device: MagicMock = request.getfixturevalue(device_name) await init_integration(hass, mock_config_entry) await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"]( - "00:00:00:00:00:03", EheimDeviceType.VERSION_EHEIM_CLASSIC_VARIO + device.mac_address, device.device_type ) await hass.async_block_till_done() await hass.services.async_call( SWITCH_DOMAIN, service, - {ATTR_ENTITY_ID: "switch.mock_classicvario"}, + {ATTR_ENTITY_ID: entity_id}, blocking=True, ) - calls = [ - call for call in classic_vario_mock.hub.mock_calls if call[0] == "send_packet" - ] - assert len(calls) == 1 - assert calls[0][1][0].get("filterActive") == int(active) + calls = [call for call in device.hub.mock_calls if call[0] == "send_packet"] + assert calls[-1][1][0][property_name] == int(active) -@pytest.mark.usefixtures("classic_vario_mock") +@pytest.mark.usefixtures("classic_vario_mock", "filter_mock") @pytest.mark.parametrize( ("device_name", "entity_list"), [ @@ -105,6 +112,25 @@ async def test_turn_on_off( ), ], ), + ( + "filter_mock", + [ + ( + "switch.mock_filter", + "filter_data", + "filterActive", + 1, + "on", + ), + ( + "switch.mock_filter", + "filter_data", + "filterActive", + 0, + "off", + ), + ], + ), ], ) async def test_state_update( diff --git a/tests/components/eheimdigital/test_time.py b/tests/components/eheimdigital/test_time.py index 990a086e6337..557e21ff2e1a 100644 --- a/tests/components/eheimdigital/test_time.py +++ b/tests/components/eheimdigital/test_time.py @@ -20,7 +20,6 @@ from .conftest import init_integration from tests.common import MockConfigEntry, snapshot_platform -@pytest.mark.usefixtures("classic_vario_mock", "heater_mock") async def test_setup( hass: HomeAssistant, eheimdigital_hub_mock: MagicMock, @@ -49,7 +48,6 @@ async def test_setup( await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) -@pytest.mark.usefixtures("classic_vario_mock", "heater_mock") @pytest.mark.parametrize( ("device_name", "entity_list"), [ @@ -87,6 +85,23 @@ async def test_setup( ), ], ), + ( + "filter_mock", + [ + ( + "time.mock_filter_day_start_time", + time(9, 0, tzinfo=timezone(timedelta(hours=1))), + "end_time_night_mode", + 9 * 60, + ), + ( + "time.mock_filter_night_start_time", + time(19, 0, tzinfo=timezone(timedelta(hours=1))), + "start_time_night_mode", + 19 * 60, + ), + ], + ), ], ) async def test_set_value( @@ -118,7 +133,6 @@ async def test_set_value( assert calls[-1][1][0][item[2]] == item[3] -@pytest.mark.usefixtures("classic_vario_mock", "heater_mock") @pytest.mark.parametrize( ("device_name", "entity_list"), [ @@ -160,6 +174,25 @@ async def test_set_value( ), ], ), + ( + "filter_mock", + [ + ( + "time.mock_filter_day_start_time", + "filter_data", + "end_time_night_mode", + 540, + time(9, 0, tzinfo=timezone(timedelta(hours=1))).isoformat(), + ), + ( + "time.mock_filter_night_start_time", + "filter_data", + "start_time_night_mode", + 1320, + time(22, 0, tzinfo=timezone(timedelta(hours=1))).isoformat(), + ), + ], + ), ], ) async def test_state_update(