1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 08:26:41 +01:00

Add support for the reeflexUV+e to eheimdigital (#163656)

This commit is contained in:
Sid
2026-03-06 20:28:39 +01:00
committed by GitHub
parent 4bcea27151
commit 7e4b8e802e
18 changed files with 1081 additions and 4 deletions

View File

@@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant
from .coordinator import EheimDigitalConfigEntry
TO_REDACT = {"emailAddr", "usrName"}
TO_REDACT = {"emailAddr", "usrName", "api_usrName", "api_password"}
async def async_get_config_entry_diagnostics(

View File

@@ -8,6 +8,7 @@ from eheimdigital.classic_vario import EheimDigitalClassicVario
from eheimdigital.device import EheimDigitalDevice
from eheimdigital.filter import EheimDigitalFilter
from eheimdigital.heater import EheimDigitalHeater
from eheimdigital.reeflex import EheimDigitalReeflexUV
from eheimdigital.types import HeaterUnit
from homeassistant.components.number import (
@@ -44,6 +45,47 @@ class EheimDigitalNumberDescription[_DeviceT: EheimDigitalDevice](
uom_fn: Callable[[_DeviceT], str] | None = None
REEFLEX_DESCRIPTIONS: tuple[
EheimDigitalNumberDescription[EheimDigitalReeflexUV], ...
] = (
EheimDigitalNumberDescription[EheimDigitalReeflexUV](
key="daily_burn_time",
translation_key="daily_burn_time",
entity_category=EntityCategory.CONFIG,
native_step=PRECISION_WHOLE,
native_unit_of_measurement=UnitOfTime.MINUTES,
device_class=NumberDeviceClass.DURATION,
native_min_value=0,
native_max_value=1440,
value_fn=lambda device: device.daily_burn_time,
set_value_fn=lambda device, value: device.set_daily_burn_time(int(value)),
),
EheimDigitalNumberDescription[EheimDigitalReeflexUV](
key="booster_time",
translation_key="booster_time",
entity_category=EntityCategory.CONFIG,
native_step=PRECISION_WHOLE,
native_unit_of_measurement=UnitOfTime.MINUTES,
device_class=NumberDeviceClass.DURATION,
native_min_value=0,
native_max_value=20160,
value_fn=lambda device: device.booster_time,
set_value_fn=lambda device, value: device.set_booster_time(int(value)),
),
EheimDigitalNumberDescription[EheimDigitalReeflexUV](
key="pause_time",
translation_key="pause_time",
entity_category=EntityCategory.CONFIG,
native_step=PRECISION_WHOLE,
native_unit_of_measurement=UnitOfTime.MINUTES,
device_class=NumberDeviceClass.DURATION,
native_min_value=0,
native_max_value=20160,
value_fn=lambda device: device.pause_time,
set_value_fn=lambda device, value: device.set_pause_time(int(value)),
),
)
FILTER_DESCRIPTIONS: tuple[EheimDigitalNumberDescription[EheimDigitalFilter], ...] = (
EheimDigitalNumberDescription[EheimDigitalFilter](
key="high_pulse_time",
@@ -189,6 +231,13 @@ async def async_setup_entry(
)
for description in HEATER_DESCRIPTIONS
)
if isinstance(device, EheimDigitalReeflexUV):
entities.extend(
EheimDigitalNumber[EheimDigitalReeflexUV](
coordinator, device, description
)
for description in REEFLEX_DESCRIPTIONS
)
entities.extend(
EheimDigitalNumber[EheimDigitalDevice](coordinator, device, description)
for description in GENERAL_DESCRIPTIONS

View File

@@ -7,9 +7,11 @@ from typing import Any, Literal, override
from eheimdigital.classic_vario import EheimDigitalClassicVario
from eheimdigital.device import EheimDigitalDevice
from eheimdigital.filter import EheimDigitalFilter
from eheimdigital.reeflex import EheimDigitalReeflexUV
from eheimdigital.types import (
FilterMode,
FilterModeProf,
ReeflexMode,
UnitOfMeasurement as EheimDigitalUnitOfMeasurement,
)
@@ -36,6 +38,20 @@ class EheimDigitalSelectDescription[_DeviceT: EheimDigitalDevice](
set_value_fn: Callable[[_DeviceT, str], Awaitable[None] | None]
REEFLEX_DESCRIPTIONS: tuple[
EheimDigitalSelectDescription[EheimDigitalReeflexUV], ...
] = (
EheimDigitalSelectDescription[EheimDigitalReeflexUV](
key="mode",
translation_key="mode",
value_fn=lambda device: device.mode.name.lower(),
set_value_fn=(
lambda device, value: device.set_mode(ReeflexMode[value.upper()])
),
options=[name.lower() for name in ReeflexMode.__members__],
),
)
FILTER_DESCRIPTIONS: tuple[EheimDigitalSelectDescription[EheimDigitalFilter], ...] = (
EheimDigitalSelectDescription[EheimDigitalFilter](
key="filter_mode",
@@ -176,6 +192,13 @@ async def async_setup_entry(
EheimDigitalFilterSelect(coordinator, device, description)
for description in FILTER_DESCRIPTIONS
)
if isinstance(device, EheimDigitalReeflexUV):
entities.extend(
EheimDigitalSelect[EheimDigitalReeflexUV](
coordinator, device, description
)
for description in REEFLEX_DESCRIPTIONS
)
async_add_entities(entities)

View File

@@ -58,6 +58,12 @@
}
},
"number": {
"booster_time": {
"name": "Booster duration"
},
"daily_burn_time": {
"name": "Daily burn duration"
},
"day_speed": {
"name": "Day speed"
},
@@ -76,6 +82,7 @@
"night_temperature_offset": {
"name": "Night temperature offset"
},
"pause_time": { "name": "Pause duration" },
"system_led": {
"name": "System LED brightness"
},
@@ -108,6 +115,10 @@
"manual_speed": {
"name": "Manual speed"
},
"mode": {
"name": "Operation mode",
"state": { "constant": "Constant", "daycycle": "Daycycle" }
},
"night_speed": {
"name": "Night speed"
}
@@ -127,9 +138,18 @@
"operating_time": {
"name": "Operating time"
},
"remaining_booster_time": {
"name": "Remaining booster time"
},
"remaining_pause_time": {
"name": "Remaining pause time"
},
"service_hours": {
"name": "Remaining hours until service"
},
"time_until_next_service": {
"name": "Time until next service"
},
"turn_feeding_time": {
"name": "Remaining off time after feeding"
},
@@ -137,12 +157,26 @@
"name": "Remaining off time"
}
},
"switch": {
"booster": {
"name": "Booster"
},
"expert": {
"name": "Expert mode"
},
"pause": {
"name": "Pause"
}
},
"time": {
"day_start_time": {
"name": "Day start time"
},
"night_start_time": {
"name": "Night start time"
},
"start_time": {
"name": "Start time"
}
}
},

View File

@@ -1,12 +1,16 @@
"""EHEIM Digital switches."""
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import Any, override
from eheimdigital.classic_vario import EheimDigitalClassicVario
from eheimdigital.device import EheimDigitalDevice
from eheimdigital.filter import EheimDigitalFilter
from eheimdigital.reeflex import EheimDigitalReeflexUV
from homeassistant.components.switch import SwitchEntity
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -17,6 +21,50 @@ from .entity import EheimDigitalEntity, exception_handler
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class EheimDigitalSwitchDescription[_DeviceT: EheimDigitalDevice](
SwitchEntityDescription
):
"""Class describing EHEIM Digital switch entities."""
is_on_fn: Callable[[_DeviceT], bool]
set_fn: Callable[[_DeviceT, bool], Awaitable[None]]
REEFLEX_DESCRIPTIONS: tuple[
EheimDigitalSwitchDescription[EheimDigitalReeflexUV], ...
] = (
EheimDigitalSwitchDescription[EheimDigitalReeflexUV](
key="active",
name=None,
entity_category=EntityCategory.CONFIG,
is_on_fn=lambda device: device.is_active,
set_fn=lambda device, value: device.set_active(active=value),
),
EheimDigitalSwitchDescription[EheimDigitalReeflexUV](
key="pause",
translation_key="pause",
entity_category=EntityCategory.CONFIG,
is_on_fn=lambda device: device.pause,
set_fn=lambda device, value: device.set_pause(pause=value),
),
EheimDigitalSwitchDescription[EheimDigitalReeflexUV](
key="booster",
translation_key="booster",
entity_category=EntityCategory.CONFIG,
is_on_fn=lambda device: device.booster,
set_fn=lambda device, value: device.set_booster(active=value),
),
EheimDigitalSwitchDescription[EheimDigitalReeflexUV](
key="expert",
translation_key="expert",
entity_category=EntityCategory.CONFIG,
is_on_fn=lambda device: device.expert,
set_fn=lambda device, value: device.set_expert(active=value),
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: EheimDigitalConfigEntry,
@@ -32,7 +80,14 @@ async def async_setup_entry(
entities: list[SwitchEntity] = []
for device in device_address.values():
if isinstance(device, (EheimDigitalClassicVario, EheimDigitalFilter)):
entities.append(EheimDigitalFilterSwitch(coordinator, device)) # noqa: PERF401
entities.append(EheimDigitalFilterSwitch(coordinator, device))
if isinstance(device, EheimDigitalReeflexUV):
entities.extend(
EheimDigitalSwitch[EheimDigitalReeflexUV](
coordinator, device, description
)
for description in REEFLEX_DESCRIPTIONS
)
async_add_entities(entities)
@@ -40,6 +95,39 @@ async def async_setup_entry(
async_setup_device_entities(coordinator.hub.devices)
class EheimDigitalSwitch[_DeviceT: EheimDigitalDevice](
EheimDigitalEntity[_DeviceT], SwitchEntity
):
"""Represent a EHEIM Digital switch entity."""
entity_description: EheimDigitalSwitchDescription[_DeviceT]
def __init__(
self,
coordinator: EheimDigitalUpdateCoordinator,
device: _DeviceT,
description: EheimDigitalSwitchDescription[_DeviceT],
) -> None:
"""Initialize an EHEIM Digital switch entity."""
super().__init__(coordinator, device)
self.entity_description = description
self._attr_unique_id = f"{self._device_address}_{description.key}"
@exception_handler
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the switch."""
return await self.entity_description.set_fn(self._device, True)
@exception_handler
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the switch."""
return await self.entity_description.set_fn(self._device, False)
@override
def _async_update_attrs(self) -> None:
self._attr_is_on = self.entity_description.is_on_fn(self._device)
class EheimDigitalFilterSwitch(
EheimDigitalEntity[EheimDigitalClassicVario | EheimDigitalFilter], SwitchEntity
):

View File

@@ -9,6 +9,7 @@ from eheimdigital.classic_vario import EheimDigitalClassicVario
from eheimdigital.device import EheimDigitalDevice
from eheimdigital.filter import EheimDigitalFilter
from eheimdigital.heater import EheimDigitalHeater
from eheimdigital.reeflex import EheimDigitalReeflexUV
from homeassistant.components.time import TimeEntity, TimeEntityDescription
from homeassistant.const import EntityCategory
@@ -29,6 +30,16 @@ class EheimDigitalTimeDescription[_DeviceT: EheimDigitalDevice](TimeEntityDescri
set_value_fn: Callable[[_DeviceT, time], Awaitable[None]]
REEFLEX_DESCRIPTIONS: tuple[EheimDigitalTimeDescription[EheimDigitalReeflexUV], ...] = (
EheimDigitalTimeDescription[EheimDigitalReeflexUV](
key="start_time",
translation_key="start_time",
entity_category=EntityCategory.CONFIG,
value_fn=lambda device: device.start_time,
set_value_fn=lambda device, value: device.set_day_start_time(value),
),
)
FILTER_DESCRIPTIONS: tuple[EheimDigitalTimeDescription[EheimDigitalFilter], ...] = (
EheimDigitalTimeDescription[EheimDigitalFilter](
key="day_start_time",
@@ -118,6 +129,13 @@ async def async_setup_entry(
)
for description in HEATER_DESCRIPTIONS
)
if isinstance(device, EheimDigitalReeflexUV):
entities.extend(
EheimDigitalTime[EheimDigitalReeflexUV](
coordinator, device, description
)
for description in REEFLEX_DESCRIPTIONS
)
async_add_entities(entities)

View File

@@ -8,6 +8,7 @@ from eheimdigital.classic_vario import EheimDigitalClassicVario
from eheimdigital.filter import EheimDigitalFilter
from eheimdigital.heater import EheimDigitalHeater
from eheimdigital.hub import EheimDigitalHub
from eheimdigital.reeflex import EheimDigitalReeflexUV
from eheimdigital.types import (
AcclimatePacket,
CCVPacket,
@@ -16,6 +17,7 @@ from eheimdigital.types import (
CloudPacket,
FilterDataPacket,
MoonPacket,
ReeflexDataPacket,
UsrDtaPacket,
)
import pytest
@@ -97,12 +99,26 @@ def filter_mock():
return eheim_filter
@pytest.fixture
def reeflex_mock():
"""Mock a reeflex device."""
eheim_reeflex = EheimDigitalReeflexUV(
MagicMock(spec=EheimDigitalHub),
UsrDtaPacket(load_json_object_fixture("reeflex/usrdta.json", DOMAIN)),
)
eheim_reeflex.reeflex_data = ReeflexDataPacket(
load_json_object_fixture("reeflex/reeflex_data.json", DOMAIN)
)
return eheim_reeflex
@pytest.fixture
def eheimdigital_hub_mock(
classic_led_ctrl_mock: MagicMock,
heater_mock: MagicMock,
classic_vario_mock: MagicMock,
filter_mock: MagicMock,
reeflex_mock: MagicMock,
) -> Generator[AsyncMock]:
"""Mock eheimdigital hub."""
with (
@@ -120,6 +136,7 @@ def eheimdigital_hub_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,
"00:00:00:00:00:05": reeflex_mock,
}
eheimdigital_hub_mock.return_value.main = classic_led_ctrl_mock
yield eheimdigital_hub_mock

View File

@@ -0,0 +1,24 @@
{
"title": "REEFLEX_DATA",
"from": "00:00:00:00:00:05",
"startTime": 390,
"dailyBurnTime": 720,
"isLighting": 0,
"isActive": 1,
"swOnDay": 1,
"swOnNight": 0,
"pause": 0,
"booster": 0,
"boosterTime": 0,
"remainingBoosterTime": 0,
"expert": 1,
"mode": 1,
"pauseTime": 0,
"remainingPauseTime": 0,
"isUVCConnected": 1,
"timeUntilNextService": 325,
"version": 8,
"sync": "",
"partnerName": "",
"to": "USER_OPT"
}

View File

@@ -0,0 +1,36 @@
{
"title": "USRDTA",
"from": "00:00:00:00:00:05",
"name": "Mock reeflex",
"aqName": "Mock Aquarium",
"version": 11,
"language": "DE",
"timezone": 60,
"tID": 30,
"dst": 1,
"tankconfig": "reeflex",
"power": "9",
"netmode": "ST",
"host": "eheimdigital",
"groupID": 0,
"meshing": 1,
"firstStart": 0,
"revision": [2044, 2044],
"build": ["1765358332000", "1765354627915"],
"latestAvailableRevision": [-1, -1, -1, -1],
"firmwareAvailable": 0,
"softChange": 0,
"emailAddr": "xxx",
"stMail": 0,
"stMailMode": 0,
"fstTime": 0,
"sstTime": 0,
"liveTime": 650100,
"usrName": "xxx",
"unit": 0,
"demoUse": 0,
"sysLED": 100,
"api_usrName": "api",
"api_password": "admin",
"to": "USER"
}

View File

@@ -315,6 +315,79 @@
'version': 4,
}),
}),
'00:00:00:00:00:05': dict({
'reeflex_data': dict({
'booster': 0,
'boosterTime': 0,
'dailyBurnTime': 720,
'expert': 1,
'from': '00:00:00:00:00:05',
'isActive': 1,
'isLighting': 0,
'isUVCConnected': 1,
'mode': 1,
'partnerName': '',
'pause': 0,
'pauseTime': 0,
'remainingBoosterTime': 0,
'remainingPauseTime': 0,
'startTime': 390,
'swOnDay': 1,
'swOnNight': 0,
'sync': '',
'timeUntilNextService': 325,
'title': 'REEFLEX_DATA',
'to': 'USER_OPT',
'version': 8,
}),
'usrdta': dict({
'api_password': 'admin',
'api_usrName': 'api',
'aqName': 'Mock Aquarium',
'build': list([
'1765358332000',
'1765354627915',
]),
'demoUse': 0,
'dst': 1,
'emailAddr': 'xxx',
'firmwareAvailable': 0,
'firstStart': 0,
'from': '00:00:00:00:00:05',
'fstTime': 0,
'groupID': 0,
'host': 'eheimdigital',
'language': 'DE',
'latestAvailableRevision': list([
-1,
-1,
-1,
-1,
]),
'liveTime': 650100,
'meshing': 1,
'name': 'Mock reeflex',
'netmode': 'ST',
'power': '9',
'revision': list([
2044,
2044,
]),
'softChange': 0,
'sstTime': 0,
'stMail': 0,
'stMailMode': 0,
'sysLED': 100,
'tID': 30,
'tankconfig': 'reeflex',
'timezone': 60,
'title': 'USRDTA',
'to': 'USER',
'unit': 0,
'usrName': 'xxx',
'version': 11,
}),
}),
}),
'entry': dict({
'data': dict({

View File

@@ -650,3 +650,242 @@
'state': 'unknown',
})
# ---
# name: test_setup[number.mock_reeflex_booster_duration-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 20160,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.mock_reeflex_booster_duration',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Booster duration',
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Booster duration',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'booster_time',
'unique_id': '00:00:00:00:00:05_booster_time',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
})
# ---
# name: test_setup[number.mock_reeflex_booster_duration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock reeflex Booster duration',
'max': 20160,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_reeflex_booster_duration',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[number.mock_reeflex_daily_burn_duration-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 1440,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.mock_reeflex_daily_burn_duration',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Daily burn duration',
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Daily burn duration',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'daily_burn_time',
'unique_id': '00:00:00:00:00:05_daily_burn_time',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
})
# ---
# name: test_setup[number.mock_reeflex_daily_burn_duration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock reeflex Daily burn duration',
'max': 1440,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_reeflex_daily_burn_duration',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[number.mock_reeflex_pause_duration-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 20160,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.mock_reeflex_pause_duration',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Pause duration',
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Pause duration',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'pause_time',
'unique_id': '00:00:00:00:00:05_pause_time',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
})
# ---
# name: test_setup[number.mock_reeflex_pause_duration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock reeflex Pause duration',
'max': 20160,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_reeflex_pause_duration',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[number.mock_reeflex_system_led_brightness-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 100,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.mock_reeflex_system_led_brightness',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'System LED brightness',
'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:05_system_led',
'unit_of_measurement': '%',
})
# ---
# name: test_setup[number.mock_reeflex_system_led_brightness-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Mock reeflex System LED brightness',
'max': 100,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'number.mock_reeflex_system_led_brightness',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@@ -631,3 +631,61 @@
'state': 'unknown',
})
# ---
# name: test_setup[select.mock_reeflex_operation_mode-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'constant',
'daycycle',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': None,
'entity_id': 'select.mock_reeflex_operation_mode',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Operation mode',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Operation mode',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'mode',
'unique_id': '00:00:00:00:00:05_mode',
'unit_of_measurement': None,
})
# ---
# name: test_setup[select.mock_reeflex_operation_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Mock reeflex Operation mode',
'options': list([
'constant',
'daycycle',
]),
}),
'context': <ANY>,
'entity_id': 'select.mock_reeflex_operation_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@@ -97,3 +97,199 @@
'state': 'on',
})
# ---
# name: test_setup[switch.mock_reeflex-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.mock_reeflex',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 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': None,
'unique_id': '00:00:00:00:00:05_active',
'unit_of_measurement': None,
})
# ---
# name: test_setup[switch.mock_reeflex-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Mock reeflex',
}),
'context': <ANY>,
'entity_id': 'switch.mock_reeflex',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[switch.mock_reeflex_booster-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.mock_reeflex_booster',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Booster',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Booster',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'booster',
'unique_id': '00:00:00:00:00:05_booster',
'unit_of_measurement': None,
})
# ---
# name: test_setup[switch.mock_reeflex_booster-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Mock reeflex Booster',
}),
'context': <ANY>,
'entity_id': 'switch.mock_reeflex_booster',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[switch.mock_reeflex_expert_mode-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.mock_reeflex_expert_mode',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Expert mode',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Expert mode',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'expert',
'unique_id': '00:00:00:00:00:05_expert',
'unit_of_measurement': None,
})
# ---
# name: test_setup[switch.mock_reeflex_expert_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Mock reeflex Expert mode',
}),
'context': <ANY>,
'entity_id': 'switch.mock_reeflex_expert_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[switch.mock_reeflex_pause-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.mock_reeflex_pause',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Pause',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Pause',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'pause',
'unique_id': '00:00:00:00:00:05_pause',
'unit_of_measurement': None,
})
# ---
# name: test_setup[switch.mock_reeflex_pause-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Mock reeflex Pause',
}),
'context': <ANY>,
'entity_id': 'switch.mock_reeflex_pause',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@@ -293,3 +293,52 @@
'state': 'unknown',
})
# ---
# name: test_setup[time.mock_reeflex_start_time-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'time',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'time.mock_reeflex_start_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Start time',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Start time',
'platform': 'eheimdigital',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'start_time',
'unique_id': '00:00:00:00:00:05_start_time',
'unit_of_measurement': None,
})
# ---
# name: test_setup[time.mock_reeflex_start_time-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Mock reeflex Start time',
}),
'context': <ANY>,
'entity_id': 'time.mock_reeflex_start_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@@ -119,6 +119,35 @@ async def test_setup(
),
],
),
(
"reeflex_mock",
[
(
"number.mock_reeflex_daily_burn_duration",
20,
"dailyBurnTime",
20,
),
(
"number.mock_reeflex_booster_duration",
20,
"boosterTime",
20,
),
(
"number.mock_reeflex_pause_duration",
20,
"pauseTime",
20,
),
(
"number.mock_reeflex_system_led_brightness",
20,
"sysLED",
20,
),
],
),
],
)
async def test_set_value(
@@ -238,6 +267,39 @@ async def test_set_value(
),
],
),
(
"reeflex_mock",
[
(
"number.mock_reeflex_daily_burn_duration",
"reeflex_data",
"dailyBurnTime",
20,
20,
),
(
"number.mock_reeflex_booster_duration",
"reeflex_data",
"boosterTime",
20,
20,
),
(
"number.mock_reeflex_pause_duration",
"reeflex_data",
"pauseTime",
20,
20,
),
(
"number.mock_reeflex_system_led_brightness",
"usrdta",
"sysLED",
100,
100,
),
],
),
],
)
async def test_state_update(

View File

@@ -2,7 +2,7 @@
from unittest.mock import AsyncMock, MagicMock, patch
from eheimdigital.types import FilterMode, FilterModeProf
from eheimdigital.types import FilterMode, FilterModeProf, ReeflexMode
import pytest
from syrupy.assertion import SnapshotAssertion
@@ -84,6 +84,17 @@ async def test_setup(
("select.mock_filter_low_pulse_speed", "770", "dfs_soll_low", 11),
],
),
(
"reeflex_mock",
[
(
"select.mock_reeflex_operation_mode",
"constant",
"mode",
int(ReeflexMode.CONSTANT),
),
],
),
],
)
async def test_set_value(
@@ -185,6 +196,18 @@ async def test_set_value(
),
],
),
(
"reeflex_mock",
[
(
"select.mock_reeflex_operation_mode",
"reeflex_data",
"mode",
int(ReeflexMode.CONSTANT),
"constant",
),
],
),
],
)
async def test_state_update(

View File

@@ -56,6 +56,10 @@ async def test_setup(
[
("classic_vario_mock", "switch.mock_classicvario", "filterActive"),
("filter_mock", "switch.mock_filter", "active"),
("reeflex_mock", "switch.mock_reeflex", "isActive"),
("reeflex_mock", "switch.mock_reeflex_pause", "pause"),
("reeflex_mock", "switch.mock_reeflex_booster", "booster"),
("reeflex_mock", "switch.mock_reeflex_expert_mode", "expert"),
],
)
async def test_turn_on_off(
@@ -131,6 +135,67 @@ async def test_turn_on_off(
),
],
),
(
"reeflex_mock",
[
(
"switch.mock_reeflex",
"reeflex_data",
"isActive",
1,
"on",
),
(
"switch.mock_reeflex",
"reeflex_data",
"isActive",
0,
"off",
),
(
"switch.mock_reeflex_pause",
"reeflex_data",
"pause",
1,
"on",
),
(
"switch.mock_reeflex_pause",
"reeflex_data",
"pause",
0,
"off",
),
(
"switch.mock_reeflex_booster",
"reeflex_data",
"booster",
1,
"on",
),
(
"switch.mock_reeflex_booster",
"reeflex_data",
"booster",
0,
"off",
),
(
"switch.mock_reeflex_expert_mode",
"reeflex_data",
"expert",
1,
"on",
),
(
"switch.mock_reeflex_expert_mode",
"reeflex_data",
"expert",
0,
"off",
),
],
),
],
)
async def test_state_update(

View File

@@ -102,6 +102,17 @@ async def test_setup(
),
],
),
(
"reeflex_mock",
[
(
"time.mock_reeflex_start_time",
time(9, 0, tzinfo=timezone(timedelta(hours=1))),
"startTime",
9 * 60,
),
],
),
],
)
async def test_set_value(
@@ -193,6 +204,18 @@ async def test_set_value(
),
],
),
(
"reeflex_mock",
[
(
"time.mock_reeflex_start_time",
"reeflex_data",
"startTime",
540,
time(9, 0, tzinfo=timezone(timedelta(hours=1))).isoformat(),
),
],
),
],
)
async def test_state_update(