Add temp entities initialization

This commit is contained in:
Jean-Marc Collin
2024-02-04 20:06:44 +00:00
parent 8526b7d7ac
commit f7da58d841
3 changed files with 167 additions and 10 deletions

View File

@@ -1,4 +1,5 @@
""" Some usefull commons class """
# pylint: disable=line-too-long
import logging
@@ -182,6 +183,9 @@ class VersatileThermostatBaseEntity(Entity):
"""Returns my climate if found"""
if not self._my_climate:
self._my_climate = self.find_my_versatile_thermostat()
if self._my_climate:
# Only the first time
self.my_climate_is_initialized()
return self._my_climate
@property
@@ -238,6 +242,11 @@ class VersatileThermostatBaseEntity(Entity):
await try_find_climate(None)
@callback
def my_climate_is_initialized(self):
"""Called when the associated climate is initialized"""
return
@callback
async def async_my_climate_changed(
self, event: Event

View File

@@ -39,12 +39,14 @@ HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY]
DOMAIN = "versatile_thermostat"
# The order is important.
# NUMBER should be after CLIMATE,
PLATFORMS: list[Platform] = [
Platform.NUMBER,
Platform.SELECT,
Platform.CLIMATE,
Platform.SENSOR,
Platform.BINARY_SENSOR,
Platform.NUMBER,
]
CONF_HEATER = "heater_entity_id"
@@ -361,7 +363,9 @@ CENTRAL_MODES = [
class RegulationParamSlow:
"""Light parameters for slow latency regulation"""
kp: float = 0.2 # 20% of the current internal regulation offset are caused by the current difference of target temperature and room temperature
kp: float = (
0.2 # 20% of the current internal regulation offset are caused by the current difference of target temperature and room temperature
)
ki: float = (
0.8 / 288.0
) # 80% of the current internal regulation offset are caused by the average offset of the past 24 hours
@@ -369,7 +373,9 @@ class RegulationParamSlow:
1.0 / 25.0
) # this will add 1°C to the offset when it's 25°C colder outdoor than indoor
offset_max: float = 2.0 # limit to a final offset of -2°C to +2°C
stabilization_threshold: float = 0.0 # this needs to be disabled as otherwise the long term accumulated error will always be reset when the temp briefly crosses from/to below/above the target
stabilization_threshold: float = (
0.0 # this needs to be disabled as otherwise the long term accumulated error will always be reset when the temp briefly crosses from/to below/above the target
)
accumulated_error_threshold: float = (
2.0 * 288
) # this allows up to 2°C long term offset in both directions

View File

@@ -2,11 +2,27 @@
""" Implements the VersatileThermostat select component """
import logging
from typing import Any
# from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.core import HomeAssistant, CoreState # , callback
from homeassistant.components.number import NumberEntity, NumberMode
from homeassistant.components.number import (
NumberEntity,
NumberMode,
NumberDeviceClass,
ATTR_MAX,
ATTR_MIN,
ATTR_STEP,
ATTR_MODE,
)
from homeassistant.components.climate import (
PRESET_BOOST,
PRESET_COMFORT,
PRESET_ECO,
)
from homeassistant.components.sensor import UnitOfTemperature
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.restore_state import RestoreEntity
@@ -14,6 +30,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
from .commons import VersatileThermostatBaseEntity
from .const import (
DOMAIN,
DEVICE_MANUFACTURER,
@@ -21,9 +39,32 @@ from .const import (
CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_CENTRAL_CONFIG,
CONF_ADD_CENTRAL_BOILER_CONTROL,
CONF_TEMP_MIN,
CONF_TEMP_MAX,
CONF_STEP_TEMPERATURE,
CONF_AC_MODE,
PRESET_FROST_PROTECTION,
PRESET_ECO_AC,
PRESET_COMFORT_AC,
PRESET_BOOST_AC,
PRESET_AC_SUFFIX,
CONF_PRESETS_VALUES,
CONF_PRESETS_WITH_AC_VALUES,
# CONF_PRESETS_AWAY_VALUES,
# CONF_PRESETS_AWAY_WITH_AC_VALUES,
overrides,
)
PRESET_ICON_MAPPING = {
PRESET_FROST_PROTECTION + "_temp": "mdi:snowflake-thermometer",
PRESET_ECO + "_temp": "mdi:leaf",
PRESET_COMFORT + "_temp": "mdi:sofa",
PRESET_BOOST + "_temp": "mdi:rocket-launch",
PRESET_ECO_AC + "_temp": "mdi:leaf-circle-outline",
PRESET_COMFORT_AC + "_temp": "mdi:sofa-outline",
PRESET_BOOST_AC + "_temp": "mdi:rocket-launch-outline",
}
_LOGGER = logging.getLogger(__name__)
@@ -42,17 +83,30 @@ async def async_setup_entry(
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
is_central_boiler = entry.data.get(CONF_ADD_CENTRAL_BOILER_CONTROL)
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG or not is_central_boiler:
return
entities = []
entities = [
ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data),
]
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG or not is_central_boiler:
for preset in CONF_PRESETS_VALUES:
entities.append(
TemperatureNumber(hass, unique_id, preset, preset, entry.data)
)
if entry.data.get(CONF_AC_MODE, False):
for preset in CONF_PRESETS_WITH_AC_VALUES:
entities.append(
TemperatureNumber(hass, unique_id, preset, preset, entry.data)
)
else:
entities.append(
ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data)
)
async_add_entities(entities, True)
class ActivateBoilerThresholdNumber(NumberEntity, RestoreEntity):
class ActivateBoilerThresholdNumber(
NumberEntity, RestoreEntity
): # pylint: disable=abstract-method
"""Representation of the threshold of the number of VTherm
which should be active to activate the boiler"""
@@ -115,3 +169,91 @@ class ActivateBoilerThresholdNumber(NumberEntity, RestoreEntity):
def __str__(self):
return f"VersatileThermostat-{self.name}"
class TemperatureNumber( # pylint: disable=abstract-method
VersatileThermostatBaseEntity, NumberEntity, RestoreEntity
):
"""Representation of one temperature number"""
def __init__(
self, hass: HomeAssistant, unique_id, name, preset_name, entry_infos
) -> None:
"""Initialize the temperature with entry_infos if available. Else
the restoration will do the trick."""
super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
split = name.split("_")
self._attr_name = split[0]
if "_" + split[1] == PRESET_AC_SUFFIX:
self._attr_name = self._attr_name + " AC"
self._attr_name = self._attr_name + " temperature"
self._attr_unique_id = f"{self._device_name}_{name}"
self._attr_device_class = NumberDeviceClass.TEMPERATURE
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
# Initialize the values if included into the entry_infos. This will do
# the temperature migration.
# TODO see if this should be replace by the central config if any
temp = None
if temp := entry_infos.get(preset_name, None):
self._attr_value = self._attr_native_value = temp
self._attr_mode = NumberMode.BOX
self._preset_name = preset_name
self._attr_native_step = entry_infos.get(CONF_STEP_TEMPERATURE, 0.5)
self._attr_native_min_value = entry_infos.get(CONF_TEMP_MIN)
self._attr_native_max_value = entry_infos.get(CONF_TEMP_MAX)
@property
def icon(self) -> str | None:
return PRESET_ICON_MAPPING[self._preset_name]
@overrides
async def async_added_to_hass(self) -> None:
await super().async_added_to_hass()
old_state: CoreState = await self.async_get_last_state()
_LOGGER.debug(
"%s - Calling async_added_to_hass old_state is %s", self, old_state
)
if old_state is not None:
self._attr_value = self._attr_native_value = float(old_state.state)
@overrides
def my_climate_is_initialized(self):
"""Called when the associated climate is initialized"""
self._attr_native_step = self.my_climate.target_temperature_step
self._attr_native_min_value = self.my_climate.min_temp
self._attr_native_max_value = self.my_climate.max_temp
return
# @overrides
# @property
# def native_step(self) -> float | None:
# """The native step"""
# return self.my_climate.target_temperature_step
@overrides
def set_native_value(self, value: float) -> None:
"""Change the value"""
float_value = float(value)
old_value = float(self._attr_native_value)
if float_value == old_value:
return
self._attr_value = self._attr_native_value = float_value
def __str__(self):
return f"VersatileThermostat-{self.name}"
@property
def native_unit_of_measurement(self) -> str | None:
"""The unit of measurement"""
if not self.my_climate:
return UnitOfTemperature.CELSIUS
return self.my_climate.temperature_unit