"""Support for WiLight switches.""" from __future__ import annotations from typing import Any from pywilight.const import ITEM_SWITCH, SWITCH_PAUSE_VALVE, SWITCH_VALVE from pywilight.wilight_device import PyWiLightDevice import voluptuous as vol from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import DOMAIN, WiLightDevice from .parent_device import WiLightParent from .support import wilight_to_hass_trigger, wilight_trigger as wl_trigger # Attr of features supported by the valve switch entities ATTR_WATERING_TIME = "watering_time" ATTR_PAUSE_TIME = "pause_time" ATTR_TRIGGER_1 = "trigger_1" ATTR_TRIGGER_2 = "trigger_2" ATTR_TRIGGER_3 = "trigger_3" ATTR_TRIGGER_4 = "trigger_4" ATTR_TRIGGER_1_DESC = "trigger_1_description" ATTR_TRIGGER_2_DESC = "trigger_2_description" ATTR_TRIGGER_3_DESC = "trigger_3_description" ATTR_TRIGGER_4_DESC = "trigger_4_description" # Attr of services data supported by the valve switch entities ATTR_TRIGGER = "trigger" ATTR_TRIGGER_INDEX = "trigger_index" # Service of features supported by the valve switch entities SERVICE_SET_WATERING_TIME = "set_watering_time" SERVICE_SET_PAUSE_TIME = "set_pause_time" SERVICE_SET_TRIGGER = "set_trigger" # Range of features supported by the valve switch entities RANGE_WATERING_TIME = 1800 RANGE_PAUSE_TIME = 24 RANGE_TRIGGER_INDEX = 4 # Service call validation schemas VALID_WATERING_TIME = vol.All( vol.Coerce(int), vol.Range(min=1, max=RANGE_WATERING_TIME) ) VALID_PAUSE_TIME = vol.All(vol.Coerce(int), vol.Range(min=1, max=RANGE_PAUSE_TIME)) VALID_TRIGGER_INDEX = vol.All( vol.Coerce(int), vol.Range(min=1, max=RANGE_TRIGGER_INDEX) ) # Descriptions of the valve switch entities DESC_WATERING = "watering" DESC_PAUSE = "pause" # Icons of the valve switch entities ICON_WATERING = "mdi:water" ICON_PAUSE = "mdi:pause-circle-outline" def entities_from_discovered_wilight(api_device: PyWiLightDevice) -> tuple[Any]: """Parse configuration and add WiLight switch entities.""" entities: Any = [] for item in api_device.items: if item["type"] == ITEM_SWITCH: index = item["index"] item_name = item["name"] if item["sub_type"] == SWITCH_VALVE: entities.append(WiLightValveSwitch(api_device, index, item_name)) elif item["sub_type"] == SWITCH_PAUSE_VALVE: entities.append(WiLightValvePauseSwitch(api_device, index, item_name)) return entities async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up WiLight switches from a config entry.""" parent: WiLightParent = hass.data[DOMAIN][entry.entry_id] # Handle a discovered WiLight device. assert parent.api entities = entities_from_discovered_wilight(parent.api) async_add_entities(entities) # Handle services for a discovered WiLight device. async def set_watering_time(entity, service: Any) -> None: if not isinstance(entity, WiLightValveSwitch): raise ValueError("Entity is not a WiLight valve switch") watering_time = service.data[ATTR_WATERING_TIME] await entity.async_set_watering_time(watering_time=watering_time) async def set_trigger(entity, service: Any) -> None: if not isinstance(entity, WiLightValveSwitch): raise ValueError("Entity is not a WiLight valve switch") trigger_index = service.data[ATTR_TRIGGER_INDEX] trigger = service.data[ATTR_TRIGGER] await entity.async_set_trigger(trigger_index=trigger_index, trigger=trigger) async def set_pause_time(entity, service: Any) -> None: if not isinstance(entity, WiLightValvePauseSwitch): raise ValueError("Entity is not a WiLight valve pause switch") pause_time = service.data[ATTR_PAUSE_TIME] await entity.async_set_pause_time(pause_time=pause_time) platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( SERVICE_SET_WATERING_TIME, { vol.Required(ATTR_WATERING_TIME): VALID_WATERING_TIME, }, set_watering_time, ) platform.async_register_entity_service( SERVICE_SET_TRIGGER, { vol.Required(ATTR_TRIGGER_INDEX): VALID_TRIGGER_INDEX, vol.Required(ATTR_TRIGGER): wl_trigger, }, set_trigger, ) platform.async_register_entity_service( SERVICE_SET_PAUSE_TIME, { vol.Required(ATTR_PAUSE_TIME): VALID_PAUSE_TIME, }, set_pause_time, ) def wilight_to_hass_pause_time(value: int) -> int: """Convert wilight pause_time seconds to hass hour.""" return round(value / 3600) def hass_to_wilight_pause_time(value: int) -> int: """Convert hass pause_time hours to wilight seconds.""" return round(value * 3600) class WiLightValveSwitch(WiLightDevice, SwitchEntity): """Representation of a WiLights Valve switch.""" @property def name(self) -> str: """Return the name of the switch.""" return f"{self._attr_name} {DESC_WATERING}" @property def is_on(self) -> bool: """Return true if device is on.""" return self._status.get("on", False) @property def watering_time(self) -> int | None: """Return watering time of valve switch. None is unknown, 1 is minimum, 1800 is maximum. """ return self._status.get("timer_target") @property def trigger_1(self) -> str | None: """Return trigger_1 of valve switch.""" return self._status.get("trigger_1") @property def trigger_2(self) -> str | None: """Return trigger_2 of valve switch.""" return self._status.get("trigger_2") @property def trigger_3(self) -> str | None: """Return trigger_3 of valve switch.""" return self._status.get("trigger_3") @property def trigger_4(self) -> str | None: """Return trigger_4 of valve switch.""" return self._status.get("trigger_4") @property def trigger_1_description(self) -> str | None: """Return trigger_1_description of valve switch.""" return wilight_to_hass_trigger(self._status.get("trigger_1")) @property def trigger_2_description(self) -> str | None: """Return trigger_2_description of valve switch.""" return wilight_to_hass_trigger(self._status.get("trigger_2")) @property def trigger_3_description(self) -> str | None: """Return trigger_3_description of valve switch.""" return wilight_to_hass_trigger(self._status.get("trigger_3")) @property def trigger_4_description(self) -> str | None: """Return trigger_4_description of valve switch.""" return wilight_to_hass_trigger(self._status.get("trigger_4")) @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes.""" attr: dict[str, Any] = {} if self.watering_time is not None: attr[ATTR_WATERING_TIME] = self.watering_time if self.trigger_1 is not None: attr[ATTR_TRIGGER_1] = self.trigger_1 if self.trigger_2 is not None: attr[ATTR_TRIGGER_2] = self.trigger_2 if self.trigger_3 is not None: attr[ATTR_TRIGGER_3] = self.trigger_3 if self.trigger_4 is not None: attr[ATTR_TRIGGER_4] = self.trigger_4 if self.trigger_1_description is not None: attr[ATTR_TRIGGER_1_DESC] = self.trigger_1_description if self.trigger_2_description is not None: attr[ATTR_TRIGGER_2_DESC] = self.trigger_2_description if self.trigger_3_description is not None: attr[ATTR_TRIGGER_3_DESC] = self.trigger_3_description if self.trigger_4_description is not None: attr[ATTR_TRIGGER_4_DESC] = self.trigger_4_description return attr @property def icon(self) -> str: """Return the icon to use in the frontend.""" return ICON_WATERING async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" await self._client.turn_on(self._index) async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" await self._client.turn_off(self._index) async def async_set_watering_time(self, watering_time: int) -> None: """Set the watering time.""" await self._client.set_switch_time(self._index, watering_time) async def async_set_trigger(self, trigger_index: int, trigger: str) -> None: """Set the trigger according to index.""" if trigger_index == 1: await self._client.set_switch_trigger_1(self._index, trigger) if trigger_index == 2: await self._client.set_switch_trigger_2(self._index, trigger) if trigger_index == 3: await self._client.set_switch_trigger_3(self._index, trigger) if trigger_index == 4: await self._client.set_switch_trigger_4(self._index, trigger) class WiLightValvePauseSwitch(WiLightDevice, SwitchEntity): """Representation of a WiLights Valve Pause switch.""" @property def name(self) -> str: """Return the name of the switch.""" return f"{self._attr_name} {DESC_PAUSE}" @property def is_on(self) -> bool: """Return true if device is on.""" return self._status.get("on", False) @property def pause_time(self) -> int | None: """Return pause time of valve switch. None is unknown, 1 is minimum, 24 is maximum. """ pause_time = self._status.get("timer_target") if pause_time is not None: return wilight_to_hass_pause_time(pause_time) return pause_time @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes.""" attr: dict[str, Any] = {} if self.pause_time is not None: attr[ATTR_PAUSE_TIME] = self.pause_time return attr @property def icon(self) -> str: """Return the icon to use in the frontend.""" return ICON_PAUSE async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" await self._client.turn_on(self._index) async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" await self._client.turn_off(self._index) async def async_set_pause_time(self, pause_time: int) -> None: """Set the pause time.""" target_time = hass_to_wilight_pause_time(pause_time) await self._client.set_switch_time(self._index, target_time)