Compare commits

...

1 Commits

Author SHA1 Message Date
Jean-Marc Collin
638e007c21 Add min_temp and max_temp in configuration #17
Mise en sécurité du thermostat si pas de changement de température #18
Add a configurable minimal delay of activation #19
2023-01-18 23:40:55 +01:00
7 changed files with 213 additions and 95 deletions

View File

@@ -96,6 +96,10 @@ from .const import (
SERVICE_SET_PRESENCE, SERVICE_SET_PRESENCE,
SERVICE_SET_PRESET_TEMPERATURE, SERVICE_SET_PRESET_TEMPERATURE,
PRESET_AWAY_SUFFIX, PRESET_AWAY_SUFFIX,
CONF_SECURITY_DELAY_MIN,
CONF_MINIMAL_ACTIVATION_DELAY,
CONF_TEMP_MAX,
CONF_TEMP_MIN,
) )
from .prop_algorithm import PropAlgorithm from .prop_algorithm import PropAlgorithm
@@ -133,6 +137,10 @@ async def async_setup_entry(
tpi_coef_ext = entry.data.get(CONF_TPI_COEF_EXT) tpi_coef_ext = entry.data.get(CONF_TPI_COEF_EXT)
presence_sensor_entity_id = entry.data.get(CONF_PRESENCE_SENSOR) presence_sensor_entity_id = entry.data.get(CONF_PRESENCE_SENSOR)
power_temp = entry.data.get(CONF_PRESET_POWER) power_temp = entry.data.get(CONF_PRESET_POWER)
temp_min = entry.data.get(CONF_TEMP_MIN)
temp_max = entry.data.get(CONF_TEMP_MAX)
security_delay_min = entry.data.get(CONF_SECURITY_DELAY_MIN)
minimal_activation_delay = entry.data.get(CONF_MINIMAL_ACTIVATION_DELAY)
presets = {} presets = {}
for (key, value) in CONF_PRESETS.items(): for (key, value) in CONF_PRESETS.items():
@@ -161,6 +169,8 @@ async def async_setup_entry(
proportional_function, proportional_function,
temp_sensor_entity_id, temp_sensor_entity_id,
ext_temp_sensor_entity_id, ext_temp_sensor_entity_id,
temp_min,
temp_max,
power_sensor_entity_id, power_sensor_entity_id,
max_power_sensor_entity_id, max_power_sensor_entity_id,
window_sensor_entity_id, window_sensor_entity_id,
@@ -176,6 +186,8 @@ async def async_setup_entry(
tpi_coef_ext, tpi_coef_ext,
presence_sensor_entity_id, presence_sensor_entity_id,
power_temp, power_temp,
security_delay_min,
minimal_activation_delay,
) )
], ],
True, True,
@@ -223,6 +235,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
proportional_function, proportional_function,
temp_sensor_entity_id, temp_sensor_entity_id,
ext_temp_sensor_entity_id, ext_temp_sensor_entity_id,
temp_min,
temp_max,
power_sensor_entity_id, power_sensor_entity_id,
max_power_sensor_entity_id, max_power_sensor_entity_id,
window_sensor_entity_id, window_sensor_entity_id,
@@ -238,6 +252,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
tpi_coef_ext, tpi_coef_ext,
presence_sensor_entity_id, presence_sensor_entity_id,
power_temp, power_temp,
security_delay_min,
minimal_activation_delay,
) -> None: ) -> None:
"""Initialize the thermostat.""" """Initialize the thermostat."""
@@ -253,6 +269,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._proportional_function = proportional_function self._proportional_function = proportional_function
self._temp_sensor_entity_id = temp_sensor_entity_id self._temp_sensor_entity_id = temp_sensor_entity_id
self._ext_temp_sensor_entity_id = ext_temp_sensor_entity_id self._ext_temp_sensor_entity_id = ext_temp_sensor_entity_id
self._attr_max_temp = temp_max
self._attr_min_temp = temp_min
self._power_sensor_entity_id = power_sensor_entity_id self._power_sensor_entity_id = power_sensor_entity_id
self._max_power_sensor_entity_id = max_power_sensor_entity_id self._max_power_sensor_entity_id = max_power_sensor_entity_id
self._window_sensor_entity_id = window_sensor_entity_id self._window_sensor_entity_id = window_sensor_entity_id
@@ -273,7 +291,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._presence_sensor_entity_id = presence_sensor_entity_id self._presence_sensor_entity_id = presence_sensor_entity_id
self._power_temp = power_temp self._power_temp = power_temp
self._presence_on = self._presence_sensor_entity_id != None self._presence_on = self._presence_sensor_entity_id is not None
# TODO if self.ac_mode: # TODO if self.ac_mode:
# self.hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF] # self.hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF]
@@ -335,14 +353,21 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
) )
self._tpi_coef_ext = 0 self._tpi_coef_ext = 0
self._security_delay_min = security_delay_min
self._minimal_activation_delay = minimal_activation_delay
self._last_temperature_mesure = datetime.now()
self._last_ext_temperature_mesure = datetime.now()
self._security_state = False
self._saved_hvac_mode = None
# Initiate the ProportionalAlgorithm # Initiate the ProportionalAlgorithm
self._prop_algorithm = PropAlgorithm( self._prop_algorithm = PropAlgorithm(
self._proportional_function, self._proportional_function,
self._tpi_coef_int, self._tpi_coef_int,
self._tpi_coef_ext, self._tpi_coef_ext,
self._cycle_min, self._cycle_min,
self._minimal_activation_delay,
) )
self._async_cancel_cycle = None self._async_cancel_cycle = None
self._window_call_cancel = None self._window_call_cancel = None
self._motion_call_cancel = None self._motion_call_cancel = None
@@ -654,7 +679,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
_LOGGER.debug("%s - Calling async_startup", self) _LOGGER.debug("%s - Calling async_startup", self)
@callback @callback
def _async_startup_internal(*_): async def _async_startup_internal(*_):
_LOGGER.debug("%s - Calling async_startup_internal", self) _LOGGER.debug("%s - Calling async_startup_internal", self)
need_write_state = False need_write_state = False
temperature_state = self.hass.states.get(self._temp_sensor_entity_id) temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
@@ -667,7 +692,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self, self,
float(temperature_state.state), float(temperature_state.state),
) )
self._async_update_temp(temperature_state) await self._async_update_temp(temperature_state)
need_write_state = True need_write_state = True
if self._ext_temp_sensor_entity_id: if self._ext_temp_sensor_entity_id:
@@ -683,7 +708,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self, self,
float(ext_temperature_state.state), float(ext_temperature_state.state),
) )
self._async_update_ext_temp(ext_temperature_state) await self._async_update_ext_temp(ext_temperature_state)
else: else:
_LOGGER.debug( _LOGGER.debug(
"%s - external temperature sensor have NOT been retrieved cause unknown or unavailable", "%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
@@ -790,7 +815,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
await self.get_my_previous_state() await self.get_my_previous_state()
if self.hass.state == CoreState.running: if self.hass.state == CoreState.running:
_async_startup_internal() await _async_startup_internal()
else: else:
self.hass.bus.async_listen_once( self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, _async_startup_internal EVENT_HOMEASSISTANT_START, _async_startup_internal
@@ -869,7 +894,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN): if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return return
self._async_update_temp(new_state) await self._async_update_temp(new_state)
self.recalculate() self.recalculate()
await self._async_control_heating(force=False) await self._async_control_heating(force=False)
@@ -884,7 +909,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN): if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return return
self._async_update_ext_temp(new_state) await self._async_update_ext_temp(new_state)
self.recalculate() self.recalculate()
await self._async_control_heating(force=False) await self._async_control_heating(force=False)
@@ -1028,24 +1053,32 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self.async_write_ha_state() self.async_write_ha_state()
@callback @callback
def _async_update_temp(self, state): async def _async_update_temp(self, state):
"""Update thermostat with latest state from sensor.""" """Update thermostat with latest state from sensor."""
try: try:
cur_temp = float(state.state) cur_temp = float(state.state)
if math.isnan(cur_temp) or math.isinf(cur_temp): if math.isnan(cur_temp) or math.isinf(cur_temp):
raise ValueError(f"Sensor has illegal state {state.state}") raise ValueError(f"Sensor has illegal state {state.state}")
self._cur_temp = cur_temp self._cur_temp = cur_temp
self._last_temperature_mesure = datetime.now()
# try to restart if we were in security mode
if self._security_state:
await self.async_set_hvac_mode(self._saved_hvac_mode)
except ValueError as ex: except ValueError as ex:
_LOGGER.error("Unable to update temperature from sensor: %s", ex) _LOGGER.error("Unable to update temperature from sensor: %s", ex)
@callback @callback
def _async_update_ext_temp(self, state): async def _async_update_ext_temp(self, state):
"""Update thermostat with latest state from sensor.""" """Update thermostat with latest state from sensor."""
try: try:
cur_ext_temp = float(state.state) cur_ext_temp = float(state.state)
if math.isnan(cur_ext_temp) or math.isinf(cur_ext_temp): if math.isnan(cur_ext_temp) or math.isinf(cur_ext_temp):
raise ValueError(f"Sensor has illegal state {state.state}") raise ValueError(f"Sensor has illegal state {state.state}")
self._cur_ext_temp = cur_ext_temp self._cur_ext_temp = cur_ext_temp
self._last_ext_temperature_mesure = datetime.now()
# try to restart if we were in security mode
if self._security_state:
await self.async_set_hvac_mode(self._saved_hvac_mode)
except ValueError as ex: except ValueError as ex:
_LOGGER.error("Unable to update external temperature from sensor: %s", ex) _LOGGER.error("Unable to update external temperature from sensor: %s", ex)
@@ -1235,6 +1268,28 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._saved_preset_mode if self._saved_preset_mode else PRESET_NONE self._saved_preset_mode if self._saved_preset_mode else PRESET_NONE
) )
def check_date_temperature(self) -> bool:
"""Check if last temperature date is too long"""
now = datetime.now()
delta_temp = (now - self._last_temperature_mesure).total_seconds() / 60.0
delta_ext_temp = (
now - self._last_ext_temperature_mesure
).total_seconds() / 60.0
if (
delta_temp > self._security_delay_min
or delta_ext_temp > self._security_delay_min
):
_LOGGER.warning(
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f)",
self,
self._security_delay_min,
delta_temp,
delta_ext_temp,
)
return False
return True
async def _async_control_heating(self, force=False, time=None): async def _async_control_heating(self, force=False, time=None):
"""The main function used to run the calculation at each cycle""" """The main function used to run the calculation at each cycle"""
@@ -1251,17 +1306,18 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
on_time_sec: int = self._prop_algorithm.on_time_sec on_time_sec: int = self._prop_algorithm.on_time_sec
off_time_sec: int = self._prop_algorithm.off_time_sec off_time_sec: int = self._prop_algorithm.off_time_sec
_LOGGER.info( _LOGGER.info(
"%s - Running new cycle at %s. on_time_sec=%.0f, off_time_sec=%.0f", "%s - Running new cycle at %s. on_time_sec=%.0f, off_time_sec=%.0f, security_state=%s",
self, self,
time, time,
on_time_sec, on_time_sec,
off_time_sec, off_time_sec,
self._security_state,
) )
# Cancel eventual previous cycle if any # Cancel eventual previous cycle if any
if self._async_cancel_cycle is not None: if self._async_cancel_cycle is not None:
if force: if force:
_LOGGER.debug("%s - we force a new cycle") _LOGGER.debug("%s - we force a new cycle", self)
self._async_cancel_cycle() self._async_cancel_cycle()
self._async_cancel_cycle = None self._async_cancel_cycle = None
else: else:
@@ -1271,21 +1327,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
) )
self._should_relaunch_control_heating = True self._should_relaunch_control_heating = True
return return
# await self._async_cancel_cycle()
# self._async_cancel_cycle = None
# Don't turn off if we will turn on just after
# if on_time_sec <= 0:
# await self._async_heater_turn_off()
if self._hvac_mode == HVAC_MODE_HEAT and on_time_sec > 0: if self._hvac_mode == HVAC_MODE_HEAT and on_time_sec > 0:
# _LOGGER.info(
# "%s - start heating for %d min %d sec ",
# self,
# on_time_sec // 60,
# on_time_sec % 60,
# )
#
# await self._async_heater_turn_on()
async def _turn_on_off_later( async def _turn_on_off_later(
on: bool, time, heater_action, next_cycle_action on: bool, time, heater_action, next_cycle_action
@@ -1294,6 +1337,13 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._async_cancel_cycle() self._async_cancel_cycle()
self._async_cancel_cycle = None self._async_cancel_cycle = None
if time > 0 and on is True and self.check_date_temperature() is False:
_LOGGER.warning("%s - Set the thermostat into security mode")
self._security_state = True
self._saved_hvac_mode = self.hvac_mode
await self.async_set_hvac_mode(HVAC_MODE_OFF)
return
action_label = "start" if on else "stop" action_label = "start" if on else "stop"
if self._should_relaunch_control_heating: if self._should_relaunch_control_heating:
_LOGGER.debug( _LOGGER.debug(
@@ -1331,30 +1381,6 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
next_cycle_action=_turn_off_later, next_cycle_action=_turn_off_later,
) )
# if self._async_cancel_cycle:
# self._async_cancel_cycle()
# self._async_cancel_cycle = None
#
# if self._should_relaunch_control_heating:
# _LOGGER.debug("Don't stop cause a cycle have to be relaunch")
# self._should_relaunch_control_heating = False
# await self._async_control_heating()
# return
# else:
# _LOGGER.info(
# "%s - stop heating for %d min %d sec",
# self,
# off_time_sec // 60,
# off_time_sec % 60,
# )
# await self._async_heater_turn_off()
# self.update_custom_attributes()
# self._async_cancel_cycle = async_call_later(
# self.hass,
# off_time_sec,
# _turn_off_later,
# )
async def _turn_off_later(_): async def _turn_off_later(_):
await _turn_on_off_later( await _turn_on_off_later(
on=False, on=False,
@@ -1363,36 +1389,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
next_cycle_action=_turn_on_later, next_cycle_action=_turn_on_later,
) )
# if self._async_cancel_cycle:
# self._async_cancel_cycle()
# self._async_cancel_cycle = None
#
# if self._should_relaunch_control_heating:
# _LOGGER.debug("Don't stop cause a cycle have to be relaunch")
# self._should_relaunch_control_heating = False
# await self._async_control_heating()
# return
# else:
# _LOGGER.info(
# "%s - stop heating for %d min %d sec",
# self,
# off_time_sec // 60,
# off_time_sec % 60,
# )
# await self._async_heater_turn_off()
# self.update_custom_attributes()
# self._async_cancel_cycle = async_call_later(
# self.hass,
# on_time_sec,
# _turn_on_later,
# )
await _turn_on_later(None) await _turn_on_later(None)
# # Program turn off
# self._async_cancel_cycle = async_call_later(
# self.hass,
# on_time_sec,
# _turn_off_later,
# )
elif self._is_device_active: elif self._is_device_active:
_LOGGER.info( _LOGGER.info(
@@ -1446,10 +1443,16 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
"tpi_coef_ext": self._tpi_coef_ext, "tpi_coef_ext": self._tpi_coef_ext,
"saved_preset_mode": self._saved_preset_mode, "saved_preset_mode": self._saved_preset_mode,
"saved_target_temp": self._saved_target_temp, "saved_target_temp": self._saved_target_temp,
"saved_hvac_mode": self._saved_hvac_mode,
"window_state": self._window_state, "window_state": self._window_state,
"motion_state": self._motion_state, "motion_state": self._motion_state,
"overpowering_state": self._overpowering_state, "overpowering_state": self._overpowering_state,
"presence_state": self._presence_state, "presence_state": self._presence_state,
"security_delay_min": self._security_delay_min,
"last_temperature_datetime": self._last_temperature_mesure.isoformat(),
"last_ext_temperature_datetime": self._last_ext_temperature_mesure.isoformat(),
"security_state": self._security_state,
"minimal_activation_delay_sec": self._minimal_activation_delay,
"last_update_datetime": datetime.now().isoformat(), "last_update_datetime": datetime.now().isoformat(),
} }
self.async_write_ha_state() self.async_write_ha_state()

View File

@@ -45,6 +45,10 @@ from .const import (
CONF_TPI_COEF_INT, CONF_TPI_COEF_INT,
CONF_PRESENCE_SENSOR, CONF_PRESENCE_SENSOR,
PROPORTIONAL_FUNCTION_TPI, PROPORTIONAL_FUNCTION_TPI,
CONF_SECURITY_DELAY_MIN,
CONF_MINIMAL_ACTIVATION_DELAY,
CONF_TEMP_MAX,
CONF_TEMP_MIN,
) )
# from .climate import VersatileThermostat # from .climate import VersatileThermostat
@@ -117,6 +121,8 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
PROPORTIONAL_FUNCTION_TPI, PROPORTIONAL_FUNCTION_TPI,
] ]
), ),
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
} }
) )
@@ -174,6 +180,15 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
} }
) )
self.STEP_ADVANCED_DATA_SCHEMA = vol.Schema(
{
vol.Required(
CONF_MINIMAL_ACTIVATION_DELAY, default=10
): cv.positive_int,
vol.Required(CONF_SECURITY_DELAY_MIN, default=60): cv.positive_int,
}
)
async def validate_input(self, data: dict) -> dict[str]: async def validate_input(self, data: dict) -> dict[str]:
"""Validate the user input allows us to connect. """Validate the user input allows us to connect.
@@ -303,6 +318,17 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
"presence", "presence",
self.STEP_PRESENCE_DATA_SCHEMA, self.STEP_PRESENCE_DATA_SCHEMA,
user_input, user_input,
self.async_step_advanced,
)
async def async_step_advanced(self, user_input: dict | None = None) -> FlowResult:
"""Handle the advanced parameter flow steps"""
_LOGGER.debug("Into ConfigFlow.async_step_advanced user_input=%s", user_input)
return await self.generic_step(
"advanced",
self.STEP_ADVANCED_DATA_SCHEMA,
user_input,
self.async_finalize, # pylint: disable=no-member self.async_finalize, # pylint: disable=no-member
) )
@@ -417,7 +443,7 @@ class VersatileThermostatOptionsFlowHandler(
"power", "power",
self.STEP_POWER_DATA_SCHEMA, self.STEP_POWER_DATA_SCHEMA,
user_input, user_input,
self.async_step_presence, # pylint: disable=no-member self.async_step_presence,
) )
async def async_step_presence(self, user_input: dict | None = None) -> FlowResult: async def async_step_presence(self, user_input: dict | None = None) -> FlowResult:
@@ -430,7 +456,20 @@ class VersatileThermostatOptionsFlowHandler(
"presence", "presence",
self.STEP_PRESENCE_DATA_SCHEMA, self.STEP_PRESENCE_DATA_SCHEMA,
user_input, user_input,
self.async_finalize, # pylint: disable=no-member self.async_step_advanced,
)
async def async_step_advanced(self, user_input: dict | None = None) -> FlowResult:
"""Handle the advanced flow steps"""
_LOGGER.debug(
"Into OptionsFlowHandler.async_step_advanced user_input=%s", user_input
)
return await self.generic_step(
"advanced",
self.STEP_ADVANCED_DATA_SCHEMA,
user_input,
self.async_finalize,
) )
async def async_finalize(self): async def async_finalize(self):

View File

@@ -35,6 +35,10 @@ CONF_TPI_COEF_INT = "tpi_coef_int"
CONF_TPI_COEF_EXT = "tpi_coef_ext" CONF_TPI_COEF_EXT = "tpi_coef_ext"
CONF_PRESENCE_SENSOR = "presence_sensor_entity_id" CONF_PRESENCE_SENSOR = "presence_sensor_entity_id"
CONF_PRESET_POWER = "power_temp" CONF_PRESET_POWER = "power_temp"
CONF_MINIMAL_ACTIVATION_DELAY = "minimal_activation_delay"
CONF_TEMP_MIN = "temp_min"
CONF_TEMP_MAX = "temp_max"
CONF_SECURITY_DELAY_MIN = "security_delay_min"
CONF_PRESETS = { CONF_PRESETS = {
p: f"{p}_temp" p: f"{p}_temp"
@@ -81,6 +85,10 @@ ALL_CONF = (
CONF_TPI_COEF_INT, CONF_TPI_COEF_INT,
CONF_TPI_COEF_EXT, CONF_TPI_COEF_EXT,
CONF_PRESENCE_SENSOR, CONF_PRESENCE_SENSOR,
CONF_MINIMAL_ACTIVATION_DELAY,
CONF_TEMP_MIN,
CONF_TEMP_MAX,
CONF_SECURITY_DELAY_MIN,
] ]
+ CONF_PRESETS_VALUES + CONF_PRESETS_VALUES
+ CONF_PRESETS_AWAY_VALUES, + CONF_PRESETS_AWAY_VALUES,

View File

@@ -1,5 +1,4 @@
import logging import logging
import math
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -21,20 +20,22 @@ class PropAlgorithm:
tpi_coef_int, tpi_coef_int,
tpi_coef_ext, tpi_coef_ext,
cycle_min: int, cycle_min: int,
minimal_activation_delay: int,
): ):
"""Initialisation of the Proportional Algorithm""" """Initialisation of the Proportional Algorithm"""
_LOGGER.debug( _LOGGER.debug(
"Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d", "Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d, minimal_activation_delay:%d",
function_type, function_type,
tpi_coef_int, tpi_coef_int,
tpi_coef_ext, tpi_coef_ext,
cycle_min, cycle_min,
minimal_activation_delay,
) )
# TODO test function_type, bias, cycle_min
self._function = function_type self._function = function_type
self._tpi_coef_int = tpi_coef_int self._tpi_coef_int = tpi_coef_int
self._tpi_coef_ext = tpi_coef_ext self._tpi_coef_ext = tpi_coef_ext
self._cycle_min = cycle_min self._cycle_min = cycle_min
self._minimal_activation_delay = minimal_activation_delay
self._on_percent = 0 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
@@ -74,12 +75,19 @@ class PropAlgorithm:
self._on_time_sec = self._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 < self._minimal_activation_delay:
_LOGGER.debug( if self._on_time_sec > 0:
"No heating period due to heating period too small (%f < %f)", _LOGGER.info(
self._on_time_sec, "No heating period due to heating period too small (%f < %f)",
PROPORTIONAL_MIN_DURATION_SEC, self._on_time_sec,
) self._minimal_activation_delay,
)
else:
_LOGGER.debug(
"No heating period due to heating period too small (%f < %f)",
self._on_time_sec,
self._minimal_activation_delay,
)
self._on_time_sec = 0 self._on_time_sec = 0
self._off_time_sec = self._cycle_min * 60 - self._on_time_sec self._off_time_sec = self._cycle_min * 60 - self._on_time_sec

View File

@@ -12,7 +12,9 @@
"temperature_sensor_entity_id": "Temperature sensor entity id", "temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id", "external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)", "cycle_min": "Cycle duration (minutes)",
"proportional_function": "Algorithm to use (TPI is the only one for now)" "proportional_function": "Algorithm to use (TPI is the only one for now)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed"
} }
}, },
"tpi": { "tpi": {
@@ -69,6 +71,14 @@
"comfort_away_temp": "Temperature in Comfort preset when no presence", "comfort_away_temp": "Temperature in Comfort preset when no presence",
"boost_away_temp": "Temperature in Boost preset when no presence" "boost_away_temp": "Temperature in Boost preset when no presence"
} }
},
"advanced": {
"title": "Advanced parameters",
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
"data": {
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
}
} }
}, },
"error": { "error": {
@@ -91,7 +101,9 @@
"temperature_sensor_entity_id": "Temperature sensor entity id", "temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id", "external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)", "cycle_min": "Cycle duration (minutes)",
"proportional_function": "Algorithm to use (TPI is the only one for now)" "proportional_function": "Algorithm to use (TPI is the only one for now)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed"
} }
}, },
"tpi": { "tpi": {
@@ -148,6 +160,14 @@
"comfort_away_temp": "Temperature in Comfort preset when no presence", "comfort_away_temp": "Temperature in Comfort preset when no presence",
"boost_away_temp": "Temperature in Boost preset when no presence" "boost_away_temp": "Temperature in Boost preset when no presence"
} }
},
"advanced": {
"title": "Advanced parameters",
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
"data": {
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
}
} }
}, },
"error": { "error": {

View File

@@ -12,7 +12,9 @@
"temperature_sensor_entity_id": "Temperature sensor entity id", "temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id", "external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)", "cycle_min": "Cycle duration (minutes)",
"proportional_function": "Algorithm to use (TPI is the only one for now)" "proportional_function": "Algorithm to use (TPI is the only one for now)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed"
} }
}, },
"tpi": { "tpi": {
@@ -69,6 +71,14 @@
"comfort_away_temp": "Temperature in Comfort preset when no presence", "comfort_away_temp": "Temperature in Comfort preset when no presence",
"boost_away_temp": "Temperature in Boost preset when no presence" "boost_away_temp": "Temperature in Boost preset when no presence"
} }
},
"advanced": {
"title": "Advanced parameters",
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
"data": {
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
}
} }
}, },
"error": { "error": {
@@ -91,7 +101,9 @@
"temperature_sensor_entity_id": "Temperature sensor entity id", "temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id", "external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)", "cycle_min": "Cycle duration (minutes)",
"proportional_function": "Algorithm to use (TPI is the only one for now)" "proportional_function": "Algorithm to use (TPI is the only one for now)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed"
} }
}, },
"tpi": { "tpi": {
@@ -148,6 +160,14 @@
"comfort_away_temp": "Temperature in Comfort preset when no presence", "comfort_away_temp": "Temperature in Comfort preset when no presence",
"boost_away_temp": "Temperature in Boost preset when no presence" "boost_away_temp": "Temperature in Boost preset when no presence"
} }
},
"advanced": {
"title": "Advanced parameters",
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
"data": {
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
}
} }
}, },
"error": { "error": {

View File

@@ -12,7 +12,9 @@
"temperature_sensor_entity_id": "Température sensor entity id", "temperature_sensor_entity_id": "Température sensor entity id",
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id", "external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
"cycle_min": "Durée du cycle (minutes)", "cycle_min": "Durée du cycle (minutes)",
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)" "proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
"temp_min": "Température minimale permise",
"temp_max": "Température maximale permise"
} }
}, },
"tpi": { "tpi": {
@@ -69,6 +71,14 @@
"comfort_away_temp": "Température en preset Comfort en cas d'absence", "comfort_away_temp": "Température en preset Comfort en cas d'absence",
"boost_away_temp": "Température en preset Boost en cas d'absence" "boost_away_temp": "Température en preset Boost en cas d'absence"
} }
},
"advanced": {
"title": "Parameters avancés",
"description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat.",
"data": {
"minimal_activation_delay": "Délai en seondes en-dessous duquel l'équipement ne sera pas activé",
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position éteinte de sécurité"
}
} }
}, },
"error": { "error": {
@@ -91,7 +101,9 @@
"temperature_sensor_entity_id": "Température sensor entity id", "temperature_sensor_entity_id": "Température sensor entity id",
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id", "external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
"cycle_min": "Durée du cycle (minutes)", "cycle_min": "Durée du cycle (minutes)",
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)" "proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
"temp_min": "Température minimale permise",
"temp_max": "Température maximale permise"
} }
}, },
"tpi": { "tpi": {
@@ -148,6 +160,14 @@
"comfort_away_temp": "Température en preset Comfort en cas d'absence", "comfort_away_temp": "Température en preset Comfort en cas d'absence",
"boost_away_temp": "Température en preset Boost en cas d'absence" "boost_away_temp": "Température en preset Boost en cas d'absence"
} }
},
"advanced": {
"title": "Parameters avancés",
"description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat.",
"data": {
"minimal_activation_delay": "Délai en seondes en-dessous duquel l'équipement ne sera pas activé",
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position éteinte de sécurité"
}
} }
}, },
"error": { "error": {