"""Climate platform for Saunum Leil Sauna Control Unit.""" from __future__ import annotations import asyncio from typing import Any from pysaunum import MAX_TEMPERATURE, MIN_TEMPERATURE, SaunumException from homeassistant.components.climate import ( FAN_HIGH, FAN_LOW, FAN_MEDIUM, FAN_OFF, ClimateEntity, ClimateEntityFeature, HVACAction, HVACMode, ) from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import LeilSaunaConfigEntry from .const import DELAYED_REFRESH_SECONDS, DOMAIN from .entity import LeilSaunaEntity PARALLEL_UPDATES = 1 # Map Saunum fan speed (0-3) to Home Assistant fan modes FAN_SPEED_TO_MODE = { 0: FAN_OFF, 1: FAN_LOW, 2: FAN_MEDIUM, 3: FAN_HIGH, } FAN_MODE_TO_SPEED = {v: k for k, v in FAN_SPEED_TO_MODE.items()} async def async_setup_entry( hass: HomeAssistant, entry: LeilSaunaConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Saunum Leil Sauna climate entity.""" coordinator = entry.runtime_data async_add_entities([LeilSaunaClimate(coordinator)]) class LeilSaunaClimate(LeilSaunaEntity, ClimateEntity): """Representation of a Saunum Leil Sauna climate entity.""" _attr_name = None _attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT] _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ) _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_min_temp = MIN_TEMPERATURE _attr_max_temp = MAX_TEMPERATURE _attr_fan_modes = [FAN_OFF, FAN_LOW, FAN_MEDIUM, FAN_HIGH] @property def current_temperature(self) -> float | None: """Return the current temperature in Celsius.""" return self.coordinator.data.current_temperature @property def target_temperature(self) -> float | None: """Return the target temperature in Celsius.""" return self.coordinator.data.target_temperature @property def fan_mode(self) -> str | None: """Return the current fan mode.""" fan_speed = self.coordinator.data.fan_speed if fan_speed is not None and fan_speed in FAN_SPEED_TO_MODE: return FAN_SPEED_TO_MODE[fan_speed] return None @property def hvac_mode(self) -> HVACMode: """Return current HVAC mode.""" session_active = self.coordinator.data.session_active return HVACMode.HEAT if session_active else HVACMode.OFF @property def hvac_action(self) -> HVACAction | None: """Return current HVAC action.""" if not self.coordinator.data.session_active: return HVACAction.OFF heater_elements_active = self.coordinator.data.heater_elements_active return ( HVACAction.HEATING if heater_elements_active and heater_elements_active > 0 else HVACAction.IDLE ) async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set new HVAC mode.""" try: if hvac_mode == HVACMode.HEAT: await self.coordinator.client.async_start_session() else: await self.coordinator.client.async_stop_session() except SaunumException as err: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="set_hvac_mode_failed", translation_placeholders={"hvac_mode": hvac_mode}, ) from err # The device takes 1-2 seconds to turn heater elements on/off and # update heater_elements_active. Wait and refresh again to ensure # the HVAC action state reflects the actual heater status. await asyncio.sleep(DELAYED_REFRESH_SECONDS.total_seconds()) await self.coordinator.async_request_refresh() async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" try: await self.coordinator.client.async_set_target_temperature( int(kwargs[ATTR_TEMPERATURE]) ) except SaunumException as err: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="set_temperature_failed", translation_placeholders={"temperature": str(kwargs[ATTR_TEMPERATURE])}, ) from err await self.coordinator.async_request_refresh() async def async_set_fan_mode(self, fan_mode: str) -> None: """Set new fan mode.""" if not self.coordinator.data.session_active: raise ServiceValidationError( "Cannot change fan mode when sauna session is not active", translation_domain=DOMAIN, translation_key="session_not_active", ) await self.coordinator.client.async_set_fan_speed(FAN_MODE_TO_SPEED[fan_mode]) await self.coordinator.async_request_refresh()