"""Support for KNX/IP climate devices.""" from typing import List, Optional from xknx.devices import Climate as XknxClimate from xknx.dpt.dpt_hvac_mode import HVACControllerMode, HVACOperationMode from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate.const import ( HVAC_MODE_HEAT, HVAC_MODE_OFF, PRESET_AWAY, SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, ) from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from .const import CONTROLLER_MODES, DOMAIN, PRESET_MODES from .knx_entity import KnxEntity CONTROLLER_MODES_INV = {value: key for key, value in CONTROLLER_MODES.items()} PRESET_MODES_INV = {value: key for key, value in PRESET_MODES.items()} async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up climate(s) for KNX platform.""" entities = [] for device in hass.data[DOMAIN].xknx.devices: if isinstance(device, XknxClimate): entities.append(KNXClimate(device)) async_add_entities(entities) class KNXClimate(KnxEntity, ClimateEntity): """Representation of a KNX climate device.""" def __init__(self, device: XknxClimate): """Initialize of a KNX climate device.""" super().__init__(device) self._unit_of_measurement = TEMP_CELSIUS @property def supported_features(self) -> int: """Return the list of supported features.""" return SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE async def async_update(self): """Request a state update from KNX bus.""" await self._device.sync() await self._device.mode.sync() @property def temperature_unit(self): """Return the unit of measurement.""" return self._unit_of_measurement @property def current_temperature(self): """Return the current temperature.""" return self._device.temperature.value @property def target_temperature_step(self): """Return the supported step of target temperature.""" return self._device.temperature_step @property def target_temperature(self): """Return the temperature we try to reach.""" return self._device.target_temperature.value @property def min_temp(self): """Return the minimum temperature.""" return self._device.target_temperature_min @property def max_temp(self): """Return the maximum temperature.""" return self._device.target_temperature_max async def async_set_temperature(self, **kwargs) -> None: """Set new target temperature.""" temperature = kwargs.get(ATTR_TEMPERATURE) if temperature is None: return await self._device.set_target_temperature(temperature) self.async_write_ha_state() @property def hvac_mode(self) -> Optional[str]: """Return current operation ie. heat, cool, idle.""" if self._device.supports_on_off and not self._device.is_on: return HVAC_MODE_OFF if self._device.mode.supports_controller_mode: return CONTROLLER_MODES.get( self._device.mode.controller_mode.value, HVAC_MODE_HEAT ) # default to "heat" return HVAC_MODE_HEAT @property def hvac_modes(self) -> Optional[List[str]]: """Return the list of available operation/controller modes.""" _controller_modes = [ CONTROLLER_MODES.get(controller_mode.value) for controller_mode in self._device.mode.controller_modes ] if self._device.supports_on_off: if not _controller_modes: _controller_modes.append(HVAC_MODE_HEAT) _controller_modes.append(HVAC_MODE_OFF) _modes = list(set(filter(None, _controller_modes))) # default to ["heat"] return _modes if _modes else [HVAC_MODE_HEAT] async def async_set_hvac_mode(self, hvac_mode: str) -> None: """Set operation mode.""" if self._device.supports_on_off and hvac_mode == HVAC_MODE_OFF: await self._device.turn_off() else: if self._device.supports_on_off and not self._device.is_on: await self._device.turn_on() if self._device.mode.supports_controller_mode: knx_controller_mode = HVACControllerMode( CONTROLLER_MODES_INV.get(hvac_mode) ) await self._device.mode.set_controller_mode(knx_controller_mode) self.async_write_ha_state() @property def preset_mode(self) -> Optional[str]: """Return the current preset mode, e.g., home, away, temp. Requires SUPPORT_PRESET_MODE. """ if self._device.mode.supports_operation_mode: return PRESET_MODES.get(self._device.mode.operation_mode.value, PRESET_AWAY) return None @property def preset_modes(self) -> Optional[List[str]]: """Return a list of available preset modes. Requires SUPPORT_PRESET_MODE. """ _presets = [ PRESET_MODES.get(operation_mode.value) for operation_mode in self._device.mode.operation_modes ] return list(filter(None, _presets)) async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new preset mode.""" if self._device.mode.supports_operation_mode: knx_operation_mode = HVACOperationMode(PRESET_MODES_INV.get(preset_mode)) await self._device.mode.set_operation_mode(knx_operation_mode) self.async_write_ha_state()