"""Support for Hilo Climate entities.""" from datetime import datetime, timedelta from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate.const import ( ClimateEntityFeature, HVACAction, HVACMode, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_TENTHS, Platform, UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import slugify from . import Hilo from .const import CLIMATE_CLASSES, DOMAIN, LOG from .entity import HiloEntity def validate_reduction_phase(events, tag): """Validate if current time is within a challenge lock reduction phase.""" if not events: return current = events[0] phases = current["phases"] start = phases["reduction_start"] end = phases["reduction_end"] if ( start + timedelta(minutes=2) < datetime.now(start.tzinfo) < end - timedelta(minutes=2) ): LOG.warning( f"{tag} Attempt to set temperature was blocked because challenge lock is active" ) # Raising an exception here will raise it up to the GUI raise Exception("Challenge lock is active, unable to change temperature target") async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up Hilo climate entities from a config entry.""" hilo = hass.data[DOMAIN][entry.entry_id] entities = [] for d in hilo.devices.all: if d.type in CLIMATE_CLASSES: d._entity = HiloClimate(hilo, d) entities.append(d._entity) async_add_entities(entities) return True class HiloClimate(HiloEntity, ClimateEntity): """Representation of a Hilo Climate entity.""" _attr_hvac_modes = [HVACMode.HEAT] _attr_temperature_unit: str = UnitOfTemperature.CELSIUS _attr_precision: float = PRECISION_TENTHS _attr_supported_features: int = ClimateEntityFeature.TARGET_TEMPERATURE def __init__(self, hilo: Hilo, device): """Initialize the climate entity.""" super().__init__(hilo, device=device, name=device.name) old_unique_id = f"{slugify(device.name)}-climate" self._attr_unique_id = f"{device.identifier.lower()}-climate" hilo.async_migrate_unique_id( old_unique_id, self._attr_unique_id, Platform.CLIMATE ) hilo.async_migrate_unique_id( f"{slugify(device.identifier)}-climate", self._attr_unique_id, Platform.CLIMATE, ) self.operations = [HVACMode.HEAT] self._has_operation = False self._temperature_entity = None LOG.debug("Setting up Climate entity: %s", self._attr_name) @property def current_temperature(self): """Return the current temperature.""" return self._device.current_temperature @property def target_temperature(self): """Return the target temperature.""" return self._device.target_temperature @property def max_temp(self): """Return the maximum temperature.""" return self._device.max_temp @property def min_temp(self): """Return the minimum temperature.""" return self._device.min_temp def set_hvac_mode(self, hvac_mode): """Set hvac mode.""" return @property def hvac_mode(self): """Return hvac mode.""" return HVACMode.HEAT @property def hvac_action(self): """Return the current hvac action.""" return self._device.hvac_action @property def icon(self): """Return the icon to use in the frontend, based on hvac_action.""" if self._device.hvac_action == HVACAction.HEATING: return "mdi:radiator" return "mdi:radiator-disabled" async def async_set_temperature(self, **kwargs): """Set new target temperature.""" if ATTR_TEMPERATURE in kwargs: if self._hilo.challenge_lock: challenge = self._hilo._hass.states.get("sensor.defi_hilo") validate_reduction_phase( challenge.attributes.get("next_events", []), self._device._tag ) LOG.info( f"{self._device._tag} Setting temperature to {kwargs[ATTR_TEMPERATURE]}" ) await self._device.set_attribute( "target_temperature", kwargs[ATTR_TEMPERATURE] )