Skip to content

Commit 2125a41

Browse files
authored
Add zones support to Shelly Irrigation controller (home-assistant#152382)
1 parent 27516de commit 2125a41

File tree

4 files changed

+133
-6
lines changed

4 files changed

+133
-6
lines changed

homeassistant/components/shelly/switch.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription):
165165
"boolean_zone0": RpcSwitchDescription(
166166
key="boolean",
167167
sub_key="value",
168+
entity_registry_enabled_default=False,
168169
is_on=lambda status: bool(status["value"]),
169170
method_on="boolean_set",
170171
method_off="boolean_set",
@@ -175,6 +176,7 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription):
175176
"boolean_zone1": RpcSwitchDescription(
176177
key="boolean",
177178
sub_key="value",
179+
entity_registry_enabled_default=False,
178180
is_on=lambda status: bool(status["value"]),
179181
method_on="boolean_set",
180182
method_off="boolean_set",
@@ -185,6 +187,7 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription):
185187
"boolean_zone2": RpcSwitchDescription(
186188
key="boolean",
187189
sub_key="value",
190+
entity_registry_enabled_default=False,
188191
is_on=lambda status: bool(status["value"]),
189192
method_on="boolean_set",
190193
method_off="boolean_set",
@@ -195,6 +198,7 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription):
195198
"boolean_zone3": RpcSwitchDescription(
196199
key="boolean",
197200
sub_key="value",
201+
entity_registry_enabled_default=False,
198202
is_on=lambda status: bool(status["value"]),
199203
method_on="boolean_set",
200204
method_off="boolean_set",
@@ -205,6 +209,7 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription):
205209
"boolean_zone4": RpcSwitchDescription(
206210
key="boolean",
207211
sub_key="value",
212+
entity_registry_enabled_default=False,
208213
is_on=lambda status: bool(status["value"]),
209214
method_on="boolean_set",
210215
method_off="boolean_set",
@@ -215,6 +220,7 @@ class RpcSwitchDescription(RpcEntityDescription, SwitchEntityDescription):
215220
"boolean_zone5": RpcSwitchDescription(
216221
key="boolean",
217222
sub_key="value",
223+
entity_registry_enabled_default=False,
218224
is_on=lambda status: bool(status["value"]),
219225
method_on="boolean_set",
220226
method_off="boolean_set",

homeassistant/components/shelly/utils.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,11 @@ def get_rpc_sub_device_name(
417417
"""Get name based on device and channel name."""
418418
if key in device.config and key != "em:0":
419419
# workaround for Pro 3EM, we don't want to get name for em:0
420+
if (zone_id := get_irrigation_zone_id(device.config, key)) is not None:
421+
# workaround for Irrigation controller, name stored in "service:0"
422+
if zone_name := device.config["service:0"]["zones"][zone_id]["name"]:
423+
return cast(str, zone_name)
424+
420425
if entity_name := device.config[key].get("name"):
421426
return cast(str, entity_name)
422427

@@ -787,6 +792,13 @@ async def get_rpc_scripts_event_types(
787792
return script_events
788793

789794

795+
def get_irrigation_zone_id(config: dict[str, Any], key: str) -> int | None:
796+
"""Return the zone id if the component is an irrigation zone."""
797+
if key in config and (zone := get_rpc_role_by_key(config, key)).startswith("zone"):
798+
return int(zone[4:])
799+
return None
800+
801+
790802
def get_rpc_device_info(
791803
device: RpcDevice,
792804
mac: str,
@@ -823,7 +835,10 @@ def get_rpc_device_info(
823835
)
824836

825837
if (
826-
component not in (*All_LIGHT_TYPES, "cover", "em1", "switch")
838+
(
839+
component not in (*All_LIGHT_TYPES, "cover", "em1", "switch")
840+
and get_irrigation_zone_id(device.config, key) is None
841+
)
827842
or idx is None
828843
or len(get_rpc_key_instances(device.status, component, all_lights=True)) < 2
829844
):

homeassistant/components/shelly/valve.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
from homeassistant.core import HomeAssistant, callback
1818
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1919

20-
from .const import MODEL_FRANKEVER_WATER_VALVE, MODEL_NEO_WATER_VALVE
20+
from .const import (
21+
MODEL_FRANKEVER_IRRIGATION_CONTROLLER,
22+
MODEL_FRANKEVER_WATER_VALVE,
23+
MODEL_NEO_WATER_VALVE,
24+
)
2125
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
2226
from .entity import (
2327
BlockEntityDescription,
@@ -92,8 +96,8 @@ async def async_set_valve_position(self, position: int) -> None:
9296
await self.coordinator.device.number_set(self._id, position)
9397

9498

95-
class RpcShellyNeoWaterValve(RpcShellyBaseWaterValve):
96-
"""Entity that controls a valve on RPC Shelly NEO Water Valve."""
99+
class RpcShellySimpleWaterValve(RpcShellyBaseWaterValve):
100+
"""Entity that controls a valve on RPC Shelly Open/Close Water Valve."""
97101

98102
_attr_supported_features = ValveEntityFeature.OPEN | ValveEntityFeature.CLOSE
99103
_attr_reports_position = False
@@ -124,9 +128,51 @@ async def async_close_valve(self, **kwargs: Any) -> None:
124128
key="boolean",
125129
sub_key="value",
126130
role="state",
127-
entity_class=RpcShellyNeoWaterValve,
131+
entity_class=RpcShellySimpleWaterValve,
128132
models={MODEL_NEO_WATER_VALVE},
129133
),
134+
"boolean_zone0": RpcValveDescription(
135+
key="boolean",
136+
sub_key="value",
137+
role="zone0",
138+
entity_class=RpcShellySimpleWaterValve,
139+
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
140+
),
141+
"boolean_zone1": RpcValveDescription(
142+
key="boolean",
143+
sub_key="value",
144+
role="zone1",
145+
entity_class=RpcShellySimpleWaterValve,
146+
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
147+
),
148+
"boolean_zone2": RpcValveDescription(
149+
key="boolean",
150+
sub_key="value",
151+
role="zone2",
152+
entity_class=RpcShellySimpleWaterValve,
153+
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
154+
),
155+
"boolean_zone3": RpcValveDescription(
156+
key="boolean",
157+
sub_key="value",
158+
role="zone3",
159+
entity_class=RpcShellySimpleWaterValve,
160+
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
161+
),
162+
"boolean_zone4": RpcValveDescription(
163+
key="boolean",
164+
sub_key="value",
165+
role="zone4",
166+
entity_class=RpcShellySimpleWaterValve,
167+
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
168+
),
169+
"boolean_zone5": RpcValveDescription(
170+
key="boolean",
171+
sub_key="value",
172+
role="zone5",
173+
entity_class=RpcShellySimpleWaterValve,
174+
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
175+
),
130176
}
131177

132178

tests/components/shelly/test_devices.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
import pytest
1313
from syrupy.assertion import SnapshotAssertion
1414

15-
from homeassistant.components.shelly.const import DOMAIN
15+
from homeassistant.components.shelly.const import (
16+
DOMAIN,
17+
MODEL_FRANKEVER_IRRIGATION_CONTROLLER,
18+
)
1619
from homeassistant.core import HomeAssistant
1720
from homeassistant.helpers.device_registry import DeviceRegistry
1821
from homeassistant.helpers.entity_registry import EntityRegistry
@@ -475,6 +478,63 @@ async def test_shelly_pro_3em_with_emeter_name(
475478
assert device_entry.name == "Test name Phase C"
476479

477480

481+
async def test_shelly_fk_06x_with_zone_names(
482+
hass: HomeAssistant,
483+
mock_rpc_device: Mock,
484+
entity_registry: EntityRegistry,
485+
device_registry: DeviceRegistry,
486+
monkeypatch: pytest.MonkeyPatch,
487+
) -> None:
488+
"""Test Shelly Irrigation controller FK-06X with zone names.
489+
490+
We should get the main device and 6 subdevices, one subdevice per one zone.
491+
"""
492+
device_fixture = await async_load_json_object_fixture(
493+
hass, "fk-06x_gen3_irrigation.json", DOMAIN
494+
)
495+
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
496+
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
497+
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
498+
499+
await init_integration(hass, gen=3, model=MODEL_FRANKEVER_IRRIGATION_CONTROLLER)
500+
501+
# Main device
502+
entity_id = "sensor.test_name_average_temperature"
503+
504+
state = hass.states.get(entity_id)
505+
assert state
506+
507+
entry = entity_registry.async_get(entity_id)
508+
assert entry
509+
510+
device_entry = device_registry.async_get(entry.device_id)
511+
assert device_entry
512+
assert device_entry.name == "Test name"
513+
514+
# 3 zones with names, 3 with default names
515+
zone_names = [
516+
"Zone Name 1",
517+
"Zone Name 2",
518+
"Zone Name 3",
519+
"Zone 4",
520+
"Zone 5",
521+
"Zone 6",
522+
]
523+
524+
for zone_name in zone_names:
525+
entity_id = f"valve.{zone_name.lower().replace(' ', '_')}"
526+
527+
state = hass.states.get(entity_id)
528+
assert state
529+
530+
entry = entity_registry.async_get(entity_id)
531+
assert entry
532+
533+
device_entry = device_registry.async_get(entry.device_id)
534+
assert device_entry
535+
assert device_entry.name == zone_name
536+
537+
478538
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
479539
async def test_block_channel_with_name(
480540
hass: HomeAssistant,

0 commit comments

Comments
 (0)