Add custom attributes
Fix initialization temperature bug
This commit is contained in:
@@ -2,9 +2,10 @@ import math
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from typing import Any, Mapping
|
||||||
|
|
||||||
from homeassistant.core import (
|
from homeassistant.core import (
|
||||||
# HomeAssistant,
|
HomeAssistant,
|
||||||
callback,
|
callback,
|
||||||
CoreState,
|
CoreState,
|
||||||
DOMAIN as HA_DOMAIN,
|
DOMAIN as HA_DOMAIN,
|
||||||
@@ -35,10 +36,10 @@ from homeassistant.components.climate.const import (
|
|||||||
HVAC_MODE_HEAT,
|
HVAC_MODE_HEAT,
|
||||||
HVAC_MODE_OFF,
|
HVAC_MODE_OFF,
|
||||||
PRESET_ACTIVITY,
|
PRESET_ACTIVITY,
|
||||||
# PRESET_AWAY,
|
PRESET_AWAY,
|
||||||
# PRESET_BOOST,
|
PRESET_BOOST,
|
||||||
# PRESET_COMFORT,
|
PRESET_COMFORT,
|
||||||
# PRESET_ECO,
|
PRESET_ECO,
|
||||||
# PRESET_HOME,
|
# PRESET_HOME,
|
||||||
PRESET_NONE,
|
PRESET_NONE,
|
||||||
# PRESET_SLEEP,
|
# PRESET_SLEEP,
|
||||||
@@ -94,7 +95,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
_, # hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -134,6 +135,7 @@ async def async_setup_entry(
|
|||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[
|
||||||
VersatileThermostat(
|
VersatileThermostat(
|
||||||
|
hass,
|
||||||
unique_id,
|
unique_id,
|
||||||
name,
|
name,
|
||||||
heater_entity_id,
|
heater_entity_id,
|
||||||
@@ -170,6 +172,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
hass,
|
||||||
unique_id,
|
unique_id,
|
||||||
name,
|
name,
|
||||||
heater_entity_id,
|
heater_entity_id,
|
||||||
@@ -195,6 +198,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
self._hass = hass
|
||||||
|
self._attr_extra_state_attributes = {}
|
||||||
|
|
||||||
self._unique_id = unique_id
|
self._unique_id = unique_id
|
||||||
self._name = name
|
self._name = name
|
||||||
self._heater_entity_id = heater_entity_id
|
self._heater_entity_id = heater_entity_id
|
||||||
@@ -360,6 +366,13 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
"""Return the sensor temperature."""
|
"""Return the sensor temperature."""
|
||||||
return self._cur_temp
|
return self._cur_temp
|
||||||
|
|
||||||
|
# @property
|
||||||
|
# def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||||
|
# _LOGGER.debug(
|
||||||
|
# "Calling extra_state_attributes: %s", self._hass.custom_attributes
|
||||||
|
# )
|
||||||
|
# return self._hass.custom_attributes
|
||||||
|
|
||||||
async def async_set_hvac_mode(self, hvac_mode):
|
async def async_set_hvac_mode(self, hvac_mode):
|
||||||
"""Set new target hvac mode."""
|
"""Set new target hvac mode."""
|
||||||
_LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode)
|
_LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode)
|
||||||
@@ -655,6 +668,10 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if not self._hvac_mode and old_state.state:
|
if not self._hvac_mode and old_state.state:
|
||||||
self._hvac_mode = old_state.state
|
self._hvac_mode = old_state.state
|
||||||
|
|
||||||
|
self._prop_algorithm.calculate(
|
||||||
|
self._target_temp, self._cur_temp, self._cur_ext_temp
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# No previous state, try and restore defaults
|
# No previous state, try and restore defaults
|
||||||
if self._target_temp is None:
|
if self._target_temp is None:
|
||||||
@@ -1007,6 +1024,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
|
|
||||||
await self._async_heater_turn_on()
|
await self._async_heater_turn_on()
|
||||||
|
|
||||||
|
self.update_custom_attributes()
|
||||||
|
|
||||||
async def _turn_off(_):
|
async def _turn_off(_):
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - stop heating for %d min %d sec",
|
"%s - stop heating for %d min %d sec",
|
||||||
@@ -1017,6 +1036,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
await self._async_heater_turn_off()
|
await self._async_heater_turn_off()
|
||||||
self._async_cancel_cycle()
|
self._async_cancel_cycle()
|
||||||
self._async_cancel_cycle = None
|
self._async_cancel_cycle = None
|
||||||
|
self.update_custom_attributes()
|
||||||
|
|
||||||
# Program turn off
|
# Program turn off
|
||||||
self._async_cancel_cycle = async_call_later(
|
self._async_cancel_cycle = async_call_later(
|
||||||
@@ -1025,6 +1045,32 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
_turn_off,
|
_turn_off,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_custom_attributes(self):
|
||||||
|
"""Update the custom extra attributes for the entity"""
|
||||||
|
|
||||||
|
self._attr_extra_state_attributes = {
|
||||||
|
"away_temp": self._presets[PRESET_AWAY],
|
||||||
|
"eco_temp": self._presets[PRESET_ECO],
|
||||||
|
"boost_temp": self._presets[PRESET_BOOST],
|
||||||
|
"comfort_temp": self._presets[PRESET_BOOST],
|
||||||
|
"power_temp": self._presets[PRESET_POWER],
|
||||||
|
"on_percent": self._prop_algorithm.on_percent,
|
||||||
|
"on_time_sec": self._prop_algorithm.on_time_sec,
|
||||||
|
"off_time_sec": self._prop_algorithm.off_time_sec,
|
||||||
|
"ext_current_temperature": self._cur_ext_temp,
|
||||||
|
"current_power": self._current_power,
|
||||||
|
"current_power_max": self._current_power_max,
|
||||||
|
"cycle_min": self._cycle_min,
|
||||||
|
"bias": self._proportional_bias,
|
||||||
|
"function": self._proportional_function,
|
||||||
|
"tpi_coefc": self._tpi_coefc,
|
||||||
|
"tpi_coeft": self._tpi_coeft,
|
||||||
|
"is_device_active": self._is_device_active,
|
||||||
|
}
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Calling update_custom_attributes: %s", self._attr_extra_state_attributes
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_registry_entry_updated(self):
|
def async_registry_entry_updated(self):
|
||||||
"""update the entity if the config entry have been updated
|
"""update the entity if the config entry have been updated
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class PropAlgorithm:
|
|||||||
self._tpi_coefc = tpi_coefc
|
self._tpi_coefc = tpi_coefc
|
||||||
self._tpi_coeft = tpi_coeft
|
self._tpi_coeft = tpi_coeft
|
||||||
self._cycle_min = cycle_min
|
self._cycle_min = cycle_min
|
||||||
|
self._on_percent = 0
|
||||||
self._on_time_sec = 0
|
self._on_time_sec = 0
|
||||||
self._off_time_sec = self._cycle_min * 60
|
self._off_time_sec = self._cycle_min * 60
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ class PropAlgorithm:
|
|||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Proportional algorithm: calculation is not possible cause target_temp or current_temp is null. Heating will be disabled" # pylint: disable=line-too-long
|
"Proportional algorithm: calculation is not possible cause target_temp or current_temp is null. Heating will be disabled" # pylint: disable=line-too-long
|
||||||
)
|
)
|
||||||
on_percent = 0
|
self._on_percent = 0
|
||||||
else:
|
else:
|
||||||
delta_temp = target_temp - current_temp
|
delta_temp = target_temp - current_temp
|
||||||
delta_ext_temp = (
|
delta_ext_temp = (
|
||||||
@@ -50,11 +51,11 @@ class PropAlgorithm:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self._function == PROPORTIONAL_FUNCTION_LINEAR:
|
if self._function == PROPORTIONAL_FUNCTION_LINEAR:
|
||||||
on_percent = 0.25 * delta_temp + self._bias
|
self._on_percent = 0.25 * delta_temp + self._bias
|
||||||
elif self._function == PROPORTIONAL_FUNCTION_ATAN:
|
elif self._function == PROPORTIONAL_FUNCTION_ATAN:
|
||||||
on_percent = math.atan(delta_temp + self._bias) / 1.4
|
self._on_percent = math.atan(delta_temp + self._bias) / 1.4
|
||||||
elif self._function == PROPORTIONAL_FUNCTION_TPI:
|
elif self._function == PROPORTIONAL_FUNCTION_TPI:
|
||||||
on_percent = (
|
self._on_percent = (
|
||||||
self._tpi_coefc * delta_temp + self._tpi_coeft * delta_ext_temp
|
self._tpi_coefc * delta_temp + self._tpi_coeft * delta_ext_temp
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -62,14 +63,14 @@ class PropAlgorithm:
|
|||||||
"Proportional algorithm: unknown %s function. Heating will be disabled",
|
"Proportional algorithm: unknown %s function. Heating will be disabled",
|
||||||
self._function,
|
self._function,
|
||||||
)
|
)
|
||||||
on_percent = 0
|
self._on_percent = 0
|
||||||
|
|
||||||
# calculated on_time duration in seconds
|
# calculated on_time duration in seconds
|
||||||
if on_percent > 1:
|
if self._on_percent > 1:
|
||||||
on_percent = 1
|
self._on_percent = 1
|
||||||
if on_percent < 0:
|
if self._on_percent < 0:
|
||||||
on_percent = 0
|
self._on_percent = 0
|
||||||
self._on_time_sec = on_percent * self._cycle_min * 60
|
self._on_time_sec = self._on_percent * self._cycle_min * 60
|
||||||
|
|
||||||
# Do not heat for less than xx sec
|
# Do not heat for less than xx sec
|
||||||
if self._on_time_sec < PROPORTIONAL_MIN_DURATION_SEC:
|
if self._on_time_sec < PROPORTIONAL_MIN_DURATION_SEC:
|
||||||
@@ -80,18 +81,23 @@ class PropAlgorithm:
|
|||||||
)
|
)
|
||||||
self._on_time_sec = 0
|
self._on_time_sec = 0
|
||||||
|
|
||||||
self._off_time_sec = (1.0 - on_percent) * self._cycle_min * 60
|
self._off_time_sec = self._cycle_min * 60 - self._on_time_sec
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"heating percent calculated for current_temp %.1f, ext_current_temp %.1f and target_temp %.1f is %.2f, on_time is %d (sec), off_time is %d (sec)", # pylint: disable=line-too-long
|
"heating percent calculated for current_temp %.1f, ext_current_temp %.1f and target_temp %.1f is %.2f, on_time is %d (sec), off_time is %d (sec)", # pylint: disable=line-too-long
|
||||||
current_temp if current_temp else -9999.0,
|
current_temp if current_temp else -9999.0,
|
||||||
ext_current_temp if ext_current_temp else -9999.0,
|
ext_current_temp if ext_current_temp else -9999.0,
|
||||||
target_temp if target_temp else -9999.0,
|
target_temp if target_temp else -9999.0,
|
||||||
on_percent,
|
self._on_percent,
|
||||||
self.on_time_sec,
|
self.on_time_sec,
|
||||||
self.off_time_sec,
|
self.off_time_sec,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def on_percent(self) -> float:
|
||||||
|
"""Returns the percentage the heater must be ON (1 means the heater will be always on, 0 never on)""" # pylint: disable=line-too-long
|
||||||
|
return round(self._on_percent, 2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def on_time_sec(self) -> int:
|
def on_time_sec(self) -> int:
|
||||||
"""Returns the calculated time in sec the heater must be ON"""
|
"""Returns the calculated time in sec the heater must be ON"""
|
||||||
|
|||||||
Reference in New Issue
Block a user