Skip to content

Commit 554c122

Browse files
authored
Add switch platform to PoolDose integration (home-assistant#157296)
1 parent 1c0dd02 commit 554c122

File tree

7 files changed

+438
-5
lines changed

7 files changed

+438
-5
lines changed

homeassistant/components/pooldose/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
_LOGGER = logging.getLogger(__name__)
1919

20-
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
20+
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH]
2121

2222

2323
async def async_migrate_entry(hass: HomeAssistant, entry: PooldoseConfigEntry) -> bool:

homeassistant/components/pooldose/icons.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,29 @@
120120
"ph_type_dosing": {
121121
"default": "mdi:beaker"
122122
}
123+
},
124+
"switch": {
125+
"frequency_input": {
126+
"default": "mdi:sine-wave",
127+
"state": {
128+
"off": "mdi:pulse",
129+
"on": "mdi:sine-wave"
130+
}
131+
},
132+
"pause_dosing": {
133+
"default": "mdi:pause",
134+
"state": {
135+
"off": "mdi:play",
136+
"on": "mdi:pause"
137+
}
138+
},
139+
"pump_monitoring": {
140+
"default": "mdi:pump",
141+
"state": {
142+
"off": "mdi:pump-off",
143+
"on": "mdi:pump"
144+
}
145+
}
123146
}
124147
}
125148
}

homeassistant/components/pooldose/strings.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@
161161
"alcalyne": "pH+"
162162
}
163163
}
164+
},
165+
"switch": {
166+
"frequency_input": {
167+
"name": "Frequency input"
168+
},
169+
"pause_dosing": {
170+
"name": "Pause dosing"
171+
},
172+
"pump_monitoring": {
173+
"name": "Pump monitoring"
174+
}
164175
}
165176
}
166177
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""Switches for the Seko PoolDose integration."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
from typing import TYPE_CHECKING, Any, cast
7+
8+
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
9+
from homeassistant.const import EntityCategory
10+
from homeassistant.core import HomeAssistant
11+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
12+
13+
from . import PooldoseConfigEntry
14+
from .entity import PooldoseEntity
15+
16+
if TYPE_CHECKING:
17+
from .coordinator import PooldoseCoordinator
18+
19+
_LOGGER = logging.getLogger(__name__)
20+
21+
22+
SWITCH_DESCRIPTIONS: tuple[SwitchEntityDescription, ...] = (
23+
SwitchEntityDescription(
24+
key="pause_dosing",
25+
translation_key="pause_dosing",
26+
entity_category=EntityCategory.CONFIG,
27+
),
28+
SwitchEntityDescription(
29+
key="pump_monitoring",
30+
translation_key="pump_monitoring",
31+
entity_category=EntityCategory.CONFIG,
32+
),
33+
SwitchEntityDescription(
34+
key="frequency_input",
35+
translation_key="frequency_input",
36+
entity_category=EntityCategory.CONFIG,
37+
),
38+
)
39+
40+
41+
async def async_setup_entry(
42+
hass: HomeAssistant,
43+
config_entry: PooldoseConfigEntry,
44+
async_add_entities: AddConfigEntryEntitiesCallback,
45+
) -> None:
46+
"""Set up PoolDose switch entities from a config entry."""
47+
if TYPE_CHECKING:
48+
assert config_entry.unique_id is not None
49+
50+
coordinator = config_entry.runtime_data
51+
switch_data = coordinator.data["switch"]
52+
serial_number = config_entry.unique_id
53+
54+
async_add_entities(
55+
PooldoseSwitch(coordinator, serial_number, coordinator.device_info, description)
56+
for description in SWITCH_DESCRIPTIONS
57+
if description.key in switch_data
58+
)
59+
60+
61+
class PooldoseSwitch(PooldoseEntity, SwitchEntity):
62+
"""Switch entity for the Seko PoolDose Python API."""
63+
64+
def __init__(
65+
self,
66+
coordinator: PooldoseCoordinator,
67+
serial_number: str,
68+
device_info: Any,
69+
description: SwitchEntityDescription,
70+
) -> None:
71+
"""Initialize the switch."""
72+
super().__init__(coordinator, serial_number, device_info, description, "switch")
73+
self._async_update_attrs()
74+
75+
def _handle_coordinator_update(self) -> None:
76+
"""Handle updated data from the coordinator."""
77+
self._async_update_attrs()
78+
super()._handle_coordinator_update()
79+
80+
def _async_update_attrs(self) -> None:
81+
"""Update switch attributes."""
82+
data = cast(dict, self.get_data())
83+
self._attr_is_on = cast(bool, data["value"])
84+
85+
async def async_turn_on(self, **kwargs: Any) -> None:
86+
"""Turn the switch on."""
87+
await self.coordinator.client.set_switch(self.entity_description.key, True)
88+
self._attr_is_on = True
89+
self.async_write_ha_state()
90+
91+
async def async_turn_off(self, **kwargs: Any) -> None:
92+
"""Turn the switch off."""
93+
await self.coordinator.client.set_switch(self.entity_description.key, False)
94+
self._attr_is_on = False
95+
self.async_write_ha_state()

tests/components/pooldose/conftest.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pytest
99

1010
from homeassistant.components.pooldose.const import DOMAIN
11-
from homeassistant.const import CONF_HOST
11+
from homeassistant.const import CONF_HOST, Platform
1212
from homeassistant.core import HomeAssistant
1313

1414
from tests.common import (
@@ -59,6 +59,7 @@ def mock_pooldose_client(device_info: dict[str, Any]) -> Generator[MagicMock]:
5959
instant_values_data,
6060
)
6161

62+
client.set_switch = AsyncMock(return_value=RequestStatus.SUCCESS)
6263
client.is_connected = True
6364
yield client
6465

@@ -88,11 +89,23 @@ def mock_config_entry_v1_1(device_info: dict[str, Any]) -> MockConfigEntry:
8889
)
8990

9091

92+
@pytest.fixture
93+
def platforms() -> list[Platform]:
94+
"""Fixture to specify platforms to test."""
95+
return []
96+
97+
98+
@pytest.fixture
9199
async def init_integration(
92-
hass: HomeAssistant, mock_config_entry: MockConfigEntry
100+
hass: HomeAssistant,
101+
mock_config_entry: MockConfigEntry,
102+
platforms: list[Platform],
93103
) -> MockConfigEntry:
94104
"""Set up the integration for testing."""
95105
mock_config_entry.add_to_hass(hass)
96-
await hass.config_entries.async_setup(mock_config_entry.entry_id)
97-
await hass.async_block_till_done()
106+
107+
with patch("homeassistant.components.pooldose.PLATFORMS", platforms):
108+
await hass.config_entries.async_setup(mock_config_entry.entry_id)
109+
await hass.async_block_till_done()
110+
98111
return mock_config_entry
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# serializer version: 1
2+
# name: test_all_switches[switch.pool_device_frequency_input-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': <EntityCategory.CONFIG: 'config'>,
15+
'entity_id': 'switch.pool_device_frequency_input',
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': 'Frequency input',
28+
'platform': 'pooldose',
29+
'previous_unique_id': None,
30+
'suggested_object_id': None,
31+
'supported_features': 0,
32+
'translation_key': 'frequency_input',
33+
'unique_id': 'TEST123456789_frequency_input',
34+
'unit_of_measurement': None,
35+
})
36+
# ---
37+
# name: test_all_switches[switch.pool_device_frequency_input-state]
38+
StateSnapshot({
39+
'attributes': ReadOnlyDict({
40+
'friendly_name': 'Pool Device Frequency input',
41+
}),
42+
'context': <ANY>,
43+
'entity_id': 'switch.pool_device_frequency_input',
44+
'last_changed': <ANY>,
45+
'last_reported': <ANY>,
46+
'last_updated': <ANY>,
47+
'state': 'off',
48+
})
49+
# ---
50+
# name: test_all_switches[switch.pool_device_pause_dosing-entry]
51+
EntityRegistryEntrySnapshot({
52+
'aliases': set({
53+
}),
54+
'area_id': None,
55+
'capabilities': None,
56+
'config_entry_id': <ANY>,
57+
'config_subentry_id': <ANY>,
58+
'device_class': None,
59+
'device_id': <ANY>,
60+
'disabled_by': None,
61+
'domain': 'switch',
62+
'entity_category': <EntityCategory.CONFIG: 'config'>,
63+
'entity_id': 'switch.pool_device_pause_dosing',
64+
'has_entity_name': True,
65+
'hidden_by': None,
66+
'icon': None,
67+
'id': <ANY>,
68+
'labels': set({
69+
}),
70+
'name': None,
71+
'options': dict({
72+
}),
73+
'original_device_class': None,
74+
'original_icon': None,
75+
'original_name': 'Pause dosing',
76+
'platform': 'pooldose',
77+
'previous_unique_id': None,
78+
'suggested_object_id': None,
79+
'supported_features': 0,
80+
'translation_key': 'pause_dosing',
81+
'unique_id': 'TEST123456789_pause_dosing',
82+
'unit_of_measurement': None,
83+
})
84+
# ---
85+
# name: test_all_switches[switch.pool_device_pause_dosing-state]
86+
StateSnapshot({
87+
'attributes': ReadOnlyDict({
88+
'friendly_name': 'Pool Device Pause dosing',
89+
}),
90+
'context': <ANY>,
91+
'entity_id': 'switch.pool_device_pause_dosing',
92+
'last_changed': <ANY>,
93+
'last_reported': <ANY>,
94+
'last_updated': <ANY>,
95+
'state': 'off',
96+
})
97+
# ---
98+
# name: test_all_switches[switch.pool_device_pump_monitoring-entry]
99+
EntityRegistryEntrySnapshot({
100+
'aliases': set({
101+
}),
102+
'area_id': None,
103+
'capabilities': None,
104+
'config_entry_id': <ANY>,
105+
'config_subentry_id': <ANY>,
106+
'device_class': None,
107+
'device_id': <ANY>,
108+
'disabled_by': None,
109+
'domain': 'switch',
110+
'entity_category': <EntityCategory.CONFIG: 'config'>,
111+
'entity_id': 'switch.pool_device_pump_monitoring',
112+
'has_entity_name': True,
113+
'hidden_by': None,
114+
'icon': None,
115+
'id': <ANY>,
116+
'labels': set({
117+
}),
118+
'name': None,
119+
'options': dict({
120+
}),
121+
'original_device_class': None,
122+
'original_icon': None,
123+
'original_name': 'Pump monitoring',
124+
'platform': 'pooldose',
125+
'previous_unique_id': None,
126+
'suggested_object_id': None,
127+
'supported_features': 0,
128+
'translation_key': 'pump_monitoring',
129+
'unique_id': 'TEST123456789_pump_monitoring',
130+
'unit_of_measurement': None,
131+
})
132+
# ---
133+
# name: test_all_switches[switch.pool_device_pump_monitoring-state]
134+
StateSnapshot({
135+
'attributes': ReadOnlyDict({
136+
'friendly_name': 'Pool Device Pump monitoring',
137+
}),
138+
'context': <ANY>,
139+
'entity_id': 'switch.pool_device_pump_monitoring',
140+
'last_changed': <ANY>,
141+
'last_reported': <ANY>,
142+
'last_updated': <ANY>,
143+
'state': 'on',
144+
})
145+
# ---

0 commit comments

Comments
 (0)