mirror of
https://github.com/home-assistant/core.git
synced 2025-11-10 11:29:46 +00:00
Add zones support to Shelly Irrigation controller (#152382)
This commit is contained in:
@@ -165,6 +165,7 @@ RPC_SWITCHES = {
|
|||||||
"boolean_zone0": RpcSwitchDescription(
|
"boolean_zone0": RpcSwitchDescription(
|
||||||
key="boolean",
|
key="boolean",
|
||||||
sub_key="value",
|
sub_key="value",
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
is_on=lambda status: bool(status["value"]),
|
is_on=lambda status: bool(status["value"]),
|
||||||
method_on="boolean_set",
|
method_on="boolean_set",
|
||||||
method_off="boolean_set",
|
method_off="boolean_set",
|
||||||
@@ -175,6 +176,7 @@ RPC_SWITCHES = {
|
|||||||
"boolean_zone1": RpcSwitchDescription(
|
"boolean_zone1": RpcSwitchDescription(
|
||||||
key="boolean",
|
key="boolean",
|
||||||
sub_key="value",
|
sub_key="value",
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
is_on=lambda status: bool(status["value"]),
|
is_on=lambda status: bool(status["value"]),
|
||||||
method_on="boolean_set",
|
method_on="boolean_set",
|
||||||
method_off="boolean_set",
|
method_off="boolean_set",
|
||||||
@@ -185,6 +187,7 @@ RPC_SWITCHES = {
|
|||||||
"boolean_zone2": RpcSwitchDescription(
|
"boolean_zone2": RpcSwitchDescription(
|
||||||
key="boolean",
|
key="boolean",
|
||||||
sub_key="value",
|
sub_key="value",
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
is_on=lambda status: bool(status["value"]),
|
is_on=lambda status: bool(status["value"]),
|
||||||
method_on="boolean_set",
|
method_on="boolean_set",
|
||||||
method_off="boolean_set",
|
method_off="boolean_set",
|
||||||
@@ -195,6 +198,7 @@ RPC_SWITCHES = {
|
|||||||
"boolean_zone3": RpcSwitchDescription(
|
"boolean_zone3": RpcSwitchDescription(
|
||||||
key="boolean",
|
key="boolean",
|
||||||
sub_key="value",
|
sub_key="value",
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
is_on=lambda status: bool(status["value"]),
|
is_on=lambda status: bool(status["value"]),
|
||||||
method_on="boolean_set",
|
method_on="boolean_set",
|
||||||
method_off="boolean_set",
|
method_off="boolean_set",
|
||||||
@@ -205,6 +209,7 @@ RPC_SWITCHES = {
|
|||||||
"boolean_zone4": RpcSwitchDescription(
|
"boolean_zone4": RpcSwitchDescription(
|
||||||
key="boolean",
|
key="boolean",
|
||||||
sub_key="value",
|
sub_key="value",
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
is_on=lambda status: bool(status["value"]),
|
is_on=lambda status: bool(status["value"]),
|
||||||
method_on="boolean_set",
|
method_on="boolean_set",
|
||||||
method_off="boolean_set",
|
method_off="boolean_set",
|
||||||
@@ -215,6 +220,7 @@ RPC_SWITCHES = {
|
|||||||
"boolean_zone5": RpcSwitchDescription(
|
"boolean_zone5": RpcSwitchDescription(
|
||||||
key="boolean",
|
key="boolean",
|
||||||
sub_key="value",
|
sub_key="value",
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
is_on=lambda status: bool(status["value"]),
|
is_on=lambda status: bool(status["value"]),
|
||||||
method_on="boolean_set",
|
method_on="boolean_set",
|
||||||
method_off="boolean_set",
|
method_off="boolean_set",
|
||||||
|
|||||||
@@ -417,6 +417,11 @@ def get_rpc_sub_device_name(
|
|||||||
"""Get name based on device and channel name."""
|
"""Get name based on device and channel name."""
|
||||||
if key in device.config and key != "em:0":
|
if key in device.config and key != "em:0":
|
||||||
# workaround for Pro 3EM, we don't want to get name for 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"):
|
if entity_name := device.config[key].get("name"):
|
||||||
return cast(str, entity_name)
|
return cast(str, entity_name)
|
||||||
|
|
||||||
@@ -787,6 +792,13 @@ async def get_rpc_scripts_event_types(
|
|||||||
return script_events
|
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(
|
def get_rpc_device_info(
|
||||||
device: RpcDevice,
|
device: RpcDevice,
|
||||||
mac: str,
|
mac: str,
|
||||||
@@ -823,7 +835,10 @@ def get_rpc_device_info(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (
|
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 idx is None
|
||||||
or len(get_rpc_key_instances(device.status, component, all_lights=True)) < 2
|
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.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
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 .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
|
||||||
from .entity import (
|
from .entity import (
|
||||||
BlockEntityDescription,
|
BlockEntityDescription,
|
||||||
@@ -92,8 +96,8 @@ class RpcShellyWaterValve(RpcShellyBaseWaterValve):
|
|||||||
await self.coordinator.device.number_set(self._id, position)
|
await self.coordinator.device.number_set(self._id, position)
|
||||||
|
|
||||||
|
|
||||||
class RpcShellyNeoWaterValve(RpcShellyBaseWaterValve):
|
class RpcShellySimpleWaterValve(RpcShellyBaseWaterValve):
|
||||||
"""Entity that controls a valve on RPC Shelly NEO Water Valve."""
|
"""Entity that controls a valve on RPC Shelly Open/Close Water Valve."""
|
||||||
|
|
||||||
_attr_supported_features = ValveEntityFeature.OPEN | ValveEntityFeature.CLOSE
|
_attr_supported_features = ValveEntityFeature.OPEN | ValveEntityFeature.CLOSE
|
||||||
_attr_reports_position = False
|
_attr_reports_position = False
|
||||||
@@ -124,9 +128,51 @@ RPC_VALVES: dict[str, RpcValveDescription] = {
|
|||||||
key="boolean",
|
key="boolean",
|
||||||
sub_key="value",
|
sub_key="value",
|
||||||
role="state",
|
role="state",
|
||||||
entity_class=RpcShellyNeoWaterValve,
|
entity_class=RpcShellySimpleWaterValve,
|
||||||
models={MODEL_NEO_WATER_VALVE},
|
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
|
import pytest
|
||||||
from syrupy.assertion import SnapshotAssertion
|
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.core import HomeAssistant
|
||||||
from homeassistant.helpers.device_registry import DeviceRegistry
|
from homeassistant.helpers.device_registry import DeviceRegistry
|
||||||
from homeassistant.helpers.entity_registry import EntityRegistry
|
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"
|
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")
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
async def test_block_channel_with_name(
|
async def test_block_channel_with_name(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|||||||
Reference in New Issue
Block a user