"""Whole-house, zone and climate-group thermostat selectors for GREE Controller.""" from __future__ import annotations from typing import Any from homeassistant.components.select import SelectEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import GreeControllerRuntimeData from .const import DOMAIN from .coordinator import GreeControllerCoordinator HOUSE_MODE_LABELS = { "off": "Do not control", "cool": "Cooling", "heat": "Heating", } HOUSE_MODE_VALUES = {label: value for value, label in HOUSE_MODE_LABELS.items()} HOUSE_PRESET_LABELS = { "auto": "Auto schedule", "comfort": "Comfort", "sleep": "Sleep", "away": "Away", } HOUSE_PRESET_VALUES = {label: value for value, label in HOUSE_PRESET_LABELS.items()} GROUP_MODE_LABELS = { "house": "Global", "cool": "Cooling", "heat": "Heating", } GROUP_MODE_VALUES = {label: value for value, label in GROUP_MODE_LABELS.items()} GROUP_PRESET_LABELS = HOUSE_PRESET_LABELS GROUP_PRESET_VALUES = HOUSE_PRESET_VALUES async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Create whole-house, zone and climate-group thermostat selectors.""" runtime: GreeControllerRuntimeData = entry.runtime_data entities: list[SelectEntity] = [ GreeControllerHouseModeSelect(runtime.coordinator), GreeControllerHousePresetSelect(runtime.coordinator), ] for zone in runtime.coordinator.plan.get("zones", []): zone_id = str(zone.get("zone_id") or "").strip() if zone_id: entities.append(GreeControllerZonePresetSelect(runtime.coordinator, zone_id)) for group_id in runtime.coordinator.groups: entities.append(GreeControllerGroupModeSelect(runtime.coordinator, group_id)) entities.append(GreeControllerGroupPresetSelect(runtime.coordinator, group_id)) async_add_entities(entities) class _GreeControllerHouseSelect(CoordinatorEntity[GreeControllerCoordinator], SelectEntity): """Base for controller-level selectors.""" _attr_has_entity_name = True @property def device_info(self) -> DeviceInfo: return DeviceInfo( identifiers={(DOMAIN, "controller")}, name="GREE Controller", manufacturer="GREE Controller", model="Local controller", ) class GreeControllerHouseModeSelect(_GreeControllerHouseSelect): """Select the smart thermostat house mode.""" _attr_name = "Thermostat mode" _attr_unique_id = "house-thermostat-mode" _attr_options = list(HOUSE_MODE_VALUES) @property def current_option(self) -> str | None: value = str(self.coordinator.plan.get("house_mode") or "") return HOUSE_MODE_LABELS.get(value) async def async_select_option(self, option: str) -> None: value = HOUSE_MODE_VALUES.get(option) if value is None: return await self.coordinator.client.house_control(value) await self.coordinator.async_request_refresh() class GreeControllerHousePresetSelect(_GreeControllerHouseSelect): """Select the whole-house thermostat work profile.""" _attr_name = "Work profile" _attr_unique_id = "house-work-profile" _attr_options = list(HOUSE_PRESET_VALUES) @property def current_option(self) -> str | None: value = self.coordinator.plan.get("house_preset") return HOUSE_PRESET_LABELS.get(str(value)) if value is not None else None @property def extra_state_attributes(self) -> dict[str, Any]: return {"mixed_zone_profiles": self.coordinator.plan.get("house_preset") is None} async def async_select_option(self, option: str) -> None: value = HOUSE_PRESET_VALUES.get(option) if value is None: return await self.coordinator.client.house_preset(value) await self.coordinator.async_request_refresh() class GreeControllerZonePresetSelect(CoordinatorEntity[GreeControllerCoordinator], SelectEntity): """Select the temporary work profile for one thermostat zone.""" _attr_has_entity_name = True _attr_name = "Work profile" _attr_options = list(HOUSE_PRESET_VALUES) def __init__(self, coordinator: GreeControllerCoordinator, zone_id: str) -> None: super().__init__(coordinator) self._zone_id = zone_id self._attr_unique_id = f"{zone_id}-work-profile" @property def _zone(self) -> dict[str, Any]: for zone in self.coordinator.plan.get("zones", []): if str(zone.get("zone_id")) == self._zone_id: return zone return {} @property def available(self) -> bool: return super().available and bool(self._zone) @property def current_option(self) -> str | None: value = str(self._zone.get("preset_override") or "auto") return HOUSE_PRESET_LABELS.get(value) @property def device_info(self) -> DeviceInfo: zone = self._zone return DeviceInfo( identifiers={(DOMAIN, f"zone:{self._zone_id}")}, name=str(zone.get("zone_name") or self._zone_id), manufacturer="GREE Controller", model="Zone thermostat", via_device=(DOMAIN, "controller"), ) @property def extra_state_attributes(self) -> dict[str, Any]: return {"active_profile": self._zone.get("preset")} async def async_select_option(self, option: str) -> None: value = HOUSE_PRESET_VALUES.get(option) if value is None: return await self.coordinator.async_zone_control(self._zone_id, {"preset": value}) class _GreeControllerGroupSelect(CoordinatorEntity[GreeControllerCoordinator], SelectEntity): """Base class for climate-group selectors.""" _attr_has_entity_name = True def __init__(self, coordinator: GreeControllerCoordinator, group_id: str) -> None: super().__init__(coordinator) self._group_id = group_id @property def _group(self) -> dict[str, Any]: return self.coordinator.groups.get(self._group_id, {}) @property def available(self) -> bool: return super().available and bool(self._group) @property def device_info(self) -> DeviceInfo: group = self._group return DeviceInfo( identifiers={(DOMAIN, f"group:{self._group_id}")}, name=str(group.get("name") or self._group_id), manufacturer="GREE Controller", model="Climate group", via_device=(DOMAIN, "controller"), ) class GreeControllerGroupModeSelect(_GreeControllerGroupSelect): """Select the thermostat mode policy for one climate group.""" _attr_name = "Thermostat mode" _attr_options = list(GROUP_MODE_VALUES) def __init__(self, coordinator: GreeControllerCoordinator, group_id: str) -> None: super().__init__(coordinator, group_id) self._attr_unique_id = f"{group_id}-group-thermostat-mode" @property def current_option(self) -> str | None: return GROUP_MODE_LABELS.get(str(self._group.get("mode") or "")) @property def extra_state_attributes(self) -> dict[str, Any]: return { "mixed_group_mode": self._group.get("mode") == "mixed", "global_house_mode": self._group.get("house_mode"), } async def async_select_option(self, option: str) -> None: value = GROUP_MODE_VALUES.get(option) if value is None: return await self.coordinator.client.group_control(self._group_id, {"mode": value}) await self.coordinator.async_request_refresh() class GreeControllerGroupPresetSelect(_GreeControllerGroupSelect): """Select the work profile for one climate group.""" _attr_name = "Work profile" _attr_options = list(GROUP_PRESET_VALUES) def __init__(self, coordinator: GreeControllerCoordinator, group_id: str) -> None: super().__init__(coordinator, group_id) self._attr_unique_id = f"{group_id}-group-work-profile" @property def current_option(self) -> str | None: return GROUP_PRESET_LABELS.get(str(self._group.get("preset") or "")) @property def extra_state_attributes(self) -> dict[str, Any]: return {"mixed_group_profiles": self._group.get("preset") == "mixed"} async def async_select_option(self, option: str) -> None: value = GROUP_PRESET_VALUES.get(option) if value is None: return await self.coordinator.client.group_control(self._group_id, {"preset": value}) await self.coordinator.async_request_refresh()