mirror of
https://github.com/home-assistant/core.git
synced 2025-11-08 02:19:31 +00:00
Add zones support to Shelly Irrigation controller (#152382)
This commit is contained in:
@@ -165,6 +165,7 @@ RPC_SWITCHES = {
|
||||
"boolean_zone0": RpcSwitchDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
entity_registry_enabled_default=False,
|
||||
is_on=lambda status: bool(status["value"]),
|
||||
method_on="boolean_set",
|
||||
method_off="boolean_set",
|
||||
@@ -175,6 +176,7 @@ RPC_SWITCHES = {
|
||||
"boolean_zone1": RpcSwitchDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
entity_registry_enabled_default=False,
|
||||
is_on=lambda status: bool(status["value"]),
|
||||
method_on="boolean_set",
|
||||
method_off="boolean_set",
|
||||
@@ -185,6 +187,7 @@ RPC_SWITCHES = {
|
||||
"boolean_zone2": RpcSwitchDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
entity_registry_enabled_default=False,
|
||||
is_on=lambda status: bool(status["value"]),
|
||||
method_on="boolean_set",
|
||||
method_off="boolean_set",
|
||||
@@ -195,6 +198,7 @@ RPC_SWITCHES = {
|
||||
"boolean_zone3": RpcSwitchDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
entity_registry_enabled_default=False,
|
||||
is_on=lambda status: bool(status["value"]),
|
||||
method_on="boolean_set",
|
||||
method_off="boolean_set",
|
||||
@@ -205,6 +209,7 @@ RPC_SWITCHES = {
|
||||
"boolean_zone4": RpcSwitchDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
entity_registry_enabled_default=False,
|
||||
is_on=lambda status: bool(status["value"]),
|
||||
method_on="boolean_set",
|
||||
method_off="boolean_set",
|
||||
@@ -215,6 +220,7 @@ RPC_SWITCHES = {
|
||||
"boolean_zone5": RpcSwitchDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
entity_registry_enabled_default=False,
|
||||
is_on=lambda status: bool(status["value"]),
|
||||
method_on="boolean_set",
|
||||
method_off="boolean_set",
|
||||
|
||||
@@ -417,6 +417,11 @@ def get_rpc_sub_device_name(
|
||||
"""Get name based on device and channel name."""
|
||||
if key in device.config and key != "em:0":
|
||||
# workaround for Pro 3EM, we don't want to get name for em:0
|
||||
if (zone_id := get_irrigation_zone_id(device.config, key)) is not None:
|
||||
# workaround for Irrigation controller, name stored in "service:0"
|
||||
if zone_name := device.config["service:0"]["zones"][zone_id]["name"]:
|
||||
return cast(str, zone_name)
|
||||
|
||||
if entity_name := device.config[key].get("name"):
|
||||
return cast(str, entity_name)
|
||||
|
||||
@@ -787,6 +792,13 @@ async def get_rpc_scripts_event_types(
|
||||
return script_events
|
||||
|
||||
|
||||
def get_irrigation_zone_id(config: dict[str, Any], key: str) -> int | None:
|
||||
"""Return the zone id if the component is an irrigation zone."""
|
||||
if key in config and (zone := get_rpc_role_by_key(config, key)).startswith("zone"):
|
||||
return int(zone[4:])
|
||||
return None
|
||||
|
||||
|
||||
def get_rpc_device_info(
|
||||
device: RpcDevice,
|
||||
mac: str,
|
||||
@@ -823,7 +835,10 @@ def get_rpc_device_info(
|
||||
)
|
||||
|
||||
if (
|
||||
component not in (*All_LIGHT_TYPES, "cover", "em1", "switch")
|
||||
(
|
||||
component not in (*All_LIGHT_TYPES, "cover", "em1", "switch")
|
||||
and get_irrigation_zone_id(device.config, key) is None
|
||||
)
|
||||
or idx is None
|
||||
or len(get_rpc_key_instances(device.status, component, all_lights=True)) < 2
|
||||
):
|
||||
|
||||
@@ -17,7 +17,11 @@ from homeassistant.components.valve import (
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import MODEL_FRANKEVER_WATER_VALVE, MODEL_NEO_WATER_VALVE
|
||||
from .const import (
|
||||
MODEL_FRANKEVER_IRRIGATION_CONTROLLER,
|
||||
MODEL_FRANKEVER_WATER_VALVE,
|
||||
MODEL_NEO_WATER_VALVE,
|
||||
)
|
||||
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
|
||||
from .entity import (
|
||||
BlockEntityDescription,
|
||||
@@ -92,8 +96,8 @@ class RpcShellyWaterValve(RpcShellyBaseWaterValve):
|
||||
await self.coordinator.device.number_set(self._id, position)
|
||||
|
||||
|
||||
class RpcShellyNeoWaterValve(RpcShellyBaseWaterValve):
|
||||
"""Entity that controls a valve on RPC Shelly NEO Water Valve."""
|
||||
class RpcShellySimpleWaterValve(RpcShellyBaseWaterValve):
|
||||
"""Entity that controls a valve on RPC Shelly Open/Close Water Valve."""
|
||||
|
||||
_attr_supported_features = ValveEntityFeature.OPEN | ValveEntityFeature.CLOSE
|
||||
_attr_reports_position = False
|
||||
@@ -124,9 +128,51 @@ RPC_VALVES: dict[str, RpcValveDescription] = {
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
role="state",
|
||||
entity_class=RpcShellyNeoWaterValve,
|
||||
entity_class=RpcShellySimpleWaterValve,
|
||||
models={MODEL_NEO_WATER_VALVE},
|
||||
),
|
||||
"boolean_zone0": RpcValveDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
role="zone0",
|
||||
entity_class=RpcShellySimpleWaterValve,
|
||||
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
|
||||
),
|
||||
"boolean_zone1": RpcValveDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
role="zone1",
|
||||
entity_class=RpcShellySimpleWaterValve,
|
||||
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
|
||||
),
|
||||
"boolean_zone2": RpcValveDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
role="zone2",
|
||||
entity_class=RpcShellySimpleWaterValve,
|
||||
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
|
||||
),
|
||||
"boolean_zone3": RpcValveDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
role="zone3",
|
||||
entity_class=RpcShellySimpleWaterValve,
|
||||
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
|
||||
),
|
||||
"boolean_zone4": RpcValveDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
role="zone4",
|
||||
entity_class=RpcShellySimpleWaterValve,
|
||||
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
|
||||
),
|
||||
"boolean_zone5": RpcValveDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
role="zone5",
|
||||
entity_class=RpcShellySimpleWaterValve,
|
||||
models={MODEL_FRANKEVER_IRRIGATION_CONTROLLER},
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.shelly.const import DOMAIN
|
||||
from homeassistant.components.shelly.const import (
|
||||
DOMAIN,
|
||||
MODEL_FRANKEVER_IRRIGATION_CONTROLLER,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceRegistry
|
||||
from homeassistant.helpers.entity_registry import EntityRegistry
|
||||
@@ -475,6 +478,63 @@ async def test_shelly_pro_3em_with_emeter_name(
|
||||
assert device_entry.name == "Test name Phase C"
|
||||
|
||||
|
||||
async def test_shelly_fk_06x_with_zone_names(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Shelly Irrigation controller FK-06X with zone names.
|
||||
|
||||
We should get the main device and 6 subdevices, one subdevice per one zone.
|
||||
"""
|
||||
device_fixture = await async_load_json_object_fixture(
|
||||
hass, "fk-06x_gen3_irrigation.json", DOMAIN
|
||||
)
|
||||
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
|
||||
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
|
||||
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
|
||||
|
||||
await init_integration(hass, gen=3, model=MODEL_FRANKEVER_IRRIGATION_CONTROLLER)
|
||||
|
||||
# Main device
|
||||
entity_id = "sensor.test_name_average_temperature"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
# 3 zones with names, 3 with default names
|
||||
zone_names = [
|
||||
"Zone Name 1",
|
||||
"Zone Name 2",
|
||||
"Zone Name 3",
|
||||
"Zone 4",
|
||||
"Zone 5",
|
||||
"Zone 6",
|
||||
]
|
||||
|
||||
for zone_name in zone_names:
|
||||
entity_id = f"valve.{zone_name.lower().replace(' ', '_')}"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == zone_name
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_block_channel_with_name(
|
||||
hass: HomeAssistant,
|
||||
|
||||
Reference in New Issue
Block a user