"""YoLink select platform.""" from __future__ import annotations from collections.abc import Awaitable, Callable from dataclasses import dataclass from typing import Any from yolink.client_request import ClientRequest from yolink.const import ATTR_DEVICE_SPRINKLER from yolink.device import YoLinkDevice from yolink.message_resolver import sprinkler_message_resolve from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import DOMAIN from .coordinator import YoLinkCoordinator from .entity import YoLinkEntity @dataclass(frozen=True, kw_only=True) class YoLinkSelectEntityDescription(SelectEntityDescription): """YoLink SelectEntityDescription.""" state_key: str = "state" exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True should_update_entity: Callable = lambda state: True value: Callable = lambda data: data on_option_selected: Callable[[YoLinkCoordinator, str], Awaitable[bool]] async def set_sprinker_mode_fn(coordinator: YoLinkCoordinator, option: str) -> bool: """Set sprinkler mode.""" data: dict[str, Any] = await coordinator.call_device( ClientRequest( "setState", { "state": { "mode": option, } }, ) ) sprinkler_message_resolve(coordinator.device, data, None) coordinator.async_set_updated_data(data) return True SELECTOR_MAPPINGS: tuple[YoLinkSelectEntityDescription, ...] = ( YoLinkSelectEntityDescription( key="model", options=["auto", "manual", "off"], translation_key="sprinkler_mode", value=lambda data: ( data.get("mode") if data is not None else None ), # watering state report will missing state field exists_fn=lambda device: device.device_type == ATTR_DEVICE_SPRINKLER, should_update_entity=lambda value: value is not None, on_option_selected=set_sprinker_mode_fn, ), ) async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up YoLink select from a config entry.""" device_coordinators = hass.data[DOMAIN][config_entry.entry_id].device_coordinators async_add_entities( YoLinkSelectEntity(config_entry, selector_device_coordinator, description) for selector_device_coordinator in device_coordinators.values() if selector_device_coordinator.device.device_type in [ATTR_DEVICE_SPRINKLER] for description in SELECTOR_MAPPINGS if description.exists_fn(selector_device_coordinator.device) ) class YoLinkSelectEntity(YoLinkEntity, SelectEntity): """YoLink Select Entity.""" entity_description: YoLinkSelectEntityDescription def __init__( self, config_entry: ConfigEntry, coordinator: YoLinkCoordinator, description: YoLinkSelectEntityDescription, ) -> None: """Init YoLink Select.""" super().__init__(config_entry, coordinator) self.entity_description = description self._attr_unique_id = ( f"{coordinator.device.device_id} {self.entity_description.key}" ) @callback def update_entity_state(self, state: dict[str, Any]) -> None: """Update HA Entity State.""" if ( current_value := self.entity_description.value( state.get(self.entity_description.state_key) ) ) is None and self.entity_description.should_update_entity( current_value ) is False: return self._attr_current_option = current_value self.async_write_ha_state() async def async_select_option(self, option: str) -> None: """Change the selected option.""" if await self.entity_description.on_option_selected(self.coordinator, option): self._attr_current_option = option self.async_write_ha_state()