Files
koolnova-BMS-Integration/custom_components/climate.py
T

175 lines
7.0 KiB
Python

""" for Climate integration. """
from __future__ import annotations
from datetime import timedelta
import logging
from homeassistant.core import HomeAssistant
from homeassistant.util import Throttle
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.climate import (
ClimateEntity,
ConfigEntry,
)
from homeassistant.components.climate.const import (
HVACMode,
FAN_AUTO,
FAN_OFF,
)
from .const import (
DOMAIN,
SUPPORT_FLAGS,
SUPPORTED_HVAC_MODES,
SUPPORTED_FAN_MODES,
FAN_TRANSLATION,
HVAC_TRANSLATION,
)
from homeassistant.const import (
TEMP_CELSIUS,
ATTR_TEMPERATURE,
)
from .koolnova.device import Koolnova, Area
from .koolnova.const import (
MIN_TEMP,
MAX_TEMP,
STEP_TEMP,
MIN_TEMP_ORDER,
MAX_TEMP_ORDER,
STEP_TEMP_ORDER,
SysState,
ZoneState,
ZoneClimMode,
ZoneFanMode,
)
_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
async def async_setup_entry(hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback):
"""Setup switch entries"""
entities = []
for device in hass.data[DOMAIN]:
for area in device.areas:
_LOGGER.debug("Device: {} - Area: {}".format(device, area))
entities.append(AreaClimateEntity(device, area))
async_add_entities(entities)
class AreaClimateEntity(ClimateEntity):
""" Reperesentation of a climate entity """
# pylint: disable = too-many-instance-attributes
_attr_supported_features: int = SUPPORT_FLAGS
_attr_temperature_unit: str = TEMP_CELSIUS
_attr_hvac_modes: list[HVACMode] = SUPPORTED_HVAC_MODES
_attr_fan_modes: list[str] = SUPPORTED_FAN_MODES
_attr_hvac_mode: HVACMode = HVACMode.OFF
_attr_fan_mode: str = FAN_OFF
_attr_min_temp: float = MIN_TEMP
_attr_max_temp: float = MAX_TEMP
_attr_precision: float = STEP_TEMP
_attr_target_temperature_high: float = MAX_TEMP_ORDER
_attr_target_temperature_low: float = MIN_TEMP_ORDER
_attr_target_temperature_step: float = STEP_TEMP_ORDER
def __init__(self,
device: Koolnova, # pylint: disable=unused-argument
area: Area, # pylint: disable=unused-argument
) -> None:
""" Class constructor """
self._device = device
self._area = area
self._attr_name = f"{device.name} {area.name} area"
self._attr_device_info = device.device_info
self._attr_unique_id = f"{DOMAIN}-{area.name}-area-climate"
self._attr_current_temperature = area.real_temp
self._attr_target_temperature = area.order_temp
_LOGGER.debug("[Climate {}] {} - {}".format(self._area.id_zone, self._area.fan_mode, FAN_TRANSLATION[int(self._area.fan_mode)]))
self._attr_fan_mode = FAN_TRANSLATION[int(self._area.fan_mode)]
_LOGGER.debug("[Climate {}] {} - {}".format(self._area.id_zone, self._area.clim_mode, self._translate_to_hvac_mode()))
self._attr_hvac_mode = self._translate_to_hvac_mode()
def _translate_to_hvac_mode(self) -> int:
""" translate area state and clim mode to HA hvac mode """
ret = 0
if self._area.state == ZoneState.STATE_OFF:
ret = HVACMode.OFF
else:
ret = HVAC_TRANSLATION[int(self._area.clim_mode)]
return ret
async def async_set_temperature(self, **kwargs) -> None:
""" set new target temperature """
_LOGGER.debug("[Climate {}] set target temp - kwargs: {}".format(self._area.id_zone, kwargs))
if "temperature" in kwargs:
target_temp = kwargs.get("temperature")
ret = await self._device.set_area_target_temp(zone_id = self._area.id_zone, temp = target_temp)
if not ret:
_LOGGER.error("Error sending target temperature for area id {}".format(self._area.id_zone))
else:
_LOGGER.warning("Target temperature not defined for climate id {}".format(self._area.id_zone))
async def async_set_fan_mode(self, fan_mode:str) -> None:
""" set new target fan mode """
_LOGGER.debug("[Climate {}] set new fan mode: {}".format(self._area.id_zone, fan_mode))
for k,v in FAN_TRANSLATION.items():
if v == fan_mode:
opt = k
break
ret = await self._device.set_area_fan_mode(zone_id = self._area.id_zone,
mode = ZoneFanMode(opt))
await self._update_state()
async def async_set_hvac_mode(self, hvac_mode:HVACMode) -> None:
""" set new target hvac mode """
_LOGGER.debug("[Climate {}] set new hvac mode: {}".format(self._area.id_zone, hvac_mode))
opt = 0
for k,v in HVAC_TRANSLATION.items():
if v == hvac_mode:
opt = k
break
ret = await self._device.set_area_clim_mode(zone_id = self._area.id_zone,
mode = ZoneClimMode(opt))
await self._update_state()
async def async_turn_on(self) -> None:
""" turn the entity on """
_LOGGER.debug("[Climate {}] turn on the entity".format(self._area.id_zone))
#await self._update_state()
async def async_turn_off(self) -> None:
""" turn the entity off """
_LOGGER.debug("[Climate {}] turn off the entity".format(self._area.id_zone))
#await self._update_state()
async def _update_state(self) -> None:
""" Private update attributes """
_LOGGER.debug("[Climate {}] _update_state".format(self._area.id_zone))
# retreive current temperature from specific area
ret, up_area = await self._device.update_area(self._area.id_zone)
if not ret:
_LOGGER.error("[Climate {}] Cannot update area values")
return
self._area = up_area
_LOGGER.debug("[Climate {}] temp:{} - target:{} - state: {} - hvac:{} - fan:{}".format(self._area.id_zone,
self._area.real_temp,
self._area.order_temp,
self._area.state,
self._area.clim_mode,
self._area.fan_mode))
self._attr_current_temperature = self._area.real_temp
self._attr_target_temperature = self._area.order_temp
self._attr_hvac_mode = self._translate_to_hvac_mode()
self._attr_fan_mode = FAN_TRANSLATION[int(self._area.fan_mode)]
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
""" Retreive latest values """
await self._update_state()