Files
versatile_thermostat/custom_components/versatile_thermostat/vtherm_api.py
2024-03-09 10:57:13 +00:00

236 lines
9.1 KiB
Python

""" The API of Versatile Thermostat"""
import logging
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
from homeassistant.components.number import NumberEntity
from .const import (
DOMAIN,
CONF_AUTO_REGULATION_EXPERT,
CONF_SHORT_EMA_PARAMS,
CONF_SAFETY_MODE,
CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_CENTRAL_CONFIG,
)
VTHERM_API_NAME = "vtherm_api"
_LOGGER = logging.getLogger(__name__)
class VersatileThermostatAPI(dict):
"""The VersatileThermostatAPI"""
_hass: HomeAssistant = None
@classmethod
def get_vtherm_api(cls, hass=None):
"""Get the eventual VTherm API class instance or
instantiate it if it doesn't exists"""
if hass is not None:
VersatileThermostatAPI._hass = hass
if VersatileThermostatAPI._hass is None:
return None
domain = VersatileThermostatAPI._hass.data.get(DOMAIN)
if not domain:
VersatileThermostatAPI._hass.data.setdefault(DOMAIN, {})
ret = VersatileThermostatAPI._hass.data.get(DOMAIN).get(VTHERM_API_NAME)
if ret is None:
ret = VersatileThermostatAPI()
VersatileThermostatAPI._hass.data[DOMAIN][VTHERM_API_NAME] = ret
return ret
def __init__(self) -> None:
_LOGGER.debug("building a VersatileThermostatAPI")
super().__init__()
self._expert_params = None
self._short_ema_params = None
self._safety_mode = None
self._central_boiler_entity = None
self._threshold_number_entity = None
self._nb_active_number_entity = None
self._central_configuration = None
# A dict that will store all Number entities which holds the temperature
self._number_temperatures = dict()
def find_central_configuration(self):
"""Search for a central configuration"""
if not self._central_configuration:
for (
config_entry
) in VersatileThermostatAPI._hass.config_entries.async_entries(DOMAIN):
if (
config_entry.data.get(CONF_THERMOSTAT_TYPE)
== CONF_THERMOSTAT_CENTRAL_CONFIG
):
self._central_configuration = config_entry
break
# return self._central_configuration
return self._central_configuration
def add_entry(self, entry: ConfigEntry):
"""Add a new entry"""
_LOGGER.debug("Add the entry %s", entry.entry_id)
# Add the entry in hass.data
VersatileThermostatAPI._hass.data[DOMAIN][entry.entry_id] = entry
def remove_entry(self, entry: ConfigEntry):
"""Remove an entry"""
_LOGGER.debug("Remove the entry %s", entry.entry_id)
VersatileThermostatAPI._hass.data[DOMAIN].pop(entry.entry_id)
# If not more entries are preset, remove the API
if len(self) == 0:
_LOGGER.debug("No more entries-> Remove the API from DOMAIN")
VersatileThermostatAPI._hass.data.pop(DOMAIN)
def set_global_config(self, config):
"""Read the global configuration from configuration.yaml file"""
_LOGGER.info("Read global config from configuration.yaml")
self._expert_params = config.get(CONF_AUTO_REGULATION_EXPERT)
if self._expert_params:
_LOGGER.debug("We have found expert params %s", self._expert_params)
self._short_ema_params = config.get(CONF_SHORT_EMA_PARAMS)
if self._short_ema_params:
_LOGGER.debug("We have found short ema params %s", self._short_ema_params)
self._safety_mode = config.get(CONF_SAFETY_MODE)
if self._safety_mode:
_LOGGER.debug("We have found safet_mode params %s", self._safety_mode)
def register_central_boiler(self, central_boiler_entity):
"""Register the central boiler entity. This is used by the CentralBoilerBinarySensor
class to register itself at creation"""
self._central_boiler_entity = central_boiler_entity
def register_central_boiler_activation_number_threshold(
self, threshold_number_entity
):
"""register the two number entities needed for boiler activation"""
self._threshold_number_entity = threshold_number_entity
# If sensor and threshold number are initialized, reload the listener
# if self._nb_active_number_entity and self._central_boiler_entity:
# self._hass.async_add_job(self.reload_central_boiler_binary_listener)
def register_nb_device_active_boiler(self, nb_active_number_entity):
"""register the two number entities needed for boiler activation"""
self._nb_active_number_entity = nb_active_number_entity
# if self._threshold_number_entity and self._central_boiler_entity:
# self._hass.async_add_job(self.reload_central_boiler_binary_listener)
def register_temperature_number(
self,
config_id: str,
preset_name: str,
number_entity: NumberEntity,
):
"""Register the NumberEntity for a particular device / preset."""
# Search for device_name into the _number_temperatures dict
if not self._number_temperatures.get(config_id):
self._number_temperatures[config_id] = dict()
self._number_temperatures.get(config_id)[preset_name] = number_entity
def get_temperature_number_value(self, config_id, preset_name) -> float | None:
"""Returns the value of a previously registred NumberEntity which represent
a temperature. If no NumberEntity was previously registred, then returns None"""
entities = self._number_temperatures.get(config_id, None)
if entities:
entity = entities.get(preset_name, None)
if entity:
return entity.state
return None
async def init_vtherm_links(self, only_use_central=False):
"""INitialize all VTherms entities links
This method is called when HA is fully started (and all entities should be initialized)
Or when we need to reload all VTherm links (with Number temp entities, central boiler, ...)
"""
await self.reload_central_boiler_binary_listener()
await self.reload_central_boiler_entities_list()
# Initialization of all preset for all VTherm
component: EntityComponent[ClimateEntity] = self._hass.data.get(
CLIMATE_DOMAIN, None
)
if component:
for entity in component.entities:
if hasattr(entity, "init_presets"):
if (
only_use_central is False
or entity.use_central_config_temperature
):
await entity.init_presets(self.find_central_configuration())
async def reload_central_boiler_binary_listener(self):
"""Reloads the BinarySensor entity which listen to the number of
active devices and the thresholds entities"""
if self._central_boiler_entity:
await self._central_boiler_entity.listen_nb_active_vtherm_entity()
async def reload_central_boiler_entities_list(self):
"""Reload the central boiler list of entities if a central boiler is used"""
if self._nb_active_number_entity is not None:
await self._nb_active_number_entity.listen_vtherms_entities()
@property
def self_regulation_expert(self):
"""Get the self regulation params"""
return self._expert_params
@property
def short_ema_params(self):
"""Get the short EMA params in expert mode"""
return self._short_ema_params
@property
def safety_mode(self):
"""Get the safety_mode params"""
return self._safety_mode
@property
def central_boiler_entity(self):
"""Get the central boiler binary_sensor entity"""
return self._central_boiler_entity
@property
def nb_active_device_for_boiler(self):
"""Returns the number of active VTherm which have an
influence on boiler"""
if self._nb_active_number_entity is None:
return None
else:
return self._nb_active_number_entity.native_value
@property
def nb_active_device_for_boiler_entity(self):
"""Returns the number of active VTherm entity which have an
influence on boiler"""
return self._nb_active_number_entity
@property
def nb_active_device_for_boiler_threshold_entity(self):
"""Returns the number of active VTherm entity which have an
influence on boiler"""
return self._threshold_number_entity
@property
def nb_active_device_for_boiler_threshold(self):
"""Returns the number of active VTherm entity which have an
influence on boiler"""
if self._threshold_number_entity is None:
return None
return int(self._threshold_number_entity.native_value)
@property
def hass(self):
"""Get the HomeAssistant object"""
return VersatileThermostatAPI._hass