Skip to content

Commit dbc38cd

Browse files
autinerdjoostlek
andauthored
Add switch platform to eheimdigital (home-assistant#142412)
* Add switch platform to eheimdigital * docstring fixes * Review * Review --------- Co-authored-by: Joostlek <joostlek@outlook.com>
1 parent 102d55e commit dbc38cd

File tree

5 files changed

+232
-0
lines changed

5 files changed

+232
-0
lines changed

homeassistant/components/eheimdigital/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Platform.LIGHT,
1515
Platform.NUMBER,
1616
Platform.SENSOR,
17+
Platform.SWITCH,
1718
Platform.TIME,
1819
]
1920

homeassistant/components/eheimdigital/icons.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
}
3232
}
3333
},
34+
"switch": {
35+
"filter_active": {
36+
"default": "mdi:pump",
37+
"state": {
38+
"off": "mdi:pump-off"
39+
}
40+
}
41+
},
3442
"time": {
3543
"day_start_time": {
3644
"default": "mdi:weather-sunny"
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""EHEIM Digital switches."""
2+
3+
from typing import Any, override
4+
5+
from eheimdigital.classic_vario import EheimDigitalClassicVario
6+
from eheimdigital.device import EheimDigitalDevice
7+
8+
from homeassistant.components.switch import SwitchEntity
9+
from homeassistant.core import HomeAssistant
10+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
11+
12+
from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator
13+
from .entity import EheimDigitalEntity
14+
15+
# Coordinator is used to centralize the data updates
16+
PARALLEL_UPDATES = 0
17+
18+
19+
async def async_setup_entry(
20+
hass: HomeAssistant,
21+
entry: EheimDigitalConfigEntry,
22+
async_add_entities: AddConfigEntryEntitiesCallback,
23+
) -> None:
24+
"""Set up the callbacks for the coordinator so switches can be added as devices are found."""
25+
coordinator = entry.runtime_data
26+
27+
def async_setup_device_entities(
28+
device_address: dict[str, EheimDigitalDevice],
29+
) -> None:
30+
"""Set up the switch entities for one or multiple devices."""
31+
entities: list[SwitchEntity] = []
32+
for device in device_address.values():
33+
if isinstance(device, EheimDigitalClassicVario):
34+
entities.append(EheimDigitalClassicVarioSwitch(coordinator, device)) # noqa: PERF401
35+
36+
async_add_entities(entities)
37+
38+
coordinator.add_platform_callback(async_setup_device_entities)
39+
async_setup_device_entities(coordinator.hub.devices)
40+
41+
42+
class EheimDigitalClassicVarioSwitch(
43+
EheimDigitalEntity[EheimDigitalClassicVario], SwitchEntity
44+
):
45+
"""Represent an EHEIM Digital classicVARIO switch entity."""
46+
47+
_attr_translation_key = "filter_active"
48+
_attr_name = None
49+
50+
def __init__(
51+
self,
52+
coordinator: EheimDigitalUpdateCoordinator,
53+
device: EheimDigitalClassicVario,
54+
) -> None:
55+
"""Initialize an EHEIM Digital classicVARIO switch entity."""
56+
super().__init__(coordinator, device)
57+
self._attr_unique_id = device.mac_address
58+
self._async_update_attrs()
59+
60+
@override
61+
async def async_turn_off(self, **kwargs: Any) -> None:
62+
await self._device.set_active(active=False)
63+
64+
@override
65+
async def async_turn_on(self, **kwargs: Any) -> None:
66+
await self._device.set_active(active=True)
67+
68+
@override
69+
def _async_update_attrs(self) -> None:
70+
self._attr_is_on = self._device.is_active
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# serializer version: 1
2+
# name: test_setup_classic_vario[switch.mock_classicvario-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': set({
5+
}),
6+
'area_id': None,
7+
'capabilities': None,
8+
'config_entry_id': <ANY>,
9+
'config_subentry_id': <ANY>,
10+
'device_class': None,
11+
'device_id': <ANY>,
12+
'disabled_by': None,
13+
'domain': 'switch',
14+
'entity_category': None,
15+
'entity_id': 'switch.mock_classicvario',
16+
'has_entity_name': True,
17+
'hidden_by': None,
18+
'icon': None,
19+
'id': <ANY>,
20+
'labels': set({
21+
}),
22+
'name': None,
23+
'options': dict({
24+
}),
25+
'original_device_class': None,
26+
'original_icon': None,
27+
'original_name': None,
28+
'platform': 'eheimdigital',
29+
'previous_unique_id': None,
30+
'supported_features': 0,
31+
'translation_key': 'filter_active',
32+
'unique_id': '00:00:00:00:00:03',
33+
'unit_of_measurement': None,
34+
})
35+
# ---
36+
# name: test_setup_classic_vario[switch.mock_classicvario-state]
37+
StateSnapshot({
38+
'attributes': ReadOnlyDict({
39+
'friendly_name': 'Mock classicVARIO',
40+
}),
41+
'context': <ANY>,
42+
'entity_id': 'switch.mock_classicvario',
43+
'last_changed': <ANY>,
44+
'last_reported': <ANY>,
45+
'last_updated': <ANY>,
46+
'state': 'on',
47+
})
48+
# ---
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
"""Tests for the switch module."""
2+
3+
from unittest.mock import AsyncMock, MagicMock, patch
4+
5+
from eheimdigital.types import EheimDeviceType
6+
import pytest
7+
from syrupy.assertion import SnapshotAssertion
8+
9+
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
10+
from homeassistant.const import (
11+
ATTR_ENTITY_ID,
12+
SERVICE_TURN_OFF,
13+
SERVICE_TURN_ON,
14+
STATE_OFF,
15+
STATE_ON,
16+
Platform,
17+
)
18+
from homeassistant.core import HomeAssistant
19+
from homeassistant.helpers import entity_registry as er
20+
21+
from .conftest import init_integration
22+
23+
from tests.common import MockConfigEntry, snapshot_platform
24+
25+
26+
@pytest.mark.usefixtures("classic_vario_mock")
27+
async def test_setup_classic_vario(
28+
hass: HomeAssistant,
29+
eheimdigital_hub_mock: MagicMock,
30+
mock_config_entry: MockConfigEntry,
31+
entity_registry: er.EntityRegistry,
32+
snapshot: SnapshotAssertion,
33+
) -> None:
34+
"""Test switch platform setup for the filter."""
35+
mock_config_entry.add_to_hass(hass)
36+
37+
with (
38+
patch("homeassistant.components.eheimdigital.PLATFORMS", [Platform.SWITCH]),
39+
patch(
40+
"homeassistant.components.eheimdigital.coordinator.asyncio.Event",
41+
new=AsyncMock,
42+
),
43+
):
44+
await hass.config_entries.async_setup(mock_config_entry.entry_id)
45+
46+
await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"](
47+
"00:00:00:00:00:03", EheimDeviceType.VERSION_EHEIM_CLASSIC_VARIO
48+
)
49+
await hass.async_block_till_done()
50+
51+
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
52+
53+
54+
@pytest.mark.parametrize(
55+
("service", "active"), [(SERVICE_TURN_OFF, False), (SERVICE_TURN_ON, True)]
56+
)
57+
async def test_turn_on_off(
58+
hass: HomeAssistant,
59+
eheimdigital_hub_mock: MagicMock,
60+
mock_config_entry: MockConfigEntry,
61+
classic_vario_mock: MagicMock,
62+
service: str,
63+
active: bool,
64+
) -> None:
65+
"""Test turning on/off the switch."""
66+
await init_integration(hass, mock_config_entry)
67+
68+
await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"](
69+
"00:00:00:00:00:03", EheimDeviceType.VERSION_EHEIM_CLASSIC_VARIO
70+
)
71+
await hass.async_block_till_done()
72+
73+
await hass.services.async_call(
74+
SWITCH_DOMAIN,
75+
service,
76+
{ATTR_ENTITY_ID: "switch.mock_classicvario"},
77+
blocking=True,
78+
)
79+
80+
classic_vario_mock.set_active.assert_awaited_once_with(active=active)
81+
82+
83+
async def test_state_update(
84+
hass: HomeAssistant,
85+
eheimdigital_hub_mock: MagicMock,
86+
mock_config_entry: MockConfigEntry,
87+
classic_vario_mock: MagicMock,
88+
) -> None:
89+
"""Test the switch state update."""
90+
await init_integration(hass, mock_config_entry)
91+
92+
await eheimdigital_hub_mock.call_args.kwargs["device_found_callback"](
93+
"00:00:00:00:00:03", EheimDeviceType.VERSION_EHEIM_CLASSIC_VARIO
94+
)
95+
await hass.async_block_till_done()
96+
97+
assert (state := hass.states.get("switch.mock_classicvario"))
98+
assert state.state == STATE_ON
99+
100+
classic_vario_mock.is_active = False
101+
102+
await eheimdigital_hub_mock.call_args.kwargs["receive_callback"]()
103+
104+
assert (state := hass.states.get("switch.mock_classicvario"))
105+
assert state.state == STATE_OFF

0 commit comments

Comments
 (0)