diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index cb4393d..147f824 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -54,6 +54,7 @@
"python.analysis.autoSearchPaths": true,
"pylint.lintOnChange": false,
"python.formatting.provider": "black",
+ "python.formatting.blackArgs": ["--line-length", "180"],
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..bf4c766
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,2 @@
+[FORMAT]
+max-line-length=180
\ No newline at end of file
diff --git a/README-fr.md b/README-fr.md
index f812399..cffe0e3 100644
--- a/README-fr.md
+++ b/README-fr.md
@@ -39,27 +39,28 @@ Un grand merci à tous mes fournisseurs de bières pour leurs dons et leurs enco
La documentation est maintenant découpée en plusieurs pages pour faciliter la lecture et la recherche d'informations :
1. [présentation](documentation/fr/presentation.md),
-2. [choisir un type de VTherm](documentation/fr/creation.md),
-3. [les attributs de base](documentation/fr/base-attributes.md)
-3. [configurer un VTherm sur un `switch`](documentation/fr/over-switch.md)
-3. [configurer un VTherm sur un `climate`](documentation/fr/over-climate.md)
-3. [configurer un VTherm sur une vanne](documentation/fr/over-valve.md)
-4. [les pré-régages (preset)](documentation/fr/feature-presets.md)
-5. [la gestion des ouvertures](documentation/fr/feature-window.md)
-6. [la gestion de la présence](documentation/fr/feature-presence.md)
-7. [la gestion de mouvement](documentation/fr/feature-motion.md)
-8. [la gestion de la puissance](documentation/fr/feature-power.md)
-9. [l'auto start and stop](documentation/fr/feature-auto-start-stop.md)
-10. [la contrôle centralisé de tous vos VTherms](documentation/fr/feature-central-mode.md)
-11. [la commande du chauffage central](documentation/fr/feature-central-boiler.md)
-12. [aspects avancés, mode sécurité](documentation/fr/feature-advanced.md)
-12. [l'auto-régulation](documentation/fr/self-regulation.md)
-13. [exemples de réglages](documentation/fr/tuning-examples.md)
-14. [les différents algorithmes](documentation/fr/algorithms.md)
-15. [documentation de référence](documentation/fr/reference.md)
-16. [exemple de réglages](documentation/fr/tuning-examples.md)
-17. [dépannage](documentation/fr/troubleshooting.md)
-18. [notes de version](documentation/fr/releases.md)
+2. [Installation](documentation/fr/installation.md),
+3. [choisir un type de VTherm](documentation/fr/creation.md),
+4. [les attributs de base](documentation/fr/base-attributes.md)
+5. [configurer un VTherm sur un `switch`](documentation/fr/over-switch.md)
+6. [configurer un VTherm sur un `climate`](documentation/fr/over-climate.md)
+7. [configurer un VTherm sur une vanne](documentation/fr/over-valve.md)
+8. [les pré-régages (preset)](documentation/fr/feature-presets.md)
+9. [la gestion des ouvertures](documentation/fr/feature-window.md)
+10. [la gestion de la présence](documentation/fr/feature-presence.md)
+11. [la gestion de mouvement](documentation/fr/feature-motion.md)
+12. [la gestion de la puissance](documentation/fr/feature-power.md)
+13. [l'auto start and stop](documentation/fr/feature-auto-start-stop.md)
+14. [la contrôle centralisé de tous vos VTherms](documentation/fr/feature-central-mode.md)
+15. [la commande du chauffage central](documentation/fr/feature-central-boiler.md)
+16. [aspects avancés, mode sécurité](documentation/fr/feature-advanced.md)
+17. [l'auto-régulation](documentation/fr/self-regulation.md)
+18. [exemples de réglages](documentation/fr/tuning-examples.md)
+19. [les différents algorithmes](documentation/fr/algorithms.md)
+20. [documentation de référence](documentation/fr/reference.md)
+21. [exemple de réglages](documentation/fr/tuning-examples.md)
+22. [dépannage](documentation/fr/troubleshooting.md)
+23. [notes de version](documentation/fr/releases.md)
# Quelques résultats
diff --git a/custom_components/versatile_thermostat/base_manager.py b/custom_components/versatile_thermostat/base_manager.py
index a2c6975..c285fe4 100644
--- a/custom_components/versatile_thermostat/base_manager.py
+++ b/custom_components/versatile_thermostat/base_manager.py
@@ -16,10 +16,10 @@ _LOGGER = logging.getLogger(__name__)
class BaseFeatureManager:
"""A base class for all feature"""
- def __init__(self, vtherm: Any, hass: HomeAssistant):
+ def __init__(self, vtherm: Any, hass: HomeAssistant, name: str = None):
"""Init of a featureManager"""
self._vtherm = vtherm
- self._name = vtherm.name
+ self._name = vtherm.name if vtherm else name
self._active_listener: list[CALLBACK_TYPE] = []
self._hass = hass
@@ -27,7 +27,7 @@ class BaseFeatureManager:
"""Initialize the attributes of the FeatureManager"""
raise NotImplementedError()
- def start_listening(self):
+ async def start_listening(self):
"""Start listening the underlying entity"""
raise NotImplementedError()
@@ -38,6 +38,10 @@ class BaseFeatureManager:
self._active_listener = []
+ async def refresh_state(self):
+ """Refresh the state and return True if a change have been made"""
+ return False
+
def add_listener(self, func: CALLBACK_TYPE) -> None:
"""Add a listener to the list of active listener"""
self._active_listener.append(func)
diff --git a/custom_components/versatile_thermostat/base_thermostat.py b/custom_components/versatile_thermostat/base_thermostat.py
index 83d66bd..4dc4c25 100644
--- a/custom_components/versatile_thermostat/base_thermostat.py
+++ b/custom_components/versatile_thermostat/base_thermostat.py
@@ -50,7 +50,6 @@ from homeassistant.const import (
ATTR_TEMPERATURE,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
- STATE_ON,
)
from .const import * # pylint: disable=wildcard-import, unused-wildcard-import
@@ -99,7 +98,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
"comfort_away_temp",
"power_temp",
"ac_mode",
- "current_max_power",
"saved_preset_mode",
"saved_target_temp",
"saved_hvac_mode",
@@ -191,7 +189,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self._ema_temp = None
self._ema_algo = None
- self._now = None
self._attr_fan_mode = None
@@ -446,10 +443,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
)
)
- # start listening for all managers
- for manager in self._managers:
- manager.start_listening()
-
self.async_on_remove(self.remove_thermostat)
# issue 428. Link to others entities will start at link
@@ -482,6 +475,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
_LOGGER.debug("%s - Calling async_startup_internal", self)
need_write_state = False
+ # start listening for all managers
+ for manager in self._managers:
+ await manager.start_listening()
+
await self.get_my_previous_state()
await self.init_presets(central_configuration)
@@ -1454,7 +1451,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
control_heating to turn all off"""
for under in self._underlyings:
- await under.turn_off()
+ await under.turn_off_and_cancel_cycle()
def save_preset_mode(self):
"""Save the current preset mode to be restored later
@@ -1466,7 +1463,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
):
self._saved_preset_mode = self._attr_preset_mode
- async def restore_preset_mode(self):
+ async def restore_preset_mode(self, force=False):
"""Restore a previous preset mode
We never restore a hidden preset mode. Normally that is not possible
"""
@@ -1474,7 +1471,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self._saved_preset_mode not in HIDDEN_PRESETS
and self._saved_preset_mode is not None
):
- await self.async_set_preset_mode_internal(self._saved_preset_mode)
+ await self.async_set_preset_mode_internal(self._saved_preset_mode, force=force)
def save_hvac_mode(self):
"""Save the current hvac-mode to be restored later"""
@@ -1582,15 +1579,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
await self.async_set_hvac_mode(HVACMode.OFF)
return
- def _set_now(self, now: datetime):
- """Set the now timestamp. This is only for tests purpose"""
- self._now = now
-
- @property
- def now(self) -> datetime:
- """Get now. The local datetime or the overloaded _set_now date"""
- return self._now if self._now is not None else NowClass.get_now(self._hass)
-
@property
def is_initialized(self) -> bool:
"""Check if all underlyings are initialized
@@ -1620,11 +1608,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
return False
# Check overpowering condition
- # Not necessary for switch because each switch is checking at startup
- overpowering = await self._power_manager.check_overpowering()
- if overpowering == STATE_ON:
- _LOGGER.debug("%s - End of cycle (overpowering)", self)
- return True
+ # Not usefull. Will be done at the next power refresh
+ # await VersatileThermostatAPI.get_vtherm_api().central_power_manager.refresh_state()
safety: bool = await self._safety_manager.refresh_state()
if safety and self.is_over_climate:
@@ -1965,3 +1950,34 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
def is_preset_configured(self, preset) -> bool:
"""Returns True if the preset in argument is configured"""
return self._presets.get(preset, None) is not None
+
+ # For testing purpose
+ # @deprecated
+ def _set_now(self, now: datetime):
+ """Set the now timestamp. This is only for tests purpose
+ This method should be replaced by the vthermAPI equivalent"""
+ VersatileThermostatAPI.get_vtherm_api(self._hass)._set_now(now) # pylint: disable=protected-access
+
+ # @deprecated
+ @property
+ def now(self) -> datetime:
+ """Get now. The local datetime or the overloaded _set_now date
+ This method should be replaced by the vthermAPI equivalent"""
+ return VersatileThermostatAPI.get_vtherm_api(self._hass).now
+
+ @property
+ def power_percent(self) -> float | None:
+ """Get the current on_percent as a percentage value. valid only for Vtherm with a TPI algo
+ Get the current on_percent value"""
+ if self._prop_algorithm and self._prop_algorithm.on_percent is not None:
+ return round(self._prop_algorithm.on_percent * 100, 0)
+ else:
+ return None
+
+ @property
+ def on_percent(self) -> float | None:
+ """Get the current on_percent value. valid only for Vtherm with a TPI algo"""
+ if self._prop_algorithm and self._prop_algorithm.on_percent is not None:
+ return self._prop_algorithm.on_percent
+ else:
+ return None
diff --git a/custom_components/versatile_thermostat/central_feature_power_manager.py b/custom_components/versatile_thermostat/central_feature_power_manager.py
new file mode 100644
index 0000000..8762413
--- /dev/null
+++ b/custom_components/versatile_thermostat/central_feature_power_manager.py
@@ -0,0 +1,309 @@
+""" Implements a central Power Feature Manager for Versatile Thermostat """
+
+import logging
+from typing import Any
+from functools import cmp_to_key
+
+from datetime import timedelta
+
+from homeassistant.const import STATE_OFF
+from homeassistant.core import HomeAssistant, Event, callback
+from homeassistant.helpers.event import (
+ async_track_state_change_event,
+ EventStateChangedData,
+ async_call_later,
+)
+from homeassistant.helpers.entity_component import EntityComponent
+from homeassistant.components.climate import (
+ ClimateEntity,
+ DOMAIN as CLIMATE_DOMAIN,
+)
+
+
+from .const import * # pylint: disable=wildcard-import, unused-wildcard-import
+from .commons import ConfigData
+from .base_manager import BaseFeatureManager
+
+# circular dependency
+# from .base_thermostat import BaseThermostat
+
+MIN_DTEMP_SECS = 20
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class CentralFeaturePowerManager(BaseFeatureManager):
+ """A central Power feature manager"""
+
+ def __init__(self, hass: HomeAssistant, vtherm_api: Any):
+ """Init of a featureManager"""
+ super().__init__(None, hass, "centralPowerManager")
+ self._hass: HomeAssistant = hass
+ self._vtherm_api = vtherm_api # no type due to circular reference
+ self._is_configured: bool = False
+ self._power_sensor_entity_id: str = None
+ self._max_power_sensor_entity_id: str = None
+ self._current_power: float = None
+ self._current_max_power: float = None
+ self._power_temp: float = None
+ self._cancel_calculate_shedding_call = None
+ # Not used now
+ self._last_shedding_date = None
+
+ def post_init(self, entry_infos: ConfigData):
+ """Gets the configuration parameters"""
+ central_config = self._vtherm_api.find_central_configuration()
+ if not central_config:
+ _LOGGER.info("No central configuration is found. Power management will be deactivated")
+ return
+
+ self._power_sensor_entity_id = entry_infos.get(CONF_POWER_SENSOR)
+ self._max_power_sensor_entity_id = entry_infos.get(CONF_MAX_POWER_SENSOR)
+ self._power_temp = entry_infos.get(CONF_PRESET_POWER)
+
+ self._is_configured = False
+ self._current_power = None
+ self._current_max_power = None
+ if (
+ entry_infos.get(CONF_USE_POWER_FEATURE, False)
+ and self._max_power_sensor_entity_id
+ and self._power_sensor_entity_id
+ and self._power_temp
+ ):
+ self._is_configured = True
+ else:
+ _LOGGER.info("Power management is not fully configured and will be deactivated")
+
+ async def start_listening(self):
+ """Start listening the power sensor"""
+ if not self._is_configured:
+ return
+
+ self.stop_listening()
+
+ self.add_listener(
+ async_track_state_change_event(
+ self.hass,
+ [self._power_sensor_entity_id],
+ self._power_sensor_changed,
+ )
+ )
+
+ self.add_listener(
+ async_track_state_change_event(
+ self.hass,
+ [self._max_power_sensor_entity_id],
+ self._max_power_sensor_changed,
+ )
+ )
+
+ @callback
+ async def _power_sensor_changed(self, event: Event[EventStateChangedData]):
+ """Handle power changes."""
+ _LOGGER.debug("Receive new Power event")
+ _LOGGER.debug(event)
+ await self.refresh_state()
+
+ @callback
+ async def _max_power_sensor_changed(self, event: Event[EventStateChangedData]):
+ """Handle power max changes."""
+ _LOGGER.debug("Receive new Power Max event")
+ _LOGGER.debug(event)
+ await self.refresh_state()
+
+ @overrides
+ async def refresh_state(self) -> bool:
+ """Tries to get the last state from sensor
+ Returns True if a change has been made"""
+
+ async def _calculate_shedding_internal(_):
+ _LOGGER.debug("Do the shedding calculation")
+ await self.calculate_shedding()
+ if self._cancel_calculate_shedding_call:
+ self._cancel_calculate_shedding_call()
+ self._cancel_calculate_shedding_call = None
+
+ if not self._is_configured:
+ return False
+
+ # Retrieve current power
+ new_power = get_safe_float(self._hass, self._power_sensor_entity_id)
+ power_changed = new_power is not None and self._current_power != new_power
+ if power_changed:
+ self._current_power = new_power
+ _LOGGER.debug("New current power has been retrieved: %.3f", self._current_power)
+
+ # Retrieve max power
+ new_max_power = get_safe_float(self._hass, self._max_power_sensor_entity_id)
+ max_power_changed = new_max_power is not None and self._current_max_power != new_max_power
+ if max_power_changed:
+ self._current_max_power = new_max_power
+ _LOGGER.debug("New current max power has been retrieved: %.3f", self._current_max_power)
+
+ # Schedule shedding calculation if there's any change
+ if power_changed or max_power_changed:
+ if not self._cancel_calculate_shedding_call:
+ self._cancel_calculate_shedding_call = async_call_later(self.hass, timedelta(seconds=MIN_DTEMP_SECS), _calculate_shedding_internal)
+ return True
+
+ return False
+
+ # For testing purpose only, do an immediate shedding calculation
+ async def _do_immediate_shedding(self):
+ """Do an immmediate shedding calculation if a timer was programmed.
+ Else, do nothing"""
+ if self._cancel_calculate_shedding_call:
+ self._cancel_calculate_shedding_call()
+ self._cancel_calculate_shedding_call = None
+ await self.calculate_shedding()
+
+ async def calculate_shedding(self):
+ """Do the shedding calculation and set/unset VTherm into overpowering state"""
+ if not self.is_configured or self.current_max_power is None or self.current_power is None:
+ return
+
+ _LOGGER.debug("-------- Start of calculate_shedding")
+ # Find all VTherms
+ available_power = self.current_max_power - self.current_power
+ vtherms_sorted = self.find_all_vtherm_with_power_management_sorted_by_dtemp()
+
+ # shedding only
+ if available_power < 0:
+ _LOGGER.debug(
+ "The available power is is < 0 (%s). Set overpowering only for list: %s",
+ available_power,
+ vtherms_sorted,
+ )
+ # we will set overpowering for the nearest target temp first
+ total_power_gain = 0
+
+ for vtherm in vtherms_sorted:
+ if vtherm.is_device_active and not vtherm.power_manager.is_overpowering_detected:
+ device_power = vtherm.power_manager.device_power
+ total_power_gain += device_power
+ _LOGGER.info("vtherm %s should be in overpowering state (device_power=%.2f)", vtherm.name, device_power)
+ await vtherm.power_manager.set_overpowering(True, device_power)
+
+ _LOGGER.debug("after vtherm %s total_power_gain=%s, available_power=%s", vtherm.name, total_power_gain, available_power)
+ if total_power_gain >= -available_power:
+ _LOGGER.debug("We have found enough vtherm to set to overpowering")
+ break
+ # unshedding only
+ else:
+ vtherms_sorted.reverse()
+ _LOGGER.debug("The available power is is > 0 (%s). Do a complete shedding/un-shedding calculation for list: %s", available_power, vtherms_sorted)
+
+ total_power_added = 0
+
+ for vtherm in vtherms_sorted:
+ # We want to do always unshedding in order to initialize the state
+ # so we cannot use is_overpowering_detected which test also UNKNOWN and UNAVAILABLE
+ if vtherm.power_manager.overpowering_state == STATE_OFF:
+ continue
+
+ power_consumption_max = device_power = vtherm.power_manager.device_power
+ # calculate the power_consumption_max
+ if vtherm.on_percent is not None:
+ power_consumption_max = max(
+ device_power / vtherm.nb_underlying_entities,
+ device_power * vtherm.on_percent,
+ )
+
+ _LOGGER.debug("vtherm %s power_consumption_max is %s (device_power=%s, overclimate=%s)", vtherm.name, power_consumption_max, device_power, vtherm.is_over_climate)
+
+ # or not ... is for initializing the overpowering state if not already done
+ if total_power_added + power_consumption_max < available_power or not vtherm.power_manager.is_overpowering_detected:
+ # we count the unshedding only if the VTherm was in shedding
+ if vtherm.power_manager.is_overpowering_detected:
+ _LOGGER.info("vtherm %s should not be in overpowering state (power_consumption_max=%.2f)", vtherm.name, power_consumption_max)
+ total_power_added += power_consumption_max
+
+ await vtherm.power_manager.set_overpowering(False)
+
+ if total_power_added >= available_power:
+ _LOGGER.debug("We have found enough vtherm to set to non-overpowering")
+ break
+
+ _LOGGER.debug("after vtherm %s total_power_added=%s, available_power=%s", vtherm.name, total_power_added, available_power)
+
+ self._last_shedding_date = self._vtherm_api.now
+ _LOGGER.debug("-------- End of calculate_shedding")
+
+ def get_climate_components_entities(self) -> list:
+ """Get all VTherms entitites"""
+ vtherms = []
+ component: EntityComponent[ClimateEntity] = self._hass.data.get(
+ CLIMATE_DOMAIN, None
+ )
+ if component:
+ for entity in component.entities:
+ # A little hack to test if the climate is a VTherm. Cannot use isinstance
+ # due to circular dependency of BaseThermostat
+ if (
+ entity.device_info
+ and entity.device_info.get("model", None) == DOMAIN
+ ):
+ vtherms.append(entity)
+ return vtherms
+
+ def find_all_vtherm_with_power_management_sorted_by_dtemp(
+ self,
+ ) -> list:
+ """Returns all the VTherms with power management activated"""
+ entities = self.get_climate_components_entities()
+ vtherms = [
+ vtherm
+ for vtherm in entities
+ if vtherm.power_manager.is_configured and vtherm.is_on
+ ]
+
+ # sort the result with the min temp difference first. A and B should be BaseThermostat class
+ def cmp_temps(a, b) -> int:
+ diff_a = float("inf")
+ diff_b = float("inf")
+ a_target = a.target_temperature if not a.power_manager.is_overpowering_detected else a.saved_target_temp
+ b_target = b.target_temperature if not b.power_manager.is_overpowering_detected else b.saved_target_temp
+ if a.current_temperature is not None and a_target is not None:
+ diff_a = a_target - a.current_temperature
+ if b.current_temperature is not None and b_target is not None:
+ diff_b = b_target - b.current_temperature
+
+ if diff_a == diff_b:
+ return 0
+ return 1 if diff_a > diff_b else -1
+
+ vtherms.sort(key=cmp_to_key(cmp_temps))
+ return vtherms
+
+ @property
+ def is_configured(self) -> bool:
+ """True if the FeatureManager is fully configured"""
+ return self._is_configured
+
+ @property
+ def current_power(self) -> float | None:
+ """Return the current power from sensor"""
+ return self._current_power
+
+ @property
+ def current_max_power(self) -> float | None:
+ """Return the current power from sensor"""
+ return self._current_max_power
+
+ @property
+ def power_temperature(self) -> float | None:
+ """Return the power temperature"""
+ return self._power_temp
+
+ @property
+ def power_sensor_entity_id(self) -> float | None:
+ """Return the power sensor entity id"""
+ return self._power_sensor_entity_id
+
+ @property
+ def max_power_sensor_entity_id(self) -> float | None:
+ """Return the max power sensor entity id"""
+ return self._max_power_sensor_entity_id
+
+ def __str__(self):
+ return "CentralPowerManager"
diff --git a/custom_components/versatile_thermostat/climate.py b/custom_components/versatile_thermostat/climate.py
index 61334d8..69b5628 100644
--- a/custom_components/versatile_thermostat/climate.py
+++ b/custom_components/versatile_thermostat/climate.py
@@ -28,6 +28,7 @@ from .thermostat_switch import ThermostatOverSwitch
from .thermostat_climate import ThermostatOverClimate
from .thermostat_valve import ThermostatOverValve
from .thermostat_climate_valve import ThermostatOverClimateValve
+from .vtherm_api import VersatileThermostatAPI
_LOGGER = logging.getLogger(__name__)
@@ -51,6 +52,9 @@ async def async_setup_entry(
)
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
+ # Initialize the central power manager
+ vtherm_api = VersatileThermostatAPI.get_vtherm_api(hass)
+ vtherm_api.central_power_manager.post_init(entry.data)
return
# Instantiate the right base class
diff --git a/custom_components/versatile_thermostat/commons.py b/custom_components/versatile_thermostat/commons.py
index 6018d01..4242e5d 100644
--- a/custom_components/versatile_thermostat/commons.py
+++ b/custom_components/versatile_thermostat/commons.py
@@ -3,6 +3,7 @@
# pylint: disable=line-too-long
import logging
+import warnings
from types import MappingProxyType
from typing import Any, TypeVar
@@ -132,3 +133,20 @@ def check_and_extract_service_configuration(service_config) -> dict:
"check_and_extract_service_configuration(%s) gives '%s'", service_config, ret
)
return ret
+
+
+def deprecated(message):
+ """A decorator to indicate that the method/attribut is deprecated"""
+
+ def decorator(func):
+ def wrapper(*args, **kwargs):
+ warnings.warn(
+ f"{func.__name__} is deprecated: {message}",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return func(*args, **kwargs)
+
+ return wrapper
+
+ return decorator
diff --git a/custom_components/versatile_thermostat/config_flow.py b/custom_components/versatile_thermostat/config_flow.py
index 9190647..4cbbe0a 100644
--- a/custom_components/versatile_thermostat/config_flow.py
+++ b/custom_components/versatile_thermostat/config_flow.py
@@ -90,11 +90,10 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
CONF_USE_MOTION_FEATURE, False
) and (self._infos.get(CONF_MOTION_SENSOR) is not None or is_central_config)
- self._infos[CONF_USE_POWER_FEATURE] = self._infos.get(
- CONF_USE_POWER_CENTRAL_CONFIG, False
- ) or (
- self._infos.get(CONF_POWER_SENSOR) is not None
- and self._infos.get(CONF_MAX_POWER_SENSOR) is not None
+ self._infos[CONF_USE_POWER_FEATURE] = (
+ self._infos.get(CONF_USE_POWER_CENTRAL_CONFIG, False)
+ or self._infos.get(CONF_USE_POWER_FEATURE, False)
+ or (is_central_config and self._infos.get(CONF_POWER_SENSOR) is not None and self._infos.get(CONF_MAX_POWER_SENSOR) is not None)
)
self._infos[CONF_USE_PRESENCE_FEATURE] = (
self._infos.get(CONF_USE_PRESENCE_CENTRAL_CONFIG, False)
@@ -184,7 +183,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
Data has the keys from STEP_*_DATA_SCHEMA with values provided by the user.
"""
- # check the heater_entity_id
+ # check the entity_ids
for conf in [
CONF_UNDERLYING_LIST,
CONF_TEMP_SENSOR,
@@ -330,14 +329,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
):
return False
- if (
- infos.get(CONF_USE_POWER_FEATURE, False) is True
- and infos.get(CONF_USE_POWER_CENTRAL_CONFIG, False) is False
- and (
- infos.get(CONF_POWER_SENSOR, None) is None
- or infos.get(CONF_MAX_POWER_SENSOR, None) is None
- )
- ):
+ if infos.get(CONF_USE_POWER_FEATURE, False) is True and infos.get(CONF_USE_POWER_CENTRAL_CONFIG, False) is False and infos.get(CONF_PRESET_POWER, None) is None:
return False
if (
@@ -815,7 +807,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
"""Handle the specific power flow steps"""
_LOGGER.debug("Into ConfigFlow.async_step_spec_power user_input=%s", user_input)
- schema = STEP_CENTRAL_POWER_DATA_SCHEMA
+ schema = STEP_NON_CENTRAL_POWER_DATA_SCHEMA
self._infos[COMES_FROM] = "async_step_spec_power"
diff --git a/custom_components/versatile_thermostat/config_schema.py b/custom_components/versatile_thermostat/config_schema.py
index 90e4121..8a23db4 100644
--- a/custom_components/versatile_thermostat/config_schema.py
+++ b/custom_components/versatile_thermostat/config_schema.py
@@ -339,6 +339,12 @@ STEP_CENTRAL_POWER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
}
)
+STEP_NON_CENTRAL_POWER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
+ {
+ vol.Optional(CONF_PRESET_POWER, default="13"): vol.Coerce(float),
+ }
+)
+
STEP_POWER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
{
vol.Required(CONF_USE_POWER_CENTRAL_CONFIG, default=True): cv.boolean,
diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py
index 9f14082..be1fc46 100644
--- a/custom_components/versatile_thermostat/const.py
+++ b/custom_components/versatile_thermostat/const.py
@@ -503,6 +503,8 @@ def get_safe_float(hass, entity_id: str):
if (
entity_id is None
or not (state := hass.states.get(entity_id))
+ or state.state is None
+ or state.state == "None"
or state.state == "unknown"
or state.state == "unavailable"
):
diff --git a/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py b/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py
index 3880b36..8708563 100644
--- a/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py
+++ b/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py
@@ -71,7 +71,7 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
)
@overrides
- def start_listening(self):
+ async def start_listening(self):
"""Start listening the underlying entity"""
@overrides
diff --git a/custom_components/versatile_thermostat/feature_motion_manager.py b/custom_components/versatile_thermostat/feature_motion_manager.py
index 6232386..deaf678 100644
--- a/custom_components/versatile_thermostat/feature_motion_manager.py
+++ b/custom_components/versatile_thermostat/feature_motion_manager.py
@@ -86,7 +86,7 @@ class FeatureMotionManager(BaseFeatureManager):
self._motion_state = STATE_UNKNOWN
@overrides
- def start_listening(self):
+ async def start_listening(self):
"""Start listening the underlying entity"""
if self._is_configured:
self.stop_listening()
diff --git a/custom_components/versatile_thermostat/feature_power_manager.py b/custom_components/versatile_thermostat/feature_power_manager.py
index 7af6bc9..8642d78 100644
--- a/custom_components/versatile_thermostat/feature_power_manager.py
+++ b/custom_components/versatile_thermostat/feature_power_manager.py
@@ -12,22 +12,15 @@ from homeassistant.const import (
STATE_UNKNOWN,
)
-
from homeassistant.core import (
HomeAssistant,
- callback,
- Event,
)
-from homeassistant.helpers.event import (
- async_track_state_change_event,
- EventStateChangedData,
-)
-from homeassistant.components.climate import HVACMode
from .const import * # pylint: disable=wildcard-import, unused-wildcard-import
from .commons import ConfigData
from .base_manager import BaseFeatureManager
+from .vtherm_api import VersatileThermostatAPI
_LOGGER = logging.getLogger(__name__)
@@ -50,194 +43,94 @@ class FeaturePowerManager(BaseFeatureManager):
def __init__(self, vtherm: Any, hass: HomeAssistant):
"""Init of a featureManager"""
super().__init__(vtherm, hass)
- self._power_sensor_entity_id = None
- self._max_power_sensor_entity_id = None
- self._current_power = None
- self._current_max_power = None
self._power_temp = None
self._overpowering_state = STATE_UNAVAILABLE
self._is_configured: bool = False
self._device_power: float = 0
+ self._use_power_feature: bool = False
@overrides
def post_init(self, entry_infos: ConfigData):
"""Reinit of the manager"""
# Power management
- self._power_sensor_entity_id = entry_infos.get(CONF_POWER_SENSOR)
- self._max_power_sensor_entity_id = entry_infos.get(CONF_MAX_POWER_SENSOR)
self._power_temp = entry_infos.get(CONF_PRESET_POWER)
self._device_power = entry_infos.get(CONF_DEVICE_POWER) or 0
+ self._use_power_feature = entry_infos.get(CONF_USE_POWER_FEATURE, False)
self._is_configured = False
- self._current_power = None
- self._current_max_power = None
- if (
- entry_infos.get(CONF_USE_POWER_FEATURE, False)
- and self._max_power_sensor_entity_id
- and self._power_sensor_entity_id
- and self._device_power
- ):
+
+ @overrides
+ async def start_listening(self):
+ """Start listening the underlying entity. There is nothing to listen"""
+ central_power_configuration = (
+ VersatileThermostatAPI.get_vtherm_api().central_power_manager.is_configured
+ )
+
+ if self._use_power_feature and self._device_power and central_power_configuration:
self._is_configured = True
- self._overpowering_state = STATE_UNKNOWN
+ # Try to restore _overpowering_state from previous state
+ old_state = await self._vtherm.async_get_last_state()
+ self._overpowering_state = STATE_ON if old_state and old_state.attributes and old_state.attributes.get("overpowering_state") == STATE_ON else STATE_UNKNOWN
else:
- _LOGGER.info("%s - Power management is not fully configured", self)
-
- @overrides
- def start_listening(self):
- """Start listening the underlying entity"""
- if self._is_configured:
- self.stop_listening()
- else:
- return
-
- self.add_listener(
- async_track_state_change_event(
- self.hass,
- [self._power_sensor_entity_id],
- self._async_power_sensor_changed,
- )
- )
-
- self.add_listener(
- async_track_state_change_event(
- self.hass,
- [self._max_power_sensor_entity_id],
- self._async_max_power_sensor_changed,
- )
- )
-
- @overrides
- async def refresh_state(self) -> bool:
- """Tries to get the last state from sensor
- Returns True if a change has been made"""
- ret = False
- if self._is_configured:
- # try to acquire current power and power max
- current_power_state = self.hass.states.get(self._power_sensor_entity_id)
- if current_power_state and current_power_state.state not in (
- STATE_UNAVAILABLE,
- STATE_UNKNOWN,
- ):
- self._current_power = float(current_power_state.state)
- _LOGGER.debug(
- "%s - Current power have been retrieved: %.3f",
- self,
- self._current_power,
- )
- ret = True
-
- # Try to acquire power max
- current_power_max_state = self.hass.states.get(
- self._max_power_sensor_entity_id
- )
- if current_power_max_state and current_power_max_state.state not in (
- STATE_UNAVAILABLE,
- STATE_UNKNOWN,
- ):
- self._current_max_power = float(current_power_max_state.state)
- _LOGGER.debug(
- "%s - Current power max have been retrieved: %.3f",
- self,
- self._current_max_power,
- )
- ret = True
-
- return ret
-
- @callback
- async def _async_power_sensor_changed(self, event: Event[EventStateChangedData]):
- """Handle power changes."""
- _LOGGER.debug("Thermostat %s - Receive new Power event", self)
- _LOGGER.debug(event)
- new_state = event.data.get("new_state")
- old_state = event.data.get("old_state")
- if (
- new_state is None
- or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN)
- or (old_state is not None and new_state.state == old_state.state)
- ):
- return
-
- try:
- current_power = float(new_state.state)
- if math.isnan(current_power) or math.isinf(current_power):
- raise ValueError(f"Sensor has illegal state {new_state.state}")
- self._current_power = current_power
-
- if self._vtherm.preset_mode == PRESET_POWER:
- await self._vtherm.async_control_heating()
-
- except ValueError as ex:
- _LOGGER.error("Unable to update current_power from sensor: %s", ex)
-
- @callback
- async def _async_max_power_sensor_changed(
- self, event: Event[EventStateChangedData]
- ):
- """Handle power max changes."""
- _LOGGER.debug("Thermostat %s - Receive new Power Max event", self.name)
- _LOGGER.debug(event)
- new_state = event.data.get("new_state")
- old_state = event.data.get("old_state")
- if (
- new_state is None
- or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN)
- or (old_state is not None and new_state.state == old_state.state)
- ):
- return
-
- try:
- current_power_max = float(new_state.state)
- if math.isnan(current_power_max) or math.isinf(current_power_max):
- raise ValueError(f"Sensor has illegal state {new_state.state}")
- self._current_max_power = current_power_max
- if self._vtherm.preset_mode == PRESET_POWER:
- await self._vtherm.async_control_heating()
-
- except ValueError as ex:
- _LOGGER.error("Unable to update current_power from sensor: %s", ex)
+ if self._use_power_feature:
+ if not central_power_configuration:
+ _LOGGER.warning(
+ "%s - Power management is not fully configured. You have to configure the central configuration power",
+ self,
+ )
+ else:
+ _LOGGER.warning(
+ "%s - Power management is not fully configured. You have to configure the power feature of the VTherm",
+ self,
+ )
def add_custom_attributes(self, extra_state_attributes: dict[str, Any]):
"""Add some custom attributes"""
+ vtherm_api = VersatileThermostatAPI.get_vtherm_api()
extra_state_attributes.update(
{
- "power_sensor_entity_id": self._power_sensor_entity_id,
- "max_power_sensor_entity_id": self._max_power_sensor_entity_id,
+ "power_sensor_entity_id": vtherm_api.central_power_manager.power_sensor_entity_id,
+ "max_power_sensor_entity_id": vtherm_api.central_power_manager.max_power_sensor_entity_id,
"overpowering_state": self._overpowering_state,
"is_power_configured": self._is_configured,
"device_power": self._device_power,
"power_temp": self._power_temp,
- "current_power": self._current_power,
- "current_max_power": self._current_max_power,
+ "current_power": vtherm_api.central_power_manager.current_power,
+ "current_max_power": vtherm_api.central_power_manager.current_max_power,
"mean_cycle_power": self.mean_cycle_power,
}
)
- async def check_overpowering(self) -> bool:
- """Check the overpowering condition
- Turn the preset_mode of the heater to 'power' if power conditions are exceeded
- Returns True if overpowering is 'on'
+ async def check_power_available(self) -> bool:
+ """Check if the Vtherm can be started considering overpowering.
+ Returns True if no overpowering conditions are found
"""
- if not self._is_configured:
- return False
-
+ vtherm_api = VersatileThermostatAPI.get_vtherm_api()
if (
- self._current_power is None
+ not self._is_configured
+ or not vtherm_api.central_power_manager.is_configured
+ ):
+ return True
+
+ current_power = vtherm_api.central_power_manager.current_power
+ current_max_power = vtherm_api.central_power_manager.current_max_power
+ if (
+ current_power is None
+ or current_max_power is None
or self._device_power is None
- or self._current_max_power is None
):
_LOGGER.warning(
- "%s - power not valued. check_overpowering not available", self
+ "%s - power not valued. check_power_available not available", self
)
- return False
+ return True
_LOGGER.debug(
"%s - overpowering check: power=%.3f, max_power=%.3f heater power=%.3f",
self,
- self._current_power,
- self._current_max_power,
+ current_power,
+ current_max_power,
self._device_power,
)
@@ -253,62 +146,78 @@ class FeaturePowerManager(BaseFeatureManager):
self._device_power * self._vtherm.proportional_algorithm.on_percent,
)
- ret = (self._current_power + power_consumption_max) >= self._current_max_power
- if (
- self._overpowering_state == STATE_OFF
- and ret
- and self._vtherm.hvac_mode != HVACMode.OFF
- ):
+ ret = (current_power + power_consumption_max) < current_max_power
+ if not ret:
+ _LOGGER.info(
+ "%s - there is not enough power available power=%.3f, max_power=%.3f heater power=%.3f",
+ self,
+ current_power,
+ current_max_power,
+ self._device_power,
+ )
+ return ret
+
+ async def set_overpowering(self, overpowering: bool, power_consumption_max=0):
+ """Force the overpowering state for the VTherm"""
+
+ vtherm_api = VersatileThermostatAPI.get_vtherm_api()
+ current_power = vtherm_api.central_power_manager.current_power
+ current_max_power = vtherm_api.central_power_manager.current_max_power
+
+ if overpowering and not self.is_overpowering_detected:
_LOGGER.warning(
"%s - overpowering is detected. Heater preset will be set to 'power'",
self,
)
+
+ self._overpowering_state = STATE_ON
+
if self._vtherm.is_over_climate:
self._vtherm.save_hvac_mode()
+
self._vtherm.save_preset_mode()
await self._vtherm.async_underlying_entity_turn_off()
- await self._vtherm.async_set_preset_mode_internal(PRESET_POWER)
+ await self._vtherm.async_set_preset_mode_internal(PRESET_POWER, force=True)
self._vtherm.send_event(
EventType.POWER_EVENT,
{
"type": "start",
- "current_power": self._current_power,
+ "current_power": current_power,
"device_power": self._device_power,
- "current_max_power": self._current_max_power,
+ "current_max_power": current_max_power,
"current_power_consumption": power_consumption_max,
},
)
-
- # Check if we need to remove the POWER preset
- if (
- self._overpowering_state == STATE_ON
- and not ret
- and self._vtherm.preset_mode == PRESET_POWER
- ):
+ elif not overpowering and self.is_overpowering_detected:
_LOGGER.warning(
"%s - end of overpowering is detected. Heater preset will be restored to '%s'",
self,
self._vtherm._saved_preset_mode, # pylint: disable=protected-access
)
+ self._overpowering_state = STATE_OFF
+
+ # restore state
if self._vtherm.is_over_climate:
- await self._vtherm.restore_hvac_mode(False)
+ await self._vtherm.restore_hvac_mode()
await self._vtherm.restore_preset_mode()
+ # restart cycle
+ await self._vtherm.async_control_heating(force=True)
self._vtherm.send_event(
EventType.POWER_EVENT,
{
"type": "end",
- "current_power": self._current_power,
+ "current_power": current_power,
"device_power": self._device_power,
- "current_max_power": self._current_max_power,
+ "current_max_power": current_max_power,
},
)
-
- new_overpowering_state = STATE_ON if ret else STATE_OFF
- if self._overpowering_state != new_overpowering_state:
- self._overpowering_state = new_overpowering_state
- self._vtherm.update_custom_attributes()
-
- return self._overpowering_state == STATE_ON
+ elif not overpowering and self._overpowering_state != STATE_OFF:
+ # just set to not overpowering the state which was not set
+ self._overpowering_state = STATE_OFF
+ else:
+ # Nothing to do (already in the right state)
+ return
+ self._vtherm.update_custom_attributes()
@overrides
@property
@@ -325,14 +234,9 @@ class FeaturePowerManager(BaseFeatureManager):
return self._overpowering_state
@property
- def max_power_sensor_entity_id(self) -> bool:
- """Return the power max entity id"""
- return self._max_power_sensor_entity_id
-
- @property
- def power_sensor_entity_id(self) -> bool:
- """Return the power entity id"""
- return self._power_sensor_entity_id
+ def is_overpowering_detected(self) -> str | None:
+ """Return True if the Vtherm is in overpowering state"""
+ return self._overpowering_state == STATE_ON
@property
def power_temperature(self) -> bool:
@@ -344,16 +248,6 @@ class FeaturePowerManager(BaseFeatureManager):
"""Return the device power"""
return self._device_power
- @property
- def current_power(self) -> bool:
- """Return the current power from sensor"""
- return self._current_power
-
- @property
- def current_max_power(self) -> bool:
- """Return the current power from sensor"""
- return self._current_max_power
-
@property
def mean_cycle_power(self) -> float | None:
"""Returns the mean power consumption during the cycle"""
diff --git a/custom_components/versatile_thermostat/feature_presence_manager.py b/custom_components/versatile_thermostat/feature_presence_manager.py
index 5b3ab7d..845e0ee 100644
--- a/custom_components/versatile_thermostat/feature_presence_manager.py
+++ b/custom_components/versatile_thermostat/feature_presence_manager.py
@@ -67,7 +67,7 @@ class FeaturePresenceManager(BaseFeatureManager):
self._presence_state = STATE_UNKNOWN
@overrides
- def start_listening(self):
+ async def start_listening(self):
"""Start listening the underlying entity"""
if self._is_configured:
self.stop_listening()
diff --git a/custom_components/versatile_thermostat/feature_safety_manager.py b/custom_components/versatile_thermostat/feature_safety_manager.py
index 3195efe..1c45152 100644
--- a/custom_components/versatile_thermostat/feature_safety_manager.py
+++ b/custom_components/versatile_thermostat/feature_safety_manager.py
@@ -70,7 +70,7 @@ class FeatureSafetyManager(BaseFeatureManager):
self._is_configured = True
@overrides
- def start_listening(self):
+ async def start_listening(self):
"""Start listening the underlying entity"""
@overrides
diff --git a/custom_components/versatile_thermostat/feature_window_manager.py b/custom_components/versatile_thermostat/feature_window_manager.py
index 2497e6a..2c4619f 100644
--- a/custom_components/versatile_thermostat/feature_window_manager.py
+++ b/custom_components/versatile_thermostat/feature_window_manager.py
@@ -124,7 +124,7 @@ class FeatureWindowManager(BaseFeatureManager):
self._window_state = STATE_UNKNOWN
@overrides
- def start_listening(self):
+ async def start_listening(self):
"""Start listening the underlying entity"""
if self._is_configured:
self.stop_listening()
diff --git a/custom_components/versatile_thermostat/thermostat_climate_valve.py b/custom_components/versatile_thermostat/thermostat_climate_valve.py
index 064fc08..8d7ea3a 100644
--- a/custom_components/versatile_thermostat/thermostat_climate_valve.py
+++ b/custom_components/versatile_thermostat/thermostat_climate_valve.py
@@ -263,14 +263,6 @@ class ThermostatOverClimateValve(ThermostatOverClimate):
"""True if the Thermostat is regulated by valve"""
return True
- @property
- def power_percent(self) -> float | None:
- """Get the current on_percent value"""
- if self._prop_algorithm:
- return round(self._prop_algorithm.on_percent * 100, 0)
- else:
- return None
-
# @property
# def hvac_modes(self) -> list[HVACMode]:
# """Get the hvac_modes"""
diff --git a/custom_components/versatile_thermostat/thermostat_switch.py b/custom_components/versatile_thermostat/thermostat_switch.py
index 6fbfda6..9fef124 100644
--- a/custom_components/versatile_thermostat/thermostat_switch.py
+++ b/custom_components/versatile_thermostat/thermostat_switch.py
@@ -26,23 +26,21 @@ _LOGGER = logging.getLogger(__name__)
class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
"""Representation of a base class for a Versatile Thermostat over a switch."""
- _entity_component_unrecorded_attributes = (
- BaseThermostat._entity_component_unrecorded_attributes.union(
- frozenset(
- {
- "is_over_switch",
- "is_inversed",
- "underlying_entities",
- "on_time_sec",
- "off_time_sec",
- "cycle_min",
- "function",
- "tpi_coef_int",
- "tpi_coef_ext",
- "power_percent",
- "calculated_on_percent",
- }
- )
+ _entity_component_unrecorded_attributes = BaseThermostat._entity_component_unrecorded_attributes.union( # pylint: disable=protected-access
+ frozenset(
+ {
+ "is_over_switch",
+ "is_inversed",
+ "underlying_entities",
+ "on_time_sec",
+ "off_time_sec",
+ "cycle_min",
+ "function",
+ "tpi_coef_int",
+ "tpi_coef_ext",
+ "power_percent",
+ "calculated_on_percent",
+ }
)
)
@@ -61,14 +59,6 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
"""True if the switch is inversed (for pilot wire and diode)"""
return self._is_inversed is True
- @property
- def power_percent(self) -> float | None:
- """Get the current on_percent value"""
- if self._prop_algorithm:
- return round(self._prop_algorithm.on_percent * 100, 0)
- else:
- return None
-
@overrides
def post_init(self, config_entry: ConfigData):
"""Initialize the Thermostat"""
diff --git a/custom_components/versatile_thermostat/underlyings.py b/custom_components/versatile_thermostat/underlyings.py
index 47f8bf5..bb40fc3 100644
--- a/custom_components/versatile_thermostat/underlyings.py
+++ b/custom_components/versatile_thermostat/underlyings.py
@@ -190,6 +190,11 @@ class UnderlyingEntity:
"""capping of the value send to the underlying eqt"""
return value
+ async def turn_off_and_cancel_cycle(self):
+ """Turn off and cancel eventual running cycle"""
+ self._cancel_cycle()
+ await self.turn_off()
+
class UnderlyingSwitch(UnderlyingEntity):
"""Represent a underlying switch"""
@@ -409,9 +414,10 @@ class UnderlyingSwitch(UnderlyingEntity):
await self.turn_off()
return
- if await self._thermostat.power_manager.check_overpowering():
- _LOGGER.debug("%s - End of cycle (3)", self)
- return
+ # if await self._thermostat.power_manager.check_overpowering():
+ # _LOGGER.debug("%s - End of cycle (3)", self)
+ # return
+
# safety mode could have change the on_time percent
await self._thermostat.safety_manager.refresh_state()
time = self._on_time_sec
diff --git a/custom_components/versatile_thermostat/vtherm_api.py b/custom_components/versatile_thermostat/vtherm_api.py
index 16f370d..7602c72 100644
--- a/custom_components/versatile_thermostat/vtherm_api.py
+++ b/custom_components/versatile_thermostat/vtherm_api.py
@@ -1,6 +1,7 @@
""" The API of Versatile Thermostat"""
import logging
+from datetime import datetime
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
@@ -16,8 +17,11 @@ from .const import (
CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_CENTRAL_CONFIG,
CONF_MAX_ON_PERCENT,
+ NowClass,
)
+from .central_feature_power_manager import CentralFeaturePowerManager
+
VTHERM_API_NAME = "vtherm_api"
_LOGGER = logging.getLogger(__name__)
@@ -62,6 +66,12 @@ class VersatileThermostatAPI(dict):
# A dict that will store all Number entities which holds the temperature
self._number_temperatures = dict()
self._max_on_percent = None
+ self._central_power_manager = CentralFeaturePowerManager(
+ VersatileThermostatAPI._hass, self
+ )
+
+ # the current time (for testing purpose)
+ self._now = None
def find_central_configuration(self):
"""Search for a central configuration"""
@@ -176,6 +186,10 @@ class VersatileThermostatAPI(dict):
if entry_id is None or entry_id == entity.unique_id:
await entity.async_startup(self.find_central_configuration())
+ # start listening for the central power manager if not only one vtherm reload
+ if not entry_id:
+ await self.central_power_manager.start_listening()
+
async def init_vtherm_preset_with_central(self):
"""Init all VTherm presets when the VTherm uses central temperature"""
# Initialization of all preset for all VTherm
@@ -289,3 +303,18 @@ class VersatileThermostatAPI(dict):
def hass(self):
"""Get the HomeAssistant object"""
return VersatileThermostatAPI._hass
+
+ @property
+ def central_power_manager(self) -> any:
+ """Returns the central power manager"""
+ return self._central_power_manager
+
+ # For testing purpose
+ def _set_now(self, now: datetime):
+ """Set the now timestamp. This is only for tests purpose"""
+ self._now = now
+
+ @property
+ def now(self) -> datetime:
+ """Get now. The local datetime or the overloaded _set_now date"""
+ return self._now if self._now is not None else NowClass.get_now(self._hass)
diff --git a/documentation/en/creation.md b/documentation/en/creation.md
index 7925450..edcde79 100644
--- a/documentation/en/creation.md
+++ b/documentation/en/creation.md
@@ -49,7 +49,7 @@ When your device is controlled by a `climate` entity in Home Assistant and you o
This type also includes advanced self-regulation features to adjust the setpoint sent to the underlying device, helping to achieve the target temperature faster and mitigating poor internal regulation. For example, if the device's internal thermometer is too close to the heating element, it may incorrectly assume the room is warm while the setpoint is far from being achieved in other areas.
-Since version 6.8, this VTherm type can also regulate directly by controlling the valve. Ideal for controllable TRVs, this type is recommended if you have such devices.
+Since version 6.8, this VTherm type can also regulate directly by controlling the valve. Ideal for controllable TRVs, as Sonoff TRVZB, this type is recommended if you have such devices.
The underlying entities for this VTherm type are exclusively `climate`.
diff --git a/documentation/en/feature-power.md b/documentation/en/feature-power.md
index 44b71aa..1b05c08 100644
--- a/documentation/en/feature-power.md
+++ b/documentation/en/feature-power.md
@@ -1,46 +1,50 @@
# Power Management - Load Shedding
- [Power Management - Load Shedding](#power-management---load-shedding)
- - [Configure Power Management](#configure-power-management)
+ - [Example Use Case:](#example-use-case)
+ - [Configuring Power Management](#configuring-power-management)
-This feature allows you to regulate the electricity consumption of your heaters. Known as load shedding, this feature enables you to limit the electrical consumption of your heating device if overcapacity conditions are detected.
-You will need a **sensor for the total instantaneous power consumption** of your home, as well as a **sensor for the maximum allowed power**.
+This feature allows you to regulate the electrical consumption of your heaters. Known as load shedding, it lets you limit the electrical consumption of your heating equipment if overconsumption conditions are detected.
+You will need a **sensor for the total instantaneous power consumption** of your home and a **sensor for the maximum allowed power**.
-The behavior of this feature is basic:
-1. when the _VTherm_ is about to turn on a device,
-2. it compares the last known value of the power consumption sensor with the last value of the maximum allowed power. If there is a remaining margin greater than or equal to the declared power of the _VTherm_'s devices, then the _VTherm_ and its devices will be turned on. Otherwise, they will remain off until the next cycle.
+The behavior of this feature is as follows:
+1. When a new measurement of the home's power consumption or the maximum allowed power is received,
+2. If the maximum power is exceeded, the central command will shed the load of all active devices starting with those closest to the setpoint. This continues until enough _VTherms_ are shed,
+3. If there is available power reserve and some _VTherms_ are shed, the central command will re-enable as many devices as possible, starting with those furthest from the setpoint (at the time they were shed).
-WARNING: This very basic operation **is not a safety function** but more of an optimization feature to manage consumption at the cost of heating performance. Overloads may occur depending on the frequency of updates from your consumption sensors, and the actual power used by your devices. Therefore, you must always maintain a safety margin.
+**WARNING:** This is **not a safety feature** but an optimization function to manage consumption at the expense of some heating degradation. Overconsumption is still possible depending on the frequency of your consumption sensor updates and the actual power used by your equipment. Always maintain a safety margin.
-Typical use case:
-1. you have an electricity meter limited to 11 kW,
-2. you occasionally charge an electric vehicle at 5 kW,
-3. that leaves 6 kW for everything else, including heating,
-4. you have 1 kW of other equipment running,
-5. you have declared a sensor (`input_number`) for the maximum allowed power at 9 kW (= 11 kW - the reserve for other devices - margin)
+### Example Use Case:
+1. You have an electric meter limited to 11 kW,
+2. You occasionally charge an electric vehicle at 5 kW,
+3. This leaves 6 kW for everything else, including heating,
+4. You have 1 kW of other active devices,
+5. You declare a sensor (`input_number`) for the maximum allowed power at 9 kW (= 11 kW - reserved power for other devices - safety margin).
-If the vehicle is charging, the total power consumed is 6 kW (5+1), and a _VTherm_ will only turn on if its declared power is 3 kW max (9 kW - 6 kW).
-If the vehicle is charging and another _VTherm_ of 2 kW is running, the total power consumed is 8 kW (5+1+2), and a _VTherm_ will only turn on if its declared power is 1 kW max (9 kW - 8 kW). Otherwise, it will wait until the next cycle.
+If the vehicle is charging, the total consumed power is 6 kW (5 + 1), and a _VTherm_ will only turn on if its declared power is a maximum of 3 kW (9 kW - 6 kW).
+If the vehicle is charging and another _VTherm_ of 2 kW is on, the total consumed power is 8 kW (5 + 1 + 2), and a _VTherm_ will only turn on if its declared power is a maximum of 1 kW (9 kW - 8 kW). Otherwise, it will skip its turn (cycle).
+If the vehicle is not charging, the total consumed power is 1 kW, and a _VTherm_ will only turn on if its declared power is a maximum of 8 kW (9 kW - 1 kW).
-If the vehicle is not charging, the total power consumed is 1 kW, and a _VTherm_ will only turn on if its declared power is 8 kW max (9 kW - 1 kW).
+## Configuring Power Management
-## Configure Power Management
-
-If you have chosen the `With power detection` feature, configure it as follows:
+In the centralized configuration, if you have selected the `With power detection` feature, configure it as follows:

-1. the entity ID of the **instantaneous power consumption sensor** for your home,
-2. the entity ID of the **maximum allowed power sensor**,
-3. the temperature to apply if load shedding is activated.
+1. The entity ID of the **sensor for total instantaneous power consumption** of your home,
+2. The entity ID of the **sensor for maximum allowed power**,
+3. The temperature to apply if load shedding is activated.
-Note that all power values must have the same units (kW or W, for example).
-Having a **maximum allowed power sensor** allows you to adjust the maximum power over time using a scheduler or automation.
+Ensure that all power values use the same units (e.g., kW or W).
+Having a **sensor for maximum allowed power** allows you to modify the maximum power dynamically using a scheduler or automation.
+
+Note that due to centralized load-shedding, it is not possible to override the consumption and maximum consumption sensors on individual _VTherms_. This configuration must be done in the centralized settings. See [Centralized Configuration](./creation.md#centralized-configuration).
>  _*Notes*_
>
-> 1. In case of load shedding, the radiator is set to the preset named `power`. This is a hidden preset, and you cannot select it manually.
-> 2. Always keep a margin, as the maximum power may briefly be exceeded while waiting for the next cycle calculation, or due to unregulated equipment.
-> 3. If you don't want to use this feature, uncheck it in the 'Functions' menu.
-> 4. If a _VTherm_ controls multiple devices, the **electrical consumption of your heating** must match the sum of the powers.
-> 5. If you are using the Versatile Thermostat UI card (see [here](additions.md#much-better-with-the-versatile-thermostat-ui-card)), load shedding is represented as follows: .
\ No newline at end of file
+> 1. During load shedding, the heater is set to the preset named `power`. This is a hidden preset that cannot be manually selected.
+> 2. Always maintain a margin, as the maximum power can briefly be exceeded while waiting for the next cycle's calculation or due to uncontrolled devices.
+> 3. If you do not wish to use this feature, uncheck it in the 'Features' menu.
+> 4. If a single _VTherm_ controls multiple devices, the **declared heating power consumption** should correspond to the total power of all devices.
+> 5. If you use the Versatile Thermostat UI card (see [here](additions.md#better-with-the-versatile-thermostat-ui-card)), load shedding is represented as follows: .
+> 6. There may be a delay of up to 20 seconds between receiving a new value from the power consumption sensor and triggering load shedding for _VTherms_. This delay prevents overloading Home Assistant if your consumption updates are very frequent.
\ No newline at end of file
diff --git a/documentation/en/feature-presets.md b/documentation/en/feature-presets.md
index 2962eb6..6ea42eb 100644
--- a/documentation/en/feature-presets.md
+++ b/documentation/en/feature-presets.md
@@ -5,7 +5,7 @@
## Configure Pre-configured Temperatures
-The preset mode allows you to pre-configure the target temperature. Used in conjunction with Scheduler (see [scheduler](additions#the-scheduler-component)), you'll have a powerful and simple way to optimize the temperature relative to the electricity consumption in your home. The managed presets are as follows:
+The preset mode allows you to pre-configure the target temperature. Used in conjunction with Scheduler (see [scheduler](additions.md#the-scheduler-component)), you'll have a powerful and simple way to optimize the temperature relative to the electricity consumption in your home. The managed presets are as follows:
- **Eco**: the device is in energy-saving mode
- **Comfort**: the device is in comfort mode
- **Boost**: the device fully opens all valves
diff --git a/documentation/en/one-page.md b/documentation/en/one-page.md
deleted file mode 100644
index ea7ae99..0000000
--- a/documentation/en/one-page.md
+++ /dev/null
@@ -1,1676 +0,0 @@
-[![GitHub Release][releases-shield]][releases]
-[![GitHub Activity][commits-shield]][commits]
-[![License][license-shield]](LICENSE)
-[![hacs][hacs_badge]][hacs]
-[![BuyMeCoffee][buymecoffeebadge]][buymecoffee]
-
-
-
->  Cette intégration de thermostat vise à simplifier considérablement vos automatisations autour de la gestion du chauffage. Parce que tous les événements autour du chauffage classiques sont gérés nativement par le thermostat (personne à la maison ?, activité détectée dans une pièce ?, fenêtre ouverte ?, délestage de courant ?), vous n'avez pas à vous encombrer de scripts et d'automatismes compliqués pour gérer vos climats. ;-).
-
-- [Changements dans la version 6.0](#changements-dans-la-version-60)
- - [Entités de température pour les pre-réglages](#entités-de-température-pour-les-pre-réglages)
- - [Dans le cas d'une configuration centrale](#dans-le-cas-dune-configuration-centrale)
- - [Refonte du menu de configuration](#refonte-du-menu-de-configuration)
- - [Les options de menu 'Configuration incomplète' et 'Finaliser'](#les-options-de-menu-configuration-incomplète-et-finaliser)
-- [Changements dans la version 5.0](#changements-dans-la-version-50)
-- [Merci pour la bière buymecoffee](#merci-pour-la-bière-buymecoffee)
-- [Quand l'utiliser et ne pas l'utiliser](#quand-lutiliser-et-ne-pas-lutiliser)
- - [Incompatibilités](#incompatibilités)
-- [Pourquoi une nouvelle implémentation du thermostat ?](#pourquoi-une-nouvelle-implémentation-du-thermostat-)
-- [Comment installer cet incroyable Thermostat Versatile ?](#comment-installer-cet-incroyable-thermostat-versatile-)
- - [HACS installation (recommendé)](#hacs-installation-recommendé)
- - [Installation manuelle](#installation-manuelle)
-- [Configuration](#configuration)
- - [Création d'un nouveau Versatile Thermostat](#création-dun-nouveau-versatile-thermostat)
- - [Choix des attributs de base](#choix-des-attributs-de-base)
- - [Sélectionnez des entités pilotées (sous-jacents)](#sélectionnez-des-entités-pilotées-sous-jacents)
- - [Pour un thermostat de type ```thermostat_over_switch```](#pour-un-thermostat-de-type-thermostat_over_switch)
- - [Pour un thermostat de type ```thermostat_over_climate```:](#pour-un-thermostat-de-type-thermostat_over_climate)
- - [L'auto-régulation](#lauto-régulation)
- - [L'auto-régulation en mode Expert](#lauto-régulation-en-mode-expert)
- - [Compensation de la température interne](#compensation-de-la-température-interne)
- - [Synthèse de l'algorithme d'auto-régulation](#synthèse-de-lalgorithme-dauto-régulation)
- - [Le mode auto-fan](#le-mode-auto-fan)
- - [Le démarrage / arrêt automatique](#le-démarrage--arrêt-automatique)
- - [Pour un thermostat de type ```thermostat_over_valve```:](#pour-un-thermostat-de-type-thermostat_over_valve)
- - [Configurez les coefficients de l'algorithme TPI](#configurez-les-coefficients-de-lalgorithme-tpi)
- - [Configurer les températures préréglées](#configurer-les-températures-préréglées)
- - [Configurer les portes/fenêtres en allumant/éteignant les thermostats](#configurer-les-portesfenêtres-en-allumantéteignant-les-thermostats)
- - [Le mode capteur](#le-mode-capteur)
- - [Le mode auto](#le-mode-auto)
- - [Configurer le mode d'activité ou la détection de mouvement](#configurer-le-mode-dactivité-ou-la-détection-de-mouvement)
- - [Configurer la gestion de la puissance](#configurer-la-gestion-de-la-puissance)
- - [Configurer la présence (ou l'absence)](#configurer-la-présence-ou-labsence)
- - [Configuration avancée](#configuration-avancée)
- - [Le contrôle centralisé](#le-contrôle-centralisé)
- - [Le contrôle d'une chaudière centrale](#le-contrôle-dune-chaudière-centrale)
- - [Configuration](#configuration-1)
- - [Comment trouver le bon service ?](#comment-trouver-le-bon-service-)
- - [Les évènements](#les-évènements)
- - [Avertissement](#avertissement)
- - [Synthèse des paramètres](#synthèse-des-paramètres)
-- [Exemples de réglage](#exemples-de-réglage)
- - [Chauffage électrique](#chauffage-électrique)
- - [Chauffage central (chauffage gaz ou fuel)](#chauffage-central-chauffage-gaz-ou-fuel)
- - [Le capteur de température alimenté par batterie](#le-capteur-de-température-alimenté-par-batterie)
- - [Capteur de température réactif (sur secteur)](#capteur-de-température-réactif-sur-secteur)
- - [Mes presets](#mes-presets)
-- [Algorithme](#algorithme)
- - [Algorithme TPI](#algorithme-tpi)
-- [Capteurs](#capteurs)
-- [Services](#services)
- - [Forcer la présence/occupation](#forcer-la-présenceoccupation)
- - [Modifier la température des préréglages](#modifier-la-température-des-préréglages)
- - [Modifier les paramètres de sécurité](#modifier-les-paramètres-de-sécurité)
- - [ByPass Window Check](#bypass-window-check)
-- [Evènements](#evènements)
-- [Attributs personnalisés](#attributs-personnalisés)
-- [Quelques résultats](#quelques-résultats)
-- [Encore mieux](#encore-mieux)
- - [Bien mieux avec le Versatile Thermostat UI Card](#bien-mieux-avec-le-versatile-thermostat-ui-card)
- - [Encore mieux avec le composant Scheduler !](#encore-mieux-avec-le-composant-scheduler-)
- - [Encore bien mieux avec la custom:simple-thermostat front integration](#encore-bien-mieux-avec-la-customsimple-thermostat-front-integration)
- - [Toujours mieux avec Plotly pour régler votre thermostat](#toujours-mieux-avec-plotly-pour-régler-votre-thermostat)
- - [Et toujours de mieux en mieux avec l'AappDaemon NOTIFIER pour notifier les évènements](#et-toujours-de-mieux-en-mieux-avec-laappdaemon-notifier-pour-notifier-les-évènements)
-- [Les contributions sont les bienvenues !](#les-contributions-sont-les-bienvenues)
-- [Dépannages](#dépannages)
- - [Utilisation d'un Heatzy](#utilisation-dun-heatzy)
- - [Utilisation d'un radiateur avec un fil pilote](#utilisation-dun-radiateur-avec-un-fil-pilote)
- - [Utilisation d'un radiateur avec un fil pilote](#utilisation-dun-radiateur-avec-un-fil-pilote-1)
- - [Seul le premier radiateur chauffe](#seul-le-premier-radiateur-chauffe)
- - [Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne](#le-radiateur-chauffe-alors-que-la-température-de-consigne-est-dépassée-ou-ne-chauffe-pas-alors-que-la-température-de-la-pièce-est-bien-en-dessous-de-la-consigne)
- - [Type `over_switch` ou `over_valve`](#type-over_switch-ou-over_valve)
- - [Type `over_climate`](#type-over_climate)
- - [Régler les paramètres de détection d'ouverture de fenêtre en mode auto](#régler-les-paramètres-de-détection-douverture-de-fenêtre-en-mode-auto)
- - [Pourquoi mon Versatile Thermostat se met en Securite ?](#pourquoi-mon-versatile-thermostat-se-met-en-securite-)
- - [Comment détecter le mode sécurité ?](#comment-détecter-le-mode-sécurité-)
- - [Comment être averti lorsque cela se produit ?](#comment-être-averti-lorsque-cela-se-produit-)
- - [Comment réparer ?](#comment-réparer-)
- - [Utilisation d'un groupe de personnes comme capteur de présence](#utilisation-dun-groupe-de-personnes-comme-capteur-de-présence)
- - [Activer les logs du Versatile Thermostat](#activer-les-logs-du-versatile-thermostat)
-
-Ce composant personnalisé pour Home Assistant est une mise à niveau et est une réécriture complète du composant "Awesome thermostat" (voir [Github](https://github.com/dadge/awesome_thermostat)) avec l'ajout de fonctionnalités.
-
-
->  _*Historique des dernières versions*_
-> * **Release 6.5** :
-> - Ajout d'une nouvelle fonction permettant l'arrêt et la relance automatique d'un VTherm `over_climate` [585](https://github.com/jmcollin78/versatile_thermostat/issues/585)
-> - Amélioration de la gestion des ouvertures au démarrage. Permet de mémoriser et de recalculer l'état d'une ouverture au redémarage de Home Assistant [504](https://github.com/jmcollin78/versatile_thermostat/issues/504)
-> * **Release 6.0** :
-> - Ajout d'entités du domaine Number permettant de configurer les températures des presets [354](https://github.com/jmcollin78/versatile_thermostat/issues/354)
-> - Refonte complète du menu de configuration pour supprimer les températures et utililsation d'un menu au lieu d'un tunnel de configuration [354](https://github.com/jmcollin78/versatile_thermostat/issues/354)
-> * **Release 5.4** :
-> - Ajout du pas de température [#311](https://github.com/jmcollin78/versatile_thermostat/issues/311),
-> - ajout de seuils de régulation pour les `over_valve` pour éviter de trop vider la batterie des TRV [#338](https://github.com/jmcollin78/versatile_thermostat/issues/338),
-> - ajout d'une option permettant d'utiliser la température interne d'un TRV pour forcer l' auto-régulation [#348](https://github.com/jmcollin78/versatile_thermostat/issues/348),
-> - ajout d'une fonction de keep-alive pour les VTherm `over_switch` [#345](https://github.com/jmcollin78/versatile_thermostat/issues/345)
-
-
-Autres versions
-
-> * **Release 5.3** : Ajout d'une fonction de pilotage d'une chaudière centrale [#234](https://github.com/jmcollin78/versatile_thermostat/issues/234) - plus d'infos ici: [Le contrôle d'une chaudière centrale](#le-contrôle-dune-chaudière-centrale). Ajout de la possibilité de désactiver le mode sécurité pour le thermomètre extérieur [#343](https://github.com/jmcollin78/versatile_thermostat/issues/343)
-> * **Release 5.2** : Ajout d'un `central_mode` permettant de piloter tous les VTherms de façon centralisée [#158](https://github.com/jmcollin78/versatile_thermostat/issues/158).
-> * **Release 5.1** : Limitation des valeurs envoyées aux valves et au température envoyées au climate sous-jacent.
-> * **Release 5.0** : Ajout d'une configuration centrale permettant de mettre en commun les attributs qui peuvent l'être [#239](https://github.com/jmcollin78/versatile_thermostat/issues/239).
-> * **Release 4.3** : Ajout d'un mode auto-fan pour le type `over_climate` permettant d'activer la ventilation si l'écart de température est important [#223](https://github.com/jmcollin78/versatile_thermostat/issues/223).
-> * **Release 4.2** : Le calcul de la pente de la courbe de température se fait maintenant en °/heure et non plus en °/min [#242](https://github.com/jmcollin78/versatile_thermostat/issues/242). Correction de la détection automatique des ouvertures par l'ajout d'un lissage de la courbe de température .
-> * **Release 4.1** : Ajout d'un mode de régulation **Expert** dans lequel l'utilisateur peut spécifier ses propres paramètres d'auto-régulation au lieu d'utiliser les pre-programmés [#194](https://github.com/jmcollin78/versatile_thermostat/issues/194).
-> * **Release 4.0** : Ajout de la prise en charge de la **Versatile Thermostat UI Card**. Voir [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card). Ajout d'un mode de régulation **Slow** pour les appareils de chauffage à latence lente [#168](https://github.com/jmcollin78/versatile_thermostat/issues/168). Changement de la façon dont **la puissance est calculée** dans le cas de VTherm avec des équipements multi-sous-jacents [#146](https://github.com/jmcollin78/versatile_thermostat/issues/146). Ajout de la prise en charge de AC et Heat pour VTherm via un interrupteur également [#144](https://github.com/jmcollin78/versatile_thermostat/pull/144)
-> * **Release 3.8**: Ajout d'une **fonction d'auto-régulation** pour les thermostats `over climate` dont la régulation est faite par le climate sous-jacent. Cf. [L'auto-régulation](#lauto-régulation) et [#129](https://github.com/jmcollin78/versatile_thermostat/issues/129). Ajout de la **possibilité d'inverser la commande** pour un thermostat `over switch` pour adresser les installations avec fil pilote et diode [#124](https://github.com/jmcollin78/versatile_thermostat/issues/124).
-> * **Release 3.7**: Ajout du type de **Versatile Thermostat `over valve`** pour piloter une vanne TRV directement ou tout autre équipement type gradateur pour le chauffage. La régulation se fait alors directement en agissant sur le pourcentage d'ouverture de l'entité sous-jacente : 0 la vanne est coupée, 100 : la vanne est ouverte à fond. Cf. [#131](https://github.com/jmcollin78/versatile_thermostat/issues/131). Ajout d'une fonction permettant le bypass de la détection d'ouverture [#138](https://github.com/jmcollin78/versatile_thermostat/issues/138). Ajout de la langue Slovaque
-> * **Release 3.6**: Ajout du paramètre `motion_off_delay` pour améliorer la gestion de des mouvements [#116](https://github.com/jmcollin78/versatile_thermostat/issues/116), [#128](https://github.com/jmcollin78/versatile_thermostat/issues/128). Ajout du mode AC (air conditionné) pour un VTherm over switch. Préparation du projet Github pour faciliter les contributions [#127](https://github.com/jmcollin78/versatile_thermostat/issues/127)
-> * **Release 3.5**: Plusieurs thermostats sont possibles en "thermostat over climate" mode [#113](https://github.com/jmcollin78/versatile_thermostat/issues/113)
-> * **Release 3.4**: bug fix et exposition des preset temperatures pour le mode AC [#103](https://github.com/jmcollin78/versatile_thermostat/issues/103)
-> * **Release 3.3**: ajout du mode Air Conditionné (AC). Cette fonction vous permet d'utiliser le mode AC de votre thermostat sous-jacent. Pour l'utiliser, vous devez cocher l'option "Uitliser le mode AC" et définir les valeurs de température pour les presets et pour les presets en cas d'absence
-> * **Release 3.2** : ajout de la possibilité de commander plusieurs switch à partir du même thermostat. Dans ce mode, les switchs sont déclenchés avec un délai pour minimiser la puissance nécessaire à un instant (on minimise les périodes de recouvrement). Voir [Configuration](#sélectionnez-des-entités-pilotées)
-> * **Release 3.1** : ajout d'une détection de fenêtres/portes ouvertes par chute de température. Cette nouvelle fonction permet de stopper automatiquement un radiateur lorsque la température chute brutalement. Voir [Le mode auto](#le-mode-auto)
-> * **Release majeure 3.0** : ajout d'un équipement thermostat et de capteurs (binaires et non binaires) associés. Beaucoup plus proche de la philosphie Home Assistant, vous avez maintenant un accès direct à l'énergie consommée par le radiateur piloté par le thermostat et à plein d'autres capteurs qui seront utiles dans vos automatisations et dashboard.
-> * **release 2.3** : ajout de la mesure de puissance et d'énergie du radiateur piloté par le thermostat.
-> * **release 2.2** : ajout de fonction de sécurité permettant de ne pas laisser éternellement en chauffe un radiateur en cas de panne du thermomètre
-> * **release majeure 2.0** : ajout du thermostat "over climate" permettant de transformer n'importe quel thermostat en Versatile Thermostat et lui ajouter toutes les fonctions de ce dernier.
-
-
-
-Changements dans la version 6.0
-# Changements dans la version 6.0
-
-## Entités de température pour les pre-réglages
-Les températures des presets sont maintenant directement acessibles sous la forme d'entités reliés au VTherm.
-Exemple :
-
-
-
-Les entités Boost, Confort, Eco et Hors-gel permettent de régler directement les températures de ces présets sans avoir à reconfigurer le VTHerm dans les écrans de configuration.
-Ces modifications sont persistentent à un redémarrage et sont prises en compte immédiatement par le VTherm.
-
-En fonction des fonctions activées, la liste des températures peut être plus ou moins complète :
-1. Si la gestion de présence est activée, les presets en cas d'absence sont créés. Ils sont suffixés par 'abs' pour absence,
-2. Si la gestion de la climatisation (Mode AC) est activé, les presets en mode clim sont créés. Ils sont suffixés par 'clim' pour climatisation. Seul le preset Hors gel n'a pas d'équivalent en mode clim,
-3. Les différentes combinaison absent et clim peuvent être créés en fonction de la configuration du VTherm
-
-Si un VTherm utilise les preset de la configuration centrale, ces entités ne sont pas créées, car les températures des presets sont gérés par la configuration centrale.
-
-### Dans le cas d'une configuration centrale
-Si vous avez configuré une configuration centrale, celle-ci possède aussi ses propres presets qui répondent au même règles qu'énoncées ci-dessus.
-Exemple d'une configuration centrale avec gestion de présence et mode AC (climatisation) :
-
-
-
-Dans le cas d'un changement d'une température de la configuration centrale, tous les VTherm qui utilisent ce preset sont immédiatement mis à jour.
-
-## Refonte du menu de configuration
-Le menu de configuration a été totalement revu. Il s'adapte dynamiquement aux choix de l'utilisateur et permet d'accéder directement aux réglages de la fonction voulue sans avoir à dérouler tous le tunnel de configuration.
-
-Pour créer un nouveau VTherm, il faudra d'abord choisir le type de VTherm :
-
-
-
-Puis, vous accédez maintenant au menu de configuration suivant :
-
-
-
-Chaque partie à configurer est accessible directement, sans avoir à dérouler tout le tunnel de configuration comme précédemment.
-
-Vous noterez l'option de menu nommée `Fonctions` qui permet de choisir quelles fonctions vont être implémentées pour ce VTherm :
-
-
-
-En fonction de vos choix, le menu principal s'adaptera pour ajouter les options nécessaires.
-
-Exemple de menu avec toutes les fonctions cochées :
-
-
-Vous pouvez constater que les options 'Détection des ouvertures', 'Détection de mouvement', 'Gestion de la puissance' et 'Gestion de présence' ont été ajoutées. Vous pouvez alors les configurer.
-
-### Les options de menu 'Configuration incomplète' et 'Finaliser'
-
-La dernière option du menu est spéciale. Elle permet de valider la création du VTherm lorsque toutes les fonctions ont été correctement configurées.
-Si l'une options n'est pas correctement configurée, la dernière option est la suivante :
-
-
-
-Sa sélection ne fait rien mais vous empêche de finaliser la création (resp. la modification) du VTherm.
-**Vous devez alors chercher dans les options laquelle manque**.
-
-Une fois que toute la configuration est valide, la dernière option se transforme en :
-
-
-
-Cliquez sur cette option pour créér (resp. modifier) le VTherm :
-
-
-
-
-
-Changements dans la version 5.0
-
-# Changements dans la version 5.0
-
-Vous pouvez maintenant définir une configuration centrale qui va vous permettre de mettre en commun sur tous vos VTherms (ou seulement une partie), certains attributs. Pour utiliser cette possibilité, vous devez :
-1. Créer un VTherm de type "Configuration Centrale",
-2. Saisir les attributs de cette configuration centrale
-
-Pour l'utiliser ensuite dans les autres VTherms, vous devez les reconfigurer et à chaque fois que c'est possible cocher la case "Utiliser la configuration centrale". Cette case à cocher apparait dans tous les groupes d'attributs qui peuvent avoir recours à la configuration centrale : attributs principaux, TPI, ouvertures, mouvement, puissance, présence et paramètres avancés.
-
-Les attributs configurable dans la configuration centrale est listée ici : [Synthèse des paramètres](#synthèse-des-paramètres).
-
-Lors d'un changement sur la configuration centrale, tous les VTherms seront rechargés pour tenir compte de ces changements.
-
-En conséquence toute la phase de paramètrage d'un VTherm a été profondemment modifiée pour pouvoir utiliser la configuration centrale ou surcharger les valeurs de la configuration centrale par des valeurs propre au VTherm en cours de configuration.
-
-
-
-# Merci pour la bière [buymecoffee](https://www.buymeacoffee.com/jmcollin78)
-Un grand merci à @salabur, @pvince83, @bergoglio, @EPicLURcher, @ecolorado66, @Kriss1670, @maia, @f.maymil, @moutte69, @Jerome, @Gunnar M, @Greg.o, @John Burgess, @abyssmal, @capinfo26, @Helge, @MattG @Mexx62, @Someone, @Lajull, @giopeco, @fredericselier, @philpagan, @studiogriffanti, @Edwin, @Sebbou, @Gerard R., @John Burgess, @Sylvoliv, @cdenfert, @stephane.l, @jms92100 pour les bières. Ca fait très plaisir et ça m'encourage à continuer !
-
-
-# Quand l'utiliser et ne pas l'utiliser
-Ce thermostat peut piloter 3 types d'équipements :
-1. un radiateur qui ne fonctionne qu'en mode marche/arrêt (nommé ```thermostat_over_switch```). La configuration minimale nécessaire pour utiliser ce type thermostat est :
- 1. un équipement comme un radiateur (un ```switch``` ou équivalent),
- 2. une sonde de température pour la pièce (ou un input_number),
- 3. un capteur de température externe (pensez à l'intégration météo si vous n'en avez pas)
-2. un autre thermostat qui a ses propres modes de fonctionnement (nommé ```thermostat_over_climate```). Pour ce type de thermostat la configuration minimale nécessite :
- 1. un équipement - comme une climatisation, une valve thermostatique - qui est pilotée par sa propre entity de type ```climate```,
-3. un équipement qui peut prendre une valeur de 0 à 100% (nommée ```thermostat_over_valve```). A 0 le chauffage est coupé, 100% il est ouvert à fond. Ce type permet de piloter une valve thermostatique (cf. valve Shelly) qui expose une entité de type `number.` permetttant de piloter directement l'ouverture de la vanne. Versatile Thermostat régule la température de la pièce en jouant sur le pourcentage d'ouverture, à l'aide des capteurs de température intérieur et extérieur en utilisant l'algorithme TPI décrit ci-dessous.
-
-Le type `over_climate` vous permet d'ajouter à votre équipement existant toutes les fonctionnalités apportées par VersatileThermostat. L'entité climate VersatileThermostat contrôlera votre entité climate sous-jacente, l'éteindra si les fenêtres sont ouvertes, la fera passer en mode Eco si personne n'est présent, etc. Voir [ici] (#pourquoi-un-nouveau-thermostat-implémentation). Pour ce type de thermostat, tous les cycles de chauffage sont contrôlés par l'entité climate sous-jacente et non par le thermostat polyvalent lui-même. Une fonction facultative d'auto-régulation permet au Versatile Thermostat d'ajuster la température donnée en consigne au sous-jacent afin d'atteindre la consigne.
-
-Les installations avec fil pilote et diode d'activation bénéficie d'une option qui permet d'inverser la commande on/off du radiateur sous-jacent. Pour cela, utilisez le type `over switch` et cochez l'option d'inversion de la commande.
-
-## Incompatibilités
-Certains thermostat de type TRV sont réputés incompatibles avec le Versatile Thermostat. C'est le cas des vannes suivantes :
-1. les vannes POPP de Danfoss avec retour de température. Il est impossible d'éteindre cette vanne et elle s'auto-régule d'elle-même causant des conflits avec le VTherm,
-2. Les thermostats « Homematic » (et éventuellement Homematic IP) sont connus pour rencontrer des problèmes avec le Versatile Thermostat en raison des limitations du protocole RF sous-jacent. Ce problème se produit particulièrement lorsque vous essayez de contrôler plusieurs thermostats Homematic à la fois dans une seule instance de VTherm. Afin de réduire la charge du cycle de service, vous pouvez par ex. regroupez les thermostats avec des procédures spécifiques à Homematic (par exemple en utilisant un thermostat mural) et laissez Versatile Thermostat contrôler uniquement le thermostat mural directement. Une autre option consiste à contrôler un seul thermostat et à propager les changements de mode CVC et de température par un automatisme,
-3. les thermostats de type Heatzy qui ne supportent pas les commandes de type set_temperature
-4. les thermostats de type Rointe ont tendance a se réveiller tout seul. Le reste fonctionne normalement.
-5. les TRV de type Aqara SRTS-A01 et MOES TV01-ZB qui n'ont pas le retour d'état `hvac_action` permettant de savoir si elle chauffe ou pas. Donc les retours d'état sont faussés, le reste à l'air fonctionnel.
-6. La clim Airwell avec l'intégration "Midea AC LAN". Si 2 commandes de VTherm sont trop rapprochées, la clim s'arrête d'elle même.
-7. Les climates basés sur l'intégration Overkiz ne fonctionnent pas. Il parait impossible d'éteindre ni même de changer la température sur ces systèmes.
-
-# Pourquoi une nouvelle implémentation du thermostat ?
-
-Ce composant nommé __Versatile thermostat__ gère les cas d'utilisation suivants :
-- Configuration via l'interface graphique d'intégration standard (à l'aide du flux Config Entry),
-- Utilisations complètes du **mode préréglages**,
-- Désactiver le mode préréglé lorsque la température est **définie manuellement** sur un thermostat,
-- Éteindre/allumer un thermostat lorsqu'une **porte ou des fenêtres sont ouvertes/fermées** après un certain délai,
-- Changer de preset lorsqu'une **activité est détectée** ou non dans une pièce pendant un temps défini,
-- Utiliser un algorithme **TPI (Time Proportional Interval)** grâce à l'algorithme [[Argonaute](https://forum.hacf.fr/u/argonaute/summary)] ,
-- Ajouter une **gestion de délestage** ou une régulation pour ne pas dépasser une puissance totale définie. Lorsque la puissance maximale est dépassée, un préréglage caché de « puissance » est défini sur l'entité climatique. Lorsque la puissance passe en dessous du maximum, le préréglage précédent est restauré.
-- La **gestion de la présence à domicile**. Cette fonctionnalité vous permet de modifier dynamiquement la température du préréglage en tenant compte d'un capteur de présence de votre maison.
-- Des **services pour interagir avec le thermostat** à partir d'autres intégrations : vous pouvez forcer la présence / la non-présence à l'aide d'un service, et vous pouvez modifier dynamiquement la température des préréglages et changer les paramètres de sécurité.
-- Ajouter des capteurs pour voir les états internes du thermostat,
-- Contrôle centralisé de tous les Versatile Thermostat pour les stopper tous, les passer tous en hors-gel, les forcer en mode Chauffage (l'hiver), les forcer en mode Climatisation (l'été).
-- Contrôle d'une chaudière centrale et des VTherm qui doivent contrôler cette chaudière.
-
-# Comment installer cet incroyable Thermostat Versatile ?
-
-## HACS installation (recommendé)
-
-1. Installez [HACS](https://hacs.xyz/). De cette façon, vous obtenez automatiquement les mises à jour.
-2. L'intégration Versatile Thermostat est maintenant proposée directement depuis l'interface HACF (onglet intégrations),
-3. recherchez et installez "Versatile Thermostat" dans HACS et cliquez sur "installer".
-4. Redémarrez Home Assistant.
-5. Ensuite, vous pouvez ajouter une intégration de Versatile Thermostat dans la page Paramètres / Intégrations. Vous ajoutez autant de thermostats dont vous avez besoin (généralement un par radiateur ou par groupe de radiateurs qui doivent être gérés ou par pompe dans le cas d'un chauffage centralisé)
-
-
-## Installation manuelle
-
-1. À l'aide de l'outil de votre choix, ouvrez le répertoire (dossier) de votre configuration HA (où vous trouverez `configuration.yaml`).
-2. Si vous n'avez pas de répertoire (dossier) `custom_components`, vous devez le créer.
-3. Dans le répertoire (dossier) `custom_components`, créez un nouveau dossier appelé `versatile_thermostat`.
-4. Téléchargez _tous_ les fichiers du répertoire `custom_components/versatile_thermostat/` (dossier) dans ce référentiel.
-5. Placez les fichiers que vous avez téléchargés dans le nouveau répertoire (dossier) que vous avez créé.
-6. Redémarrez l'assistant domestique
-7. Configurer la nouvelle intégration du Versatile Thermostat
-
-# Configuration
-
--- VTherm = Versatile Thermostat dans la suite de ce document --
-
->  _*Notes*_
->
-> Trois façons de configurer les VTherms sont disponibles :
-> 1. Chaque Versatile Thermostat est entièrement configurée de manière indépendante. Choisissez cette option si vous ne souhaitez avoir aucune configuration ou gestion centrale.
-> 2. Certains aspects sont configurés de manière centralisée. Cela permet par ex. définir la température min/max, la détection de fenêtre ouverte,… au niveau d'une instance centrale et unique. Pour chaque VTherm que vous configurez, vous pouvez alors choisir d'utiliser la configuration centrale ou de la remplacer par des paramètres personnalisés.
-> 3. En plus de cette configuration centralisée, tous les VTherm peuvent être contrôlées par une seule entité de type `select`. Cette fonction est nommé `central_mode`. Cela permet de stopper / démarrer / mettre en hors gel / etc tous les VTherms en une seule fois. Pour chaque VTherm, l'utilisateur indique si il est concerné par ce `central_mode`.
-
-
-## Création d'un nouveau Versatile Thermostat
-
-Cliquez sur le bouton Ajouter une intégration dans la page d'intégration
-
-
-
-puis
-
-
-
-La configuration peut être modifiée via la même interface. Sélectionnez simplement le thermostat à modifier, appuyez sur "Configurer" et vous pourrez modifier certains paramètres ou la configuration.
-
-Suivez ensuite les étapes de configuration en sélectionnant dans le menu l'option à configurer.
-
-## Choix des attributs de base
-
-Choisisez le menu "Principaux attributs".
-
-
-
-Donnez les principaux attributs obligatoires :
-1. un nom (sera le nom de l'intégration et aussi le nom de l'entité climate)
-2. le type de thermostat ```thermostat_over_switch``` pour piloter un radiateur commandé par un switch ou ```thermostat_over_climate``` pour piloter un autre thermostat, ou ```thermostat_over_valve``` Cf. [ci-dessus](#pourquoi-une-nouvelle-implémentation-du-thermostat)
-4. un identifiant d'entité de capteur de température qui donne la température de la pièce dans laquelle le radiateur est installé,
-5. une entité capteur de température donnant la température extérieure. Si vous n'avez pas de capteur externe, vous pouvez utiliser l'intégration météo locale
-6. une durée de cycle en minutes. A chaque cycle, le radiateur s'allumera puis s'éteindra pendant une durée calculée afin d'atteindre la température ciblée (voir [preset](#configure-the-preset-temperature) ci-dessous). En mode ```over_climate```, le cycle ne sert qu'à faire des controles de base mais ne régule pas directement la température. C'est le ```climate``` sous-jacent qui le fait,
-7. les températures minimales et maximales du thermostat,
-8. une puissance de l'équipement ce qui va activer les capteurs de puissance et énergie consommée par l'appareil,
-9. la possibilité de controler le thermostat de façon centralisée. Cf [controle centralisé](#le-contrôle-centralisé),
-10. la liste des fonctionnalités qui seront utilisées pour ce thermostat. En fonction de vos choix, les écrans de configuration suivants s'afficheront ou pas.
-
->  _*Notes*_
-> 1. avec les types ```over_switch``` et ```over_valve```, les calculs sont effectués à chaque cycle. Donc en cas de changement de conditions, il faudra attendre le prochain cycle pour voir un changement. Pour cette raison, le cycle ne doit pas être trop long. **5 min est une bonne valeur**,
-> 2. si le cycle est trop court, le radiateur ne pourra jamais atteindre la température cible. Pour le radiateur à accumulation par exemple il sera sollicité inutilement.
-
-## Sélectionnez des entités pilotées (sous-jacents)
-
-En fonction de votre choix sur le type de thermostat, vous devrez choisir une ou plusieurs entités de type `switch`, `climate` ou `number`. Seules les entités compatibles avec le type sont présentées.
-
->  _*Comment choisir le type*_
-> Le choix du type est important. Même si il toujours possible de le modifier ensuite via l'IHM de configuration, il est préférable de se poser les quelques questions suivantes :
-> 1. **quel type d'équipement je vais piloter ?** Dans l'ordre voici ce qu'il faut faire :
-> 1. si vous avez une vanne thermostatique (TRV) commandable dans Home Assistant via une entité de type ```number``` (par exemple une _Shelly TRV_), choisissez le type `over_valve`. C'est le type le plus direct et qui assure la meilleure régulation,
-> 2. si vous avez un radiateur électrique (avec ou sans fil pilote) et qu'une entité de type ```switch``` permet de l'allumer ou de l'éteindre, alors le type ```over_switch``` est préférable. La régulation sera faite par le Versatile Thermostat en fonction de la température mesuré par votre thermomètre, à l'endroit ou vous l'avez placé,
-> 3. dans tous les autres cas, utilisez le mode ```over_climate```. Vous gardez votre entité ```climate``` d'origine et le Versatile Thermostat "ne fait que" piloter le on/off et la température cible de votre thermostat d'origine. La régulation est faite par votre thermostat d'origine dans ce cas. Ce mode est particulièrement adapté aux climatisations réversible tout-en-un dont l'exposition dans Home Assistant se limite à une entité de type ```climate```
-> 2. **quelle type de régulation je veux ?** Si l'équipement piloté possède son propre mécanisme de régulation (clim, certaine vanne TRV) et que cette régulation fonctionne bien, optez pour un ```over_climate```
-
-### Pour un thermostat de type ```thermostat_over_switch```
-
-
-Certains équipements nécessitent d'être périodiquement sollicités pour empêcher un arrêt de sécurité. Connu sous le nom de "keep-alive" cette fonction est activable en entrant un nombre de secondes non nul dans le champ d'intervalle keep-alive du thermostat. Pour désactiver la fonction ou en cas de doute, laissez-le vide ou entrez zéro (valeur par défaut).
-
-L'algorithme à utiliser est aujourd'hui limité à TPI est disponible. Voir [algorithme](#algorithme).
-Si plusieurs entités de type sont configurées, la thermostat décale les activations afin de minimiser le nombre de switch actif à un instant t. Ca permet une meilleure répartition de la puissance puisque chaque radiateur va s'allumer à son tour.
-Exemple de déclenchement synchronisé :
-
-
-Il est possible de choisir un thermostat over switch qui commande une climatisation en cochant la case "AC Mode". Dans ce cas, seul le mode refroidissement sera visible.
-
-Si votre équipement est commandé par un fil pilote avec un diode, vous aurez certainement besoin de cocher la case "Inverser la case". Elle permet de mettre le switch à On lorsqu'on doit étiendre l'équipement et à Off lorsqu'on doit l'allumer.
-
-### Pour un thermostat de type ```thermostat_over_climate```:
-
-
-Il est possible de choisir un thermostat over climate qui commande une climatisation réversible en cochant la case "AC Mode". Dans ce cas, selon l'équipement commandé vous aurez accès au chauffage et/ou au réfroidissement.
-
-#### L'auto-régulation
-Depuis la release 3.8, vous avez la possibilité d'activer la fonction d'auto-régulation. Cette fonction autorise VersatileThermostat à adapter la consigne de température donnée au climate sous-jacent afin que la température de la pièce atteigne réellement la consigne.
-Pour faire ça, le VersatileThermostat calcule un décalage basé sur les informations suivantes :
-1. la différence actuelle entre la température réelle et la température de consigne, appelé erreur brute,
-2. l'accumulation des erreurs passées,
-3. la différence entre la température extérieure et la consigne
-
-Ces trois informations sont combinées pour calculer le décalage qui sera ajouté à la consigne courante et envoyé au climate sous-jacent.
-
-La fonction d'auto-régulation se paramètre avec :
-1. une dégré de régulation :
- 1. Légère - pour des faibles besoin en auto-régulation. Dans ce mode, le décalage maximal sera de 1,5°,
- 2. Medium - pour une auto-régulation moyenne. Un décalage maximal de 2° est possible dans ce mode,
- 3. Forte - pour un fort besoin d'auto-régulation. Le décalage maximal est de 3° dans ce mode et l'auto-régulation réagira fortement aux changements de température.
-2. Un seuil d'auto-régulation : valeur en dessous de laquelle une nouvelle régulation ne sera pas appliquée. Imaginons qu'à un instant t, le décalage soit de 2°. Si au prochain calcul, le décalage est de 2.4°, il sera pas appliqué. Il ne sera appliqué que la différence entre 2 décalages sera au moins égal à ce seuil,
-3. Période minimal entre 2 auto-régulation : ce nombre, exprimé en minute, indique la durée entre 2 changements de régulation.
-
-Ces trois paramètres permettent de moduler la régulation et éviter de multiplier les envois de régulation. Certains équipements comme les TRV, les chaudières n'aiment pas qu'on change la consigne de température trop souvent.
-
->  _*Conseil de mise en place*_
-> 1. Ne démarrez pas tout de suite l'auto-régulation. Regardez comment se passe la régulation naturelle de votre équipement. Si vous constatez que la température de consigne n'est pas atteinte ou qu'elle met trop de temps à être atteinte, démarrez la régulation,
-> 2. D'abord commencez par une légère auto-régulation et gardez les deux paramètres avec leur valeurs par défaut. Attendez quelques jours et vérifiez si la situation s'est améliorée,
-> 3. Si ce n'est pas suffisant, passez en auto-régulation Medium, attendez une stabilisation,
-> 4. Si ce n'est toujours pas suffisant, passez en auto-régulation Forte,
-> 5. Si ce n'est toujours pas bon, il faudra passer en mode expert pour pouvoir régler les paramètres de régulation de façon fine. Voir en-dessous
-
-L'auto-régulation consiste à forcer l'équipement a aller plus loin en lui forçant sa température de consigne régulièrement. Sa consommation peut donc être augmentée, ainsi que son usure.
-
-#### L'auto-régulation en mode Expert
-
-En mode **Expert** pouvez régler finement les paramètres de l'auto-régulation pour atteindre vos objeetifs et optimiser au mieux. L'algorithme calcule l'écart entre la consigne et la température réelle de la pièce. Cet écard est appelé erreur.
-Les paramètres réglables sont les suivants :
-1. `kp` : le facteur appliqué à l'erreur brute,
-2. `ki` : le facteur appliqué à l'accumulation des erreurs,
-3. `k_ext` : le facteur appliqué à la différence entre la température intérieure et la température externe,
-4. `offset_max` : le maximum de correction (offset) que la régulation peut appliquer,
-5. `stabilization_threshold` : un seuil de stabilisation qui lorsqu'il est atteint par l'erreur remet à 0, l'accumulation des erreurs,
-6. `accumulated_error_threshold` : le maximum pour l'accumulation d'erreur.
-
-Pour le tuning il faut tenir compte de ces observations :
-1. `kp * erreur` va donner l'offset lié à l'erreur brute. Cet offset est directement proportionnel à l'erreur et sera à 0 lorsque la target sera atteinte,
-2. l'accumulation de l'erreur permet de corriger le stabilisation de la courbe alors qu'il reste une erreur. L'erreur s'accumule et l'offset augmente donc progressivement ce qui devrait finir par stabiliser sur la température cible. Pour que ce paramètre fondamental est un effet il faut qu'il soit pas trop petit. Une valeur moyenne est 30
-3. `ki * accumulated_error_threshold` va donner l'offset maximal lié à l'accumulation de l'erreur,
-4. `k_ext` permet d'appliquer tout de suite (sans attendre une accumulation des erreurs) une correction lorsque la température extérieure est très différente de la température cible. Si la stabilisation se fait trop haut lorsqu'il les écarts de température sont importants, c'est que ce paramètre est trop fort. Il devrait pouvoir être annulé totalement pour laisser faire les 2 premiers offset
-
-Les valeurs préprogrammées sont les suivantes :
-
-Slow régulation :
-
- kp: 0.2 # 20% of the current internal regulation offset are caused by the current difference of target temperature and room temperature
- ki: 0.8 / 288.0 # 80% of the current internal regulation offset are caused by the average offset of the past 24 hours
- k_ext: 1.0 / 25.0 # this will add 1°C to the offset when it's 25°C colder outdoor than indoor
- offset_max: 2.0 # limit to a final offset of -2°C to +2°C
- stabilization_threshold: 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: 2.0 * 288 # this allows up to 2°C long term offset in both directions
-
-Light régulation :
-
- kp: 0.2
- ki: 0.05
- k_ext: 0.05
- offset_max: 1.5
- stabilization_threshold: 0.1
- accumulated_error_threshold: 10
-
-
-Medium régulation :
-
- kp: 0.3
- ki: 0.05
- k_ext: 0.1
- offset_max: 2
- stabilization_threshold: 0.1
- accumulated_error_threshold: 20
-
-
-Strong régulation :
-
- """Strong parameters for regulation
- A set of parameters which doesn't take into account the external temp
- and concentrate to internal temp error + accumulated error.
- This should work for cold external conditions which else generates
- high external_offset"""
-
- kp: 0.4
- ki: 0.08
- k_ext: 0.0
- offset_max: 5
- stabilization_threshold: 0.1
- accumulated_error_threshold: 50
-
-Pour utiliser le mode Expert il vous faut déclarer les valeurs que vous souhaitez utiliser pour chacun de ces paramètres dans votre `configuration.yaml` sous la forme suivante :
-```
-versatile_thermostat:
- auto_regulation_expert:
- kp: 0.4
- ki: 0.08
- k_ext: 0.0
- offset_max: 5
- stabilization_threshold: 0.1
- accumulated_error_threshold: 50
-```
-et bien sur, configurer le mode auto-régulation du VTherm en mode Expert. Tous les VTherm en mode **Expert** utiliseront ces mêmes paramètres.
-
-Pour que les modifications soient prises en compte, il faut soit **relancer totalement Home Assistant** soit juste l'intégration Versatile Thermostat (Outils de dev / Yaml / rechargement de la configuration / Versatile Thermostat).
-
-#### Compensation de la température interne
-Quelque fois, il arrive que le thermomètre interne du sous-jacent (TRV, climatisation, ...) soit tellement faux que l' auto-régulation ne suffise pas à réguler.
-Cela arrive lorsque le thermomètre interne est trop près de la source de chaleur. La température interne monte alors beaucoup plus vite que la température de la pièce, ce qui génère des défauts dans la régulation.
-Exemple :
-1. la température de la pièce est 18°, la consigne est à 20°,
-2. la température interne de l'équipement est de 22°,
-3. si VTherm envoie 21° comme consigne (= 20° + 1° d'auto-regulation), alors l'équipement ne chauffera pas car sa température interne (22°) est au-dessus de la consigne (21°)
-
-Pour palier à ça, une nouvelle option facultative a été ajoutée en version 5.4 : 
-
-Lorsqu'elle est activée, cette fonction ajoutera l'écart entre la température interne et la température de la pièce à la consigne pour forcer le chauffage.
-Dans l'exemple ci-dessus, l'écart est de +4° (22° - 18°), donc VTherm enverra 25° (21°+4°) à l'équipement le forçant ainsi à chauffer.
-
-Cet écart est calculé pour chaque sous-jacent car chacun à sa propre température interne. Pensez à un VTherm qui serait relié à 3 TRV chacun avec sa température interne par exemple.
-
-On obtient alors une auto-régulation bien plus efficace qui évite l'eccueil des gros écarts de température interne défaillante.
-
-#### Synthèse de l'algorithme d'auto-régulation
-L'algorithme d'auto-régulation peut être synthétisé comme suit:
-
-1. initialiser la température cible comme la consigne du VTherm,
-1. Si l'auto-régulation est activée,
- 1. calcule de la température régulée (valable pour un VTherm),
- 2. prendre cette température comme cible,
-2. Pour chaque sous-jacent du VTherm,
- 1. Si "utiliser la température interne" est cochée,
- 1. calcule de l'écart (trv internal temp - room temp),
- 2. ajout de l'écart à la température cible,
- 3. envoie de la température cible ( = temp regulee + (temp interne - temp pièce)) au sous-jacent
-
-
-
-#### Le mode auto-fan
-Ce mode introduit en 4.3 permet de forcer l'usage de la ventilation si l'écart de température est important. En effet, en activant la ventilation, la répartition se fait plus rapidement ce qui permet de gagner du temps dans l'atteinte de la température cible.
-Vous pouvez choisir quelle ventilation vous voulez activer entre les paramètres suivants : Faible, Moyenne, Forte, Turbo.
-
-Il faut évidemment que votre équipement sous-jacent soit équipée d'une ventilation et quelle soit pilotable pour que cela fonctionne.
-Si votre équipement ne comprend pas le mode Turbo, le mode Forte` sera utilisé en remplacement.
-Une fois l'écart de température redevenu faible, la ventilation se mettra dans un mode "normal" qui dépend de votre équipement à savoir (dans l'ordre) : `Silence (mute)`, `Auto (auto)`, `Faible (low)`. La première valeur qui est possible pour votre équipement sera choisie.
-
-#### Le démarrage / arrêt automatique
-Cette fonction a été introduite en 6.5.0. Elle permet d'autoriser VTherm a stopper un équipement qui n'a pas besoin d'être allumé et de le redémarrer lorsque les conditions le réclame. Cette fonction est munie de 3 réglages qui permettent d'arrêter / relancer plus ou moins rapidement l'équipement.
-
-Pour l'utiliser, vous devez :
-1. Ajouter la fonction `Avec démmarrage et extinction automatique` dans le menu 'Fonctions',
-2. Paramétrer le niveau de détection dans l'option 'Allumage/extinction automatique' qui s'affiche lorsque la fonction a été activée. Vous choisissez le niveau de détection entre 'Lent', 'Moyen' et 'Rapide'. Les arrêts/relances seront plus nombreux avec le niveau 'Rapide'.
-
-Une fois paramétré, vous aurez maintenant une nouvelle entité de type `switch` qui vous permet d'autoriser ou non l'arrêt/relance automatique sans toucher à la configuration. Cette entité est disponible sur l'appareil VTherm et se nomme `switch._enable_auto_start_stop`. Cochez la pour autoriser le démarrage et extinction automatique.
-
-L'algorithme de détection est décrit [ici](https://github.com/jmcollin78/versatile_thermostat/issues/585).
-
-### Pour un thermostat de type ```thermostat_over_valve```:
-
-Vous pouvez choisir jusqu'à entité du domaine ```number``` ou ```ìnput_number``` qui vont commander les vannes.
-L'algorithme à utiliser est aujourd'hui limité à TPI est disponible. Voir [algorithme](#algorithme).
-
-Il est possible de choisir un thermostat over valve qui commande une climatisation en cochant la case "AC Mode". Dans ce cas, seul le mode refroidissement sera visible.
-
-## Configurez les coefficients de l'algorithme TPI
-
-Si vous avez choisi un thermostat de type ```over_switch``` ou ```over_valve``` et que vous sélectionnez l'option "TPI" vous menu, vous arriverez sur cette page :
-
-
-
-Vous devez donner :
-1. le coefficient coef_int de l'algorithme TPI,
-2. le coefficient coef_ext de l'algorithme TPI
-
-
-Pour plus d'informations sur l'algorithme TPI et son réglage, veuillez vous référer à [algorithm](#algorithm).
-
-## Configurer les températures préréglées
-
-Le mode préréglé (preset) vous permet de préconfigurer la température ciblée. Utilisé en conjonction avec Scheduler (voir [scheduler](#even-better-with-scheduler-component) vous aurez un moyen puissant et simple d'optimiser la température par rapport à la consommation électrique de votre maison. Les préréglages gérés sont les suivants :
- - **Eco** : l'appareil est en mode d'économie d'énergie
- - **Confort** : l'appareil est en mode confort
- - **Boost** : l'appareil tourne toutes les vannes à fond
-
- Si le mode AC est utilisé, vous pourrez aussi configurer les températures lorsque l'équipement en mode climatisation.
-
-**Aucun** est toujours ajouté dans la liste des modes, car c'est un moyen de ne pas utiliser les preset mais une **température manuelle** à la place.
-
-Les pré-réglages se font (depuis v6.0) directement depuis les entités du VTherm ou de la configuration centrale si vous utilisez la configuration centrale.
-
->  _*Notes*_
-> 1. En modifiant manuellement la température cible, réglez le préréglage sur Aucun (pas de préréglage). De cette façon, vous pouvez toujours définir une température cible même si aucun préréglage n'est disponible.
-> 2. Le préréglage standard ``Away`` est un préréglage caché qui n'est pas directement sélectionnable. Versatile Thermostat utilise la gestion de présence ou la gestion de mouvement pour régler automatiquement et dynamiquement la température cible en fonction d'une présence dans le logement ou d'une activité dans la pièce. Voir [gestion de la présence](#configure-the-presence-management).
-> 3. Si vous utilisez la gestion du délestage, vous verrez un préréglage caché nommé ``power``. Le préréglage de l'élément chauffant est réglé sur « puissance » lorsque des conditions de surpuissance sont rencontrées et que le délestage est actif pour cet élément chauffant. Voir [gestion de l'alimentation](#configure-the-power-management).
-> 4. si vous utilisez la configuration avancée, vous verrez le préréglage défini sur ``sécurité`` si la température n'a pas pu être récupérée après un certain délai
-> 5. Si vous ne souhaitez pas utiliser le préréglage, indiquez 0 comme température. Le préréglage sera alors ignoré et ne s'affichera pas dans le composant front
-
-## Configurer les portes/fenêtres en allumant/éteignant les thermostats
-
-Vous devez avoir choisi la fonctionnalité ```Avec détection des ouvertures``` dans la première page pour arriver sur cette page.
-La détecttion des ouvertures peut se faire de 2 manières:
-1. soit avec un capteur placé sur l'ouverture (mode capteur),
-2. soit en détectant une chute brutale de température (mode auto)
-
-### Le mode capteur
-En mode capteur, vous devez renseigner les informations suivantes:
-
-
-1. un identifiant d'entité d'un **capteur de fenêtre/porte**. Cela devrait être un binary_sensor ou un input_boolean. L'état de l'entité doit être 'on' lorsque la fenêtre est ouverte ou 'off' lorsqu'elle est fermée
-2. un **délai en secondes** avant tout changement. Cela permet d'ouvrir rapidement une fenêtre sans arrêter le chauffage.
-
-
-### Le mode auto
-En mode auto, la configuration est la suivante:
-
-
-1. un seuil de détection en degré par minute. Lorsque la température chute au delà de ce seuil, le thermostat s'éteindra. Plus cette valeur est faible et plus la détection sera rapide (en contre-partie d'un risque de faux positif),
-2. un seuil de fin de détection en degré par minute. Lorsque la chute de température repassera au-dessus cette valeur, le thermostat se remettra dans le mode précédent (mode et preset),
-3. une durée maximale de détection. Au delà de cette durée, le thermostat se remettra dans son mode et preset précédent même si la température continue de chuter.
-
-Pour régler les seuils il est conseillé de commencer avec les valeurs de référence et d'ajuster les seuils de détection. Quelques essais m'ont donné les valeurs suivantes (pour un bureau):
-- seuil de détection : 0,05 °C/min
-- seuil de non détection: 0 °C/min
-- durée max : 60 min.
-
-Un nouveau capteur "slope" a été ajouté pour tous les thermostats. Il donne la pente de la courbe de température en °C/min (ou °K/min). Cette pente est lissée et filtrée pour éviter les valeurs abérrantes des thermomètres qui viendraient pertuber la mesure.
-
-
-Pour bien régler il est conseillé d'affocher sur un même graphique historique la courbe de température et la pente de la courbe (le "slope") :
-
-
-Et c'est tout ! votre thermostat s'éteindra lorsque les fenêtres seront ouvertes et se rallumera lorsqu'il sera fermé.
-
->  _*Notes*_
-> 1. Si vous souhaitez utiliser **plusieurs capteurs de porte/fenêtre** pour automatiser votre thermostat, créez simplement un groupe avec le comportement habituel (https://www.home-assistant.io/integrations/binary_sensor.group/)
-> 2. Si vous n'avez pas de capteur de fenêtre/porte dans votre chambre, laissez simplement l'identifiant de l'entité du capteur vide,
-> 3. **Un seul mode est permis**. On ne peut pas configurer un thermostat avec un capteur et une détection automatique. Les 2 modes risquant de se contredire, il n'est pas possible d'avoir les 2 modes en même temps,
-> 4. Il est déconseillé d'utiliser le mode automatique pour un équipement soumis à des variations de température fréquentes et normales (couloirs, zones ouvertes, ...)
-
-## Configurer le mode d'activité ou la détection de mouvement
-
-Si vous avez choisi la fonctionnalité ```Avec détection de mouvement```, cliquez sur 'Valider' sur la page précédente et vous y arriverez :
-
-
-
-Nous allons maintenant voir comment configurer le nouveau mode Activité.
-Ce dont nous avons besoin:
-- un **capteur de mouvement**. ID d'entité d'un capteur de mouvement. Les états du capteur de mouvement doivent être « on » (mouvement détecté) ou « off » (aucun mouvement détecté)
-- une durée de **délai de mouvement** (en secondes) définissant combien de temps nous attendons la confirmation du mouvement avant de considérer le mouvement. Ce paramètre peut être supérieur à la temporision de votre détecteur de mouvement, sinon la détection se fera à chaque mouvement signalé par le détecteur,
-- une durée de fin **délai de mouvement** (en secondes) définissant combien de temps nous attendons la confirmation d'une fin de mouvement avant de ne plus considérer le mouvement.
-- un **préréglage de "mouvement" **. Nous utiliserons la température de ce préréglage lorsqu'une activité sera détectée.
-- un **préréglage "pas de mouvement"**. Nous utiliserons la température de ce deuxième préréglage lorsqu'aucune activité n'est détectée.
-
-Alors imaginons que nous voulions avoir le comportement suivant :
-- nous avons une pièce avec un thermostat réglé en mode activité, le mode "mouvement" choisi est confort (21,5°C), le mode "pas de mouvement" choisi est Eco (18.5°C) et la temporisation du mouvement est de 30 sec lors de la détection et de 5 minutes sur fin de détection.
-- la pièce est vide depuis un moment (aucune activité détectée), la température de cette pièce est de 18,5°
-- quelqu'un entre dans la pièce, une activité est détectée si le mouvement est présent pendant au moins 30 sec. La température passe alors à 21,5°
-- si le mouvement est présent pendant moins de 30 sec (passage rapide), la température reste sur 18,5°,
-- imaginons que la température soit passée sur 21,5°, lorsque la personne quitte la pièce, au bout de 5 min la température est ramenée à 18,5°.
-- si la personne revient avant les 5 minutes, la température reste sur 21,5°
-
-Pour que cela fonctionne, le thermostat doit être en mode préréglé « Activité ».
-
->  _*Notes*_
- 1. Sachez que comme pour les autres modes prédéfinis, ``Activity`` ne sera proposé que s'il est correctement configuré. En d'autres termes, les 4 clés de configuration doivent être définies si vous souhaitez voir l'activité dans l'interface de l'assistant domestique
-
-## Configurer la gestion de la puissance
-
-Si vous avez choisi la fonctionnalité ```Avec détection de la puissance```, cliquez sur 'Valider' sur la page précédente et vous arriverez ici :
-
-
-
-Cette fonction vous permet de réguler la consommation électrique de vos radiateurs. Connue sous le nom de délestage, cette fonction vous permet de limiter la consommation électrique de votre appareil de chauffage si des conditions de surpuissance sont détectées. Donnez un **capteur à la consommation électrique actuelle de votre maison**, un **capteur à la puissance max** qu'il ne faut pas dépasser, la **consommation électrique totale des équipements du VTherm** (en étape 1 de la configuration) et l'algorithme ne démarrera pas un radiateur si la puissance maximale sera dépassée après le démarrage du radiateur.
-
-Notez que toutes les valeurs de puissance doivent avoir les mêmes unités (kW ou W par exemple).
-Cela vous permet de modifier la puissance maximale au fil du temps à l'aide d'un planificateur ou de ce que vous voulez.
-
->  _*Notes*_
-> 1. En cas de délestage, le radiateur est réglé sur le préréglage nommé ```power```. Il s'agit d'un préréglage caché, vous ne pouvez pas le sélectionner manuellement.
-> 2. Je l'utilise pour éviter de dépasser la limite de mon contrat d'électricité lorsqu'un véhicule électrique est en charge. Cela crée une sorte d'autorégulation.
-> 3. Gardez toujours une marge, car la puissance max peut être brièvement dépassée en attendant le calcul du prochain cycle typiquement ou par des équipements non régulés.
-> 4. Si vous ne souhaitez pas utiliser cette fonctionnalité, laissez simplement l'identifiant des entités vide
-> 5. Si vous controlez plusieurs radiateurs, la **consommation électrique de votre chauffage** renseigné doit correspondre à la somme des puissances.
-
-## Configurer la présence (ou l'absence)
-
-Si sélectionnée en première page, cette fonction vous permet de modifier dynamiquement la température de tous les préréglages du thermostat configurés lorsque personne n'est à la maison ou lorsque quelqu'un rentre à la maison. Pour cela, vous devez configurer la température qui sera utilisée pour chaque préréglage lorsque la présence est désactivée. Lorsque le capteur de présence s'éteint, ces températures seront utilisées. Lorsqu'il se rallume, la température "normale" configurée pour le préréglage est utilisée. Voir [gestion des préréglages](#configure-the-preset-temperature).
-Pour configurer la présence remplissez ce formulaire :
-
-
-
-Pour cela, vous devez configurer :
-1. Un **capteur d'occupation** dont l'état doit être 'on' ou 'home' si quelqu'un est présent ou 'off' ou 'not_home' sinon,
-2. La **température utilisée en Eco** prédéfinie en cas d'absence,
-3. La **température utilisée en Confort** préréglée en cas d'absence,
-4. La **température utilisée en Boost** préréglée en cas d'absence
-
-Si le mode AC est utilisé, vous pourrez aussi configurer les températures lorsque l'équipement en mode climatisation.
-
-ATTENTION : les groupes de personnes ne fonctionnent pas en tant que capteur de présence. Ils ne sont pas reconnus comme un capteur de présence. Vous devez utiliser, un template comme décrit ici [Utilisation d'un groupe de personnes comme capteur de présence](#utilisation-dun-groupe-de-personnes-comme-capteur-de-présence).
-
->  _*Notes*_
-> 1. le changement de température est immédiat et se répercute sur le volet avant. Le calcul prendra en compte la nouvelle température cible au prochain calcul du cycle,
-> 2. vous pouvez utiliser le capteur direct person.xxxx ou un groupe de capteurs de Home Assistant. Le capteur de présence gère les états ``on`` ou ``home`` comme présents et les états ``off`` ou ``not_home`` comme absents.
-
-## Configuration avancée
-
-Ces paramètres permettent d'affiner le réglage du thermostat.
-Le formulaire de configuration avancée est le suivant :
-
-
-
-Le premier délai (minimal_activation_delay_sec) en secondes est le délai minimum acceptable pour allumer le chauffage. Lorsque le calcul donne un délai de mise sous tension inférieur à cette valeur, le chauffage reste éteint.
-
-Le deuxième délai (``safety_delay_min``) est le délai maximal entre deux mesures de température avant de régler le préréglage sur ``security``. Si le capteur de température ne donne plus de mesures de température, le thermostat et le radiateur passeront en mode ``security`` après ce délai. Ceci est utile pour éviter une surchauffe si la batterie de votre capteur de température est trop faible.
-
-Le troisième paramétre (``safety_min_on_percent``) est la valeur minimal de ``on_percent`` en dessous de laquelle le préréglage sécurité ne sera pas activé. Ce paramètre permet de ne pas mettre en sécurité un thermostat, si le radiateur piloté ne chauffe pas suffisament.
-Mettre ce paramètre à ``0.00`` déclenchera le préréglage sécurité quelque soit la dernière consigne de chauffage, à l'inverse ``1.00`` ne déclenchera jamais le préréglage sécurité ( ce qui revient à désactiver la fonction).
-
-Le quatrième param§tre (``safety_default_on_percent``) est la valeur de ``on_percent`` qui sera utilisée lorsque le thermostat passe en mode ``security``. Si vous mettez ``0`` alors le thermostat sera coupé lorsqu'il passe en mode ``security``, mettre 0,2% par exemple permet de garder un peu de chauffage (20% dans ce cas), même en mode ``security``. Ca évite de retrouver son logement totalement gelé lors d'une panne de thermomètre.
-
-Note: les paramètres `safety_min_on_percent` et `safety_default_on_percent` ne s'applique pas aux VTherms `over_climate`.
-
-Depuis la version 5.3 il est possible de désactiver la mise en sécurité suite à une absence de données du thermomètre extérieure. En effet, celui-ci ayant la plupart du temps un impact faible sur la régulation (dépendant de votre paramètrage), il est possible qu'il soit absent sans mettre en danger le logement. Pour cela, il faut ajouter les lignes suivantes dans votre `configuration.yaml` :
-```
-versatile_thermostat:
-...
- safety_mode:
- check_outdoor_sensor: false
-```
-Par défaut, le thermomètre extérieur peut déclencher une mise en sécurité si il n'envoit plus de valeur.
-
-Voir [exemple de réglages](#examples-tuning) pour avoir des exemples de réglage communs
-
->  _*Notes*_
-> 1. Lorsque le capteur de température viendra à la vie et renverra les températures, le préréglage sera restauré à sa valeur précédente,
-> 2. Attention, deux températures sont nécessaires : la température interne et la température externe et chacune doit donner la température, sinon le thermostat sera en préréglage "security",
-> 3. Un service est disponible qui permet de régler les 3 paramètres de sécurité. Ca peut servir à adapter la fonction de sécurité à votre usage,
-> 4. Pour un usage naturel, le ``safety_default_on_percent`` doit être inférieur à ``safety_min_on_percent``,
-> 5. Les thermostats de type ``thermostat_over_climate`` ne sont pas concernés par le mode security.
-
-## Le contrôle centralisé
-
-Depuis la release 5.2, si vous avez défini une configuration centralisée, vous avez une nouvelle entité nommée `select.central_mode` qui permet de piloter tous les VTherms avec une seule action. Pour qu'un VTherm soit contrôlable de façon centralisée, il faut que son attribut de configuration nommé `use_central_mode` soit vrai.
-
-Cette entité se présente sous la forme d'une liste de choix qui contient les choix suivants :
-1. `Auto` : le mode 'normal' dans lequel chaque VTherm se comporte comme dans les versions précédentes,
-2. `Stooped` : tous les VTherms sont mis à l'arrêt (`hvac_off`),
-3. `Heat only` : tous les VTherms sont mis en mode chauffage lorsque ce mode est supporté par le VTherm, sinon il est stoppé,
-3. `Cool only` : tous les VTherms sont mis en mode climatisation lorsque ce mode est supporté par le VTherm, sinon il est stoppé,
-4. `Frost protection` : tous les VTherms sont mis en preset hors-gel lorsque ce preset est supporté par le VTherm, sinon il est stoppé.
-
-Il est donc possible de contrôler tous les VTherms (que ceux que l'on désigne explicitement) avec un seul contrôle.
-Exemple de rendu :
-
-
-
-## Le contrôle d'une chaudière centrale
-
-Depuis la release 5.3, vous avez la possibilité de contrôler une chaudière centralisée. A partir du moment où il est possible de déclencher ou stopper cette chaudière depuis Home Assistant, alors Versatile Thermostat va pouvoir la commander directement.
-
-Le principe mis en place est globalement le suivant :
-1. une nouvelle entité de type `binary_sensor` et nommée par défaut `binary_sensor.central_boiler` est ajoutée,
-2. dans la configuration des VTherms vous indiquez si le VTherm doit contrôler la chaudière. En effet, dans une installation hétérogène, certains VTherm doivent commander la chaudière et d'autres non. Vous devez donc indiquer dans chaque configuration de VTherm si il contrôle la chaudière ou pas,
-3. le `binary_sensor.central_boiler` écoute les changements d'états des équipements des VTherm marqués comme contrôlant la chaudière,
-4. dès que le nombre d'équipements pilotés par le VTherm demandant du chauffage (ie son `hvac_action` passe à `Heating`) dépasse un seuil paramétrable, alors le `binary_sensor.central_boiler` passe à `on` et **si un service d'activation a été configuré, alors ce service est appelé**,
-5. si le nombre d'équipements nécessitant du chauffage repasse en dessous du seuil, alors le `binary_sensor.central_boiler` passe à `off` et si **un service de désactivation a été configuré, alors ce service est appelé**,
-6. vous avez accès à deux entités :
- - une de type `number` nommé par défaut `number.boiler_activation_threshold`, donne le seuil de déclenchement. Ce seuil est en nombre d'équipements (radiateurs) qui demande du chauffage.
- - une de type `sensor` nommé par défaut `sensor.nb_device_active_for_boiler`, donne le nombre d'équipements qui demande du chauffage. Par exemple, un VTherm ayant 4 vannes dont 3 demandes du chauffage fera passé ce capteur à 3. Seuls les équipements des VTherms qui sont marqués pour contrôler la chaudière centrale sont comptabilisés.
-
-Vous avez donc en permanence, les informations qui permettent de piloter et régler le déclenchement de la chaudière.
-
-Toutes ces entités sont rattachés au service de configuration centrale :
-
-
-### Configuration
-Pour configurer cette fonction, vous devez avoir une configuration centralisée (cf. [Configuration](#configuration)) et cochez la case 'Ajouter une chuadière centrale' :
-
-
-
-Sur la page suivante vous pouvez donner la configuration des services à appeler lors de l'allumage / extinction de la chaudière :
-
-
-
-Les services se configurent comme indiqués dans la page :
-1. le format général est `entity_id/service_id[/attribut:valeur]` (où `/attribut:valeur` est facultatif),
-2. `entity_id` est le nom de l'entité qui commande la chaudière sous la forme `domain.entity_name`. Par exemple: `switch.chaudiere` pour les chaudière commandée par un switch ou `climate.chaudière` pour une chaudière commandée par un thermostat ou tout autre entité qui permet le contrôle de la chaudière (il n'y a pas de limitation). On peut aussi commuter des entrées (`helpers`) comme des `input_boolean` ou `input_number`.
-3. `service_id` est le nom du service à appeler sous la forme `domain.service_name`. Par exemple: `switch.turn_on`, `switch.turn_off`, `climate.set_temperature`, `climate.set_hvac_mode` sont des exemples valides.
-4. pour certain service vous aurez besoin d'un paramètre. Cela peut être le 'Mode CVC' `climate.set_hvac_mode` ou la température cible pour `climate.set_temperature`. Ce paramètre doit être configuré sous la forme `attribut:valeur` en fin de chaine.
-
-Exemples (à ajuster à votre cas) :
-- `climate.chaudiere/climate.set_hvac_mode/hvac_mode:heat` : pour allumer le thermostat de la chaudière en mode chauffage,
-- `climate.chaudiere/climate.set_hvac_mode/hvac_mode:off` : pour stopper le thermostat de la chaudière,
-- `switch.pompe_chaudiere/switch.turn_on` : pour allumer le swicth qui alimente la pompe de la chaudière,
-- `switch.pompe_chaudiere/switch.turn_off` : pour allumer le swicth qui alimente la pompe de la chaudière,
-- ...
-
-### Comment trouver le bon service ?
-Pour trouver le services a utiliser, le mieux est d'aller dans "Outils de développement / Services", chercher le service a appelé, l'entité à commander et l'éventuel paramètre à donner.
-Cliquez sur 'Appeler le service'. Si votre chaudière s'allume vous avez la bonne configuration. Passez alors en mode Yaml et recopiez les paramètres.
-
-Exemple:
-
-Sous "Outils de développement / Service" :
-
-
-
-En mode yaml :
-
-
-
-Le service à configurer est alors le suivant: `climate.empty_thermostast/climate.set_hvac_mode/hvac_mode:heat` (notez la suppression du blanc dans `hvac_mode:heat`)
-
-Faite alors de même pour le service d'extinction et vous êtes parés.
-
-### Les évènements
-
-A chaque allumage ou extinction réussie de la chaudière un évènement est envoyé par Versatile Thermostat. Il peut avantageusement être capté par une automatisation, par exemple pour notifier un changement.
-Les évènements ressemblent à ça :
-
-Un évènement d'allumage :
-```
-event_type: versatile_thermostat_central_boiler_event
-data:
- central_boiler: true
- entity_id: binary_sensor.central_boiler
- name: Central boiler
- state_attributes: null
-origin: LOCAL
-time_fired: "2024-01-14T11:33:52.342026+00:00"
-context:
- id: 01HM3VZRJP3WYYWPNSDAFARW1T
- parent_id: null
- user_id: null
-```
-
-Un évènement d'extinction :
-```
-event_type: versatile_thermostat_central_boiler_event
-data:
- central_boiler: false
- entity_id: binary_sensor.central_boiler
- name: Central boiler
- state_attributes: null
-origin: LOCAL
-time_fired: "2024-01-14T11:43:52.342026+00:00"
-context:
- id: 01HM3VZRJP3WYYWPNSDAFBRW1T
- parent_id: null
- user_id: null
-```
-
-### Avertissement
-
->  _*Notes*_
-> Le contrôle par du logiciel ou du matériel de type domotique d'une chaudière centrale peut induire des risques pour son bon fonctionnement. Assurez-vous avant d'utiliser ces fonctions, que votre chaudière possède bien des fonctions de sécurité et que celles-ci fonctionnent. Allumer une chaudière si tous les robinets sont fermés peut générer de la sur-pression par exemple.
-
-
-Synthèse des paramètres
-
-## Synthèse des paramètres
-
-| Paramètre | Libellé | "over switch" | "over climate" | "over valve" | "configuration centrale" |
-| ----------------------------------------- | --------------------------------------------------------------------------------- | ------------- | ------------------- | ------------ | ------------------------ |
-| ``name`` | Nom | X | X | X | - |
-| ``thermostat_type`` | Type de thermostat | X | X | X | - |
-| ``temperature_sensor_entity_id`` | Temperature sensor entity id | X | X (auto-regulation) | X | - |
-| ``external_temperature_sensor_entity_id`` | Température de l'exterieur sensor entity id | X | X (auto-regulation) | X | X |
-| ``cycle_min`` | Durée du cycle (minutes) | X | X | X | - |
-| ``temp_min`` | Température minimale permise | X | X | X | X |
-| ``temp_max`` | Température maximale permise | X | X | X | X |
-| ``device_power`` | Puissance de l'équipement | X | X | X | - |
-| ``use_central_mode`` | Autorisation du contrôle centralisé | X | X | X | - |
-| ``use_window_feature`` | Avec détection des ouvertures | X | X | X | - |
-| ``use_motion_feature`` | Avec détection de mouvement | X | X | X | - |
-| ``use_power_feature`` | Avec gestion de la puissance | X | X | X | - |
-| ``use_presence_feature`` | Avec détection de présence | X | X | X | - |
-| ``heater_entity1_id`` | 1er radiateur | X | - | - | - |
-| ``heater_entity2_id`` | 2ème radiateur | X | - | - | - |
-| ``heater_entity3_id`` | 3ème radiateur | X | - | - | - |
-| ``heater_entity4_id`` | 4ème radiateur | X | - | - | - |
-| ``heater_keep_alive`` | Intervalle de rafraichissement du switch | X | - | - | - |
-| ``proportional_function`` | Algorithme | X | - | - | - |
-| ``climate_entity1_id`` | Thermostat sous-jacent | - | X | - | - |
-| ``climate_entity2_id`` | 2ème thermostat sous-jacent | - | X | - | - |
-| ``climate_entity3_id`` | 3ème thermostat sous-jacent | - | X | - | - |
-| ``climate_entity4_id`` | 4ème thermostat sous-jacent | - | X | - | - |
-| ``valve_entity1_id`` | Vanne sous-jacente | - | - | X | - |
-| ``valve_entity2_id`` | 2ème vanne sous-jacente | - | - | X | - |
-| ``valve_entity3_id`` | 3ème vanne sous-jacente | - | - | X | - |
-| ``valve_entity4_id`` | 4ème vanne sous-jacente | - | - | X | - |
-| ``ac_mode`` | utilisation de l'air conditionné (AC) ? | X | X | X | - |
-| ``tpi_coef_int`` | Coefficient à utiliser pour le delta de température interne | X | - | X | X |
-| ``tpi_coef_ext`` | Coefficient à utiliser pour le delta de température externe | X | - | X | X |
-| ``frost_temp`` | Température en preset Hors-gel | X | X | X | X |
-| ``window_sensor_entity_id`` | Détecteur d'ouverture (entity id) | X | X | X | - |
-| ``window_delay`` | Délai avant extinction (secondes) | X | X | X | X |
-| ``window_auto_open_threshold`` | Seuil haut de chute de température pour la détection automatique (en °/min) | X | X | X | X |
-| ``window_auto_close_threshold`` | Seuil bas de chute de température pour la fin de détection automatique (en °/min) | X | X | X | X |
-| ``window_auto_max_duration`` | Durée maximum d'une extinction automatique (en min) | X | X | X | X |
-| ``motion_sensor_entity_id`` | Détecteur de mouvement entity id | X | X | X | - |
-| ``motion_delay`` | Délai avant prise en compte du mouvement (seconds) | X | X | X | - |
-| ``motion_off_delay`` | Délai avant prise en compte de la fin de mouvement (seconds) | X | X | X | X |
-| ``motion_preset`` | Preset à utiliser si mouvement détecté | X | X | X | X |
-| ``no_motion_preset`` | Preset à utiliser si pas de mouvement détecté | X | X | X | X |
-| ``power_sensor_entity_id`` | Capteur de puissance totale (entity id) | X | X | X | X |
-| ``max_power_sensor_entity_id`` | Capteur de puissance Max (entity id) | X | X | X | X |
-| ``power_temp`` | Température si délestaqe | X | X | X | X |
-| ``presence_sensor_entity_id`` | Capteur de présence entity id (true si quelqu'un est présent) | X | X | X | - |
-| ``minimal_activation_delay`` | Délai minimal d'activation | X | - | - | X |
-| ``safety_delay_min`` | Délai maximal entre 2 mesures de températures | X | - | X | X |
-| ``safety_min_on_percent`` | Pourcentage minimal de puissance pour passer en mode sécurité | X | - | X | X |
-| ``auto_regulation_mode`` | Le mode d'auto-régulation | - | X | - | - |
-| ``auto_regulation_dtemp`` | La seuil d'auto-régulation | - | X | - | - |
-| ``auto_regulation_period_min`` | La période minimale d'auto-régulation | - | X | - | - |
-| ``inverse_switch_command`` | Inverse la commande du switch (pour switch avec fil pilote) | X | - | - | - |
-| ``auto_fan_mode`` | Mode de ventilation automatique | - | X | - | - |
-| ``auto_regulation_use_device_temp`` | Utilisation de la température interne du sous-jacent | - | X | - | - |
-| ``use_central_boiler_feature`` | Ajout du controle d'une chaudière centrale | - | - | - | X |
-| ``central_boiler_activation_service`` | Service d'activation de la chaudière | - | - | - | X |
-| ``central_boiler_deactivation_service`` | Service de desactivation de la chaudière | - | - | - | X |
-| ``used_by_controls_central_boiler`` | Indique si le VTherm contrôle la chaudière centrale | X | X | X | - |
-| ``use_auto_start_stop_feature`` | Indique si la fonction de démarrage/extinction automatique est activée | - | X | - | - |
-| ``auto_start_stop_lvel`` | Le niveau de détection de l'auto start/stop | - | X | - | - |
-
-
-# Exemples de réglage
-
-## Chauffage électrique
-- cycle : entre 5 et 10 minutes,
-- minimal_activation_delay_sec : 30 secondes
-
-## Chauffage central (chauffage gaz ou fuel)
-- cycle : entre 30 et 60 min,
-- minimal_activation_delay_sec : 300 secondes (à cause du temps de réponse)
-
-## Le capteur de température alimenté par batterie
-- safety_delay_min : 60 min (parce que ces capteurs sont paresseux)
-- safety_min_on_percent : 0,5 (50% - on passe en preset ``security`` si le radiateur chauffait plus de 50% du temps)
-- safety_default_on_percent : 0,1 (10% - en preset ``security``, on garde un fond de chauffe de 20% du temps)
-
-Il faut comprendre ces réglages comme suit :
-
-> Si le thermomètre n'envoie plus la température pendant 1 heure et que le pourcentage de chauffe (``on_percent``) était supérieur à 50 %, alors on ramène ce pourcentage de chauffe à 10 %.
-
-A vous d'adapter ces réglages à votre cas !
-
-Ce qui est important c'est de ne pas prendre trop de risque avec ces paramètres : supposez que vous êtes absent pour une longue période, que les piles de votre thermomètre arrivent en fin de vie, votre radiateur va chauffer 10% du temps pendant toute la durée de la panne.
-
-Versatile Thermostat vous permet d'être notifié lorsqu'un évènement de ce type survient. Mettez en place, les alertes qui vont bien dès l'utilisation de ce thermostat. Cf. (#notifications)
-
-## Capteur de température réactif (sur secteur)
-- safety_delay_min : 15 min
-- safety_min_on_percent : 0,7 (70% - on passe en preset ``security`` si le radiateur chauffait plus de 70% du temps)
-- safety_default_on_percent : 0,25 (25% - en preset ``security``, on garde un fond de chauffe de 25% du temps)
-
-## Mes presets
-Ceci est juste un exemple de la façon dont j'utilise le préréglage. A vous de vous adapter à votre configuration mais cela peut être utile pour comprendre son fonctionnement.
-``Hors gel`` : 10 °C
-``Éco`` : 17 °C
-``Confort`` : 19 °C
-``Boost`` : 20 °C
-
-Lorsque la présence est désactivée :
-``Hors gel`` : 10 °C
-``Éco`` : 16,5 °C
-``Confort`` : 17 °C
-``Boost`` : 18 °C
-
-Le détecteur de mouvement de mon bureau est configuré pour utiliser ``Boost`` lorsqu'un mouvement est détecté et ``Eco`` sinon.
-
-# Algorithme
-Cette intégration utilise un algorithme proportionnel. Un algorithme proportionnel est utile pour éviter l'oscillation autour de la température cible. Cet algorithme est basé sur un cycle qui alterne le chauffage et l'arrêt du chauffage. La proportion de chauffage par rapport à l'absence de chauffage est déterminée par la différence entre la température et la température cible. Plus grande est la différence et plus grande est la proportion de chauffage à l'intérieur du cycle.
-
-Cet algorithme fait converger la température et arrête d'osciller.
-
-## Algorithme TPI
-L'algorithme TPI consiste à calculer à chaque cycle un pourcentage d'état On vs Off pour le radiateur en utilisant la température cible, la température actuelle dans la pièce et la température extérieure actuelle. Cet algorithme n'est donc valable que pour les Versatile Thermostat qui régulent : `over_switch` et `over_valve`.
-
-Le pourcentage est calculé avec cette formule :
-
- on_percent = coef_int * (température cible - température actuelle) + coef_ext * (température cible - température extérieure)
- Ensuite, l'algo fait en sorte que 0 <= on_percent <= 1
-
-Les valeurs par défaut pour coef_int et coef_ext sont respectivement : ``0.6`` et ``0.01``. Ces valeurs par défaut conviennent à une pièce standard bien isolée.
-
-Pour régler ces coefficients, gardez à l'esprit que :
-1. **si la température cible n'est pas atteinte** après une situation stable, vous devez augmenter le ``coef_ext`` (le ``on_percent`` est trop bas),
-2. **si la température cible est dépassée** après une situation stable, vous devez diminuer le ``coef_ext`` (le ``on_percent`` est trop haut),
-3. **si l'atteinte de la température cible est trop lente**, vous pouvez augmenter le ``coef_int`` pour donner plus de puissance au réchauffeur,
-4. **si l'atteinte de la température cible est trop rapide et que des oscillations apparaissent** autour de la cible, vous pouvez diminuer le ``coef_int`` pour donner moins de puissance au radiateur.
-
-En type `over_valve` le `on_percent` est ramené à une valeur entre 0 et 100% et sert directement à commander l'ouverture de la vanne.
-
-Voir quelques situations à [examples](#some-results).
-
-# Capteurs
-
-Avec le thermostat sont disponibles des capteurs qui permettent de visualiser les alertes et l'état interne du thermostat. Ils sont disponibles dans les entités de l'appareil associé au thermostat :
-
-
-
-Dans l'ordre, il y a :
-1. l'entité principale climate de commande du thermostat,
-2. l'énergie consommée par le thermostat (valeur qui s'incrémente en permanence),
-3. l'heure de réception de la dernière température extérieure,
-4. l'heure de réception de la dernière température intérieure,
-5. la puissance moyenne de l'appareil sur le cycle (pour les TPI seulement),
-6. le temps passé à l'état éteint dans le cycle (TPI seulement),
-7. le temps passé à l'état allumé dans le cycle (TPI seulement),
-8. l'état de délestage,
-9. le pourcentage de puissance sur le cycle (TPI seulement),
-10. l'état de présence (si la gestion de la présence est configurée),
-11. l'état de sécurité,
-12. l'état de l'ouverture (si la gestion des ouvertures est configurée),
-13. l'état du mouvement (si la gestion du mouvements est configurée)
-14. le pourcentage d'ouverture de la vanne (pour le type `over_valve`)
-
-Pour colorer les capteurs, ajouter ces lignes et personnalisez les au besoin, dans votre configuration.yaml :
-
-```
-frontend:
- themes:
- versatile_thermostat_theme:
- state-binary_sensor-safety-on-color: "#FF0B0B"
- state-binary_sensor-power-on-color: "#FF0B0B"
- state-binary_sensor-window-on-color: "rgb(156, 39, 176)"
- state-binary_sensor-motion-on-color: "rgb(156, 39, 176)"
- state-binary_sensor-presence-on-color: "lightgreen"
- state-binary_sensor-running-on-color: "orange"
-```
-et choisissez le thème ```versatile_thermostat_theme``` dans la configuration du panel. Vous obtiendrez quelque-chose qui va ressembler à ça :
-
-
-
-# Services
-
-Cette implémentation personnalisée offre des services spécifiques pour faciliter l'intégration avec d'autres composants Home Assistant.
-
-## Forcer la présence/occupation
-Ce service permet de forcer l'état de présence indépendamment du capteur de présence. Cela peut être utile si vous souhaitez gérer la présence via un service et non via un capteur. Par exemple, vous pouvez utiliser votre réveil pour forcer l'absence lorsqu'il est allumé.
-
-Le code pour appeler ce service est le suivant :
-```
-service : versatile_thermostat.set_presence
-Les données:
- présence : "off"
-cible:
- entity_id : climate.my_thermostat
-```
-
-## Modifier la température des préréglages
-Ce service est utile si vous souhaitez modifier dynamiquement la température préréglée. Au lieu de changer de préréglage, certains cas d'utilisation doivent modifier la température du préréglage. Ainsi, vous pouvez garder le Programmateur inchangé pour gérer le préréglage et ajuster la température du préréglage.
-Si le préréglage modifié est actuellement sélectionné, la modification de la température cible est immédiate et sera prise en compte au prochain cycle de calcul.
-
-Vous pouvez modifier l'une ou les deux températures (lorsqu'elles sont présentes ou absentes) de chaque préréglage.
-
-Utilisez le code suivant pour régler la température du préréglage :
-```
-service : versatile_thermostat.set_preset_temperature
-date:
- preset : boost
- temperature : 17,8
- temperature_away : 15
-target:
- entity_id : climate.my_thermostat
-```
-
-Ou pour changer le pré-réglage du mode Air Conditionné (AC) ajoutez un préfixe `_ac`` au nom du preset comme ceci :
-```
-service: versatile_thermostat.set_preset_temperature
-data:
- preset: boost_ac
- temperature: 25
- temperature_away: 30
-target:
- entity_id: climate.my_thermostat
-```
-
->  _*Notes*_
- - après un redémarrage, les préréglages sont réinitialisés à la température configurée. Si vous souhaitez que votre changement soit permanent, vous devez modifier le préréglage de la température dans la configuration de l'intégration.
-
-## Modifier les paramètres de sécurité
-Ce service permet de modifier dynamiquement les paramètres de sécurité décrits ici [Configuration avancée](#configuration-avancée).
-Si le thermostat est en mode ``security`` les nouveaux paramètres sont appliqués immédiatement.
-
-Pour changer les paramètres de sécurité utilisez le code suivant :
-```
-service : versatile_thermostat.set_safety
-data:
- min_on_percent: "0.5"
- default_on_percent: "0.1"
- delay_min: 60
-target:
- entity_id : climate.my_thermostat
-```
-
-## ByPass Window Check
-Ce service permet d'activer ou non un bypass de la vérification des fenetres.
-Il permet de continuer à chauffer même si la fenetre est detectée ouverte.
-Mis à ``true`` les modifications de status de la fenetre n'auront plus d'effet sur le thermostat, remis à ``false`` cela s'assurera de désactiver le thermostat si la fenetre est toujours ouverte.
-
-Pour changer le paramètre de bypass utilisez le code suivant :
-```
-service : versatile_thermostat.set_window_bypass
-data:
- window_bypass: true
-target:
- entity_id : climate.my_thermostat
-```
-
-# Evènements
-Les évènements marquant du thermostat sont notifiés par l'intermédiaire du bus de message.
-Les évènements notifiés sont les suivants:
-
-- ``versatile_thermostat_security_event`` : un thermostat entre ou sort du preset ``security``
-- ``versatile_thermostat_power_event`` : un thermostat entre ou sort du preset ``power``
-- ``versatile_thermostat_temperature_event`` : une ou les deux mesures de température d'un thermostat n'ont pas été mis à jour depuis plus de `safety_delay_min`` minutes
-- ``versatile_thermostat_hvac_mode_event`` : le thermostat est allumé ou éteint. Cet évènement est aussi diffusé au démarrage du thermostat
-- ``versatile_thermostat_preset_event`` : un nouveau preset est sélectionné sur le thermostat. Cet évènement est aussi diffusé au démarrage du thermostat
-- ``versatile_thermostat_central_boiler_event`` : un évènement indiquant un changement dans l'état de la chaudière.
-
-Si vous avez bien suivi, lorsqu'un thermostat passe en mode sécurité, 3 évènements sont déclenchés :
-1. ``versatile_thermostat_temperature_event`` pour indiquer qu'un thermomètre ne répond plus,
-2. ``versatile_thermostat_preset_event`` pour indiquer le passage en preset ```security```,
-3. ``versatile_thermostat_hvac_mode_event`` pour indiquer l'extinction éventuelle du thermostat
-
-Chaque évènement porte les valeurs clés de l'évènement (températures, preset courant, puissance courante, ...) ainsi que les états du thermostat.
-
-Vous pouvez très facilement capter ses évènements dans une automatisation par exemple pour notifier les utilisateurs.
-
-# Attributs personnalisés
-
-Pour régler l'algorithme, vous avez accès à tout le contexte vu et calculé par le thermostat via des attributs dédiés. Vous pouvez voir (et utiliser) ces attributs dans l'IHM "Outils de développement / états" de HA. Entrez votre thermostat et vous verrez quelque chose comme ceci :
-
-
-Les attributs personnalisés sont les suivants :
-
-| Attribut | Signification |
-| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| ``hvac_modes`` | La liste des modes supportés par le thermostat |
-| ``temp_min`` | La température minimale |
-| ``temp_max`` | La température maximale |
-| ``preset_modes`` | Les préréglages visibles pour ce thermostat. Les préréglages cachés ne sont pas affichés ici |
-| ``temperature_actuelle`` | La température actuelle telle que rapportée par le capteur |
-| ``temperature`` | La température cible |
-| ``action_hvac`` | L'action en cours d'exécution par le réchauffeur. Peut être inactif, chauffage |
-| ``preset_mode`` | Le préréglage actuellement sélectionné. Peut être l'un des 'preset_modes' ou un préréglage caché comme power |
-| ``[eco/confort/boost]_temp`` | La température configurée pour le préréglage xxx |
-| ``[eco/confort/boost]_away_temp`` | La température configurée pour le préréglage xxx lorsque la présence est désactivée ou not_home |
-| ``temp_power`` | La température utilisée lors de la détection de la perte |
-| ``on_percent`` | Le pourcentage sur calculé par l'algorithme TPI |
-| ``on_time_sec`` | La période On en sec. Doit être ```on_percent * cycle_min``` |
-| ``off_time_sec`` | La période d'arrêt en sec. Doit être ```(1 - on_percent) * cycle_min``` |
-| ``cycle_min`` | Le cycle de calcul en minutes |
-| ``function`` | L'algorithme utilisé pour le calcul du cycle |
-| ``tpi_coef_int`` | Le ``coef_int`` de l'algorithme TPI |
-| ``tpi_coef_ext`` | Le ``coef_ext`` de l'algorithme TPI |
-| ``saved_preset_mode`` | Le dernier preset utilisé avant le basculement automatique du preset |
-| ``saved_target_temp`` | La dernière température utilisée avant la commutation automatique |
-| ``window_state`` | Le dernier état connu du capteur de fenêtre. Aucun si la fenêtre n'est pas configurée |
-| ``is_window_bypass`` | True si le bypass de la détection d'ouverture et activé |
-| ``motion_state`` | Le dernier état connu du capteur de mouvement. Aucun si le mouvement n'est pas configuré |
-| ``overpowering_state`` | Le dernier état connu du capteur surpuissant. Aucun si la gestion de l'alimentation n'est pas configurée |
-| ``presence_state`` | Le dernier état connu du capteur de présence. Aucun si la gestion de présence n'est pas configurée |
-| ``safety_delay_min`` | Le délai avant d'activer le mode de sécurité lorsque un des 2 capteurs de température n'envoie plus de mesures |
-| ``safety_min_on_percent`` | Pourcentage de chauffe en dessous duquel le thermostat ne passera pas en sécurité |
-| ``safety_default_on_percent`` | Pourcentage de chauffe utilisé lorsque le thermostat est en sécurité |
-| ``last_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température interne |
-| ``last_ext_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température extérieure |
-| ``security_state`` | L'état de sécurité. vrai ou faux |
-| ``minimal_activation_delay_sec`` | Le délai d'activation minimal en secondes |
-| ``last_update_datetime`` | La date et l'heure au format ISO8866 de cet état |
-| ``friendly_name`` | Le nom du thermostat |
-| ``supported_features`` | Une combinaison de toutes les fonctionnalités prises en charge par ce thermostat. Voir la documentation officielle sur l'intégration climatique pour plus d'informations |
-| ``valve_open_percent`` | Le pourcentage d'ouverture de la vanne |
-| ``regulated_target_temperature`` | La température de consigne calculée par l'auto-régulation |
-| ``is_inversed`` | True si la commande est inversée (fil pilote avec diode) |
-| ``is_controlled_by_central_mode`` | True si le VTherm peut être controlé de façon centrale |
-| ``last_central_mode`` | Le dernier mode central utilisé (None si le VTherm n'est pas controlé en central) |
-| ``is_used_by_central_boiler`` | Indique si le VTherm peut contrôler la chaudière centrale |
-| ``auto_start_stop_enable`` | Indique si le VTherm est autorisé à s'auto démarrer/arrêter |
-| ``auto_start_stop_level`` | Indique le niveau d'auto start/stop |
-| ``hvac_off_reason`` | Indique la raison de l'arrêt (hvac_off) du VTherm. Ce peut être Window, Auto-start/stop ou Manuel |
-
-# Quelques résultats
-
-**Convergence de la température vers la cible configurée par preset:**
-
-
-[Cycle de marche/arrêt calculé par l'intégration :](https://)
-
-
-**Coef_int trop élevé (oscillations autour de la cible)**
-
-
-**Évolution du calcul de l'algorithme**
-
-Voir le code de ce composant [[ci-dessous](#even-better-with-apex-chart-to-tune-your-thermostat)]
-
-**Thermostat finement réglé**
-Merci [impuR_Shozz](https://forum.hacf.fr/u/impur_shozz/summary) !
-On peut voir une stabilité autour de la température cible (consigne) et lorsqu'à cible le on_percent (puissance) est proche de 0,3 ce qui semble une très bonne valeur.
-
-
-
-Enjoy !
-
-# Encore mieux
-
-## Bien mieux avec le Versatile Thermostat UI Card
-Une carte spéciale pour le Versatile Thermostat a été développée (sur la base du Better Thermostat). Elle est dispo ici [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card) et propose une vision moderne de tous les status du VTherm :
-
-
-
-## Encore mieux avec le composant Scheduler !
-
-Afin de profiter de toute la puissance du Versatile Thermostat, je vous invite à l'utiliser avec https://github.com/nielsfaber/scheduler-component
-En effet, le composant scheduler propose une gestion de la base climatique sur les modes prédéfinis. Cette fonctionnalité a un intérêt limité avec le thermostat générique mais elle devient très puissante avec le Versatile Thermostat :
-
-À partir d'ici, je suppose que vous avez installé Versatile Thermostat et Scheduler Component.
-
-Dans Scheduler, ajoutez un planning :
-
-
-
-Choisissez le groupe "climat", choisissez une (ou plusieurs) entité(s), sélectionnez "MAKE SCHEME" et cliquez sur suivant :
-(il est possible de choisir "SET PRESET", mais je préfère utiliser "MAKE SCHEME")
-
-
-
-Définissez votre schéma de mode et enregistrez :
-
-
-
-
-Dans cet exemple, j'ai réglé le mode ECO pendant la nuit et le jour lorsqu'il n'y a personne à la maison BOOST le matin et CONFORT le soir.
-
-
-J'espère que cet exemple vous aidera, n'hésitez pas à me faire part de vos retours !
-
-## Encore bien mieux avec la custom:simple-thermostat front integration
-Le ``custom:simple-thermostat`` [ici](https://github.com/nervetattoo/simple-thermostat) est une excellente intégration qui permet une certaine personnalisation qui s'adapte bien à ce thermostat.
-Vous pouvez avoir quelque chose comme ça très facilement 
-Exemple de configuration :
-
-```
- type: custom:simple-thermostat
- entity: climate.thermostat_sam2
- layout:
- step: row
- label:
- temperature: T°
- state: Etat
- hide:
- state: false
- control:
- hvac:
- _name: Mode
- preset:
- _name: Preset
- sensors:
- - entity: sensor.total_puissance_radiateur_sam2
- icon: mdi:lightning-bolt-outline
- header:
- toggle:
- entity: input_boolean.etat_ouverture_porte_sam
- name: Porte sam
-```
-
-Vous pouvez personnaliser ce composant à l'aide du composant HACS card-mod pour ajuster les couleurs des alertes. Exemple pour afficher en rouge les alertes sécurité et délestage :
-
-```
- card_mod:
- style: |
- {% if is_state('binary_sensor.thermostat_chambre_security_state', 'on') %}
- ha-card .body .sensor-heading ha-icon[icon="mdi:alert-outline"] {
- color: red;
- }
- {% endif %}
- {% if is_state('binary_sensor.thermostat_chambre_overpowering_state', 'on') %}
- ha-card .body .sensor-heading ha-icon[icon="mdi:flash"] {
- color: red;
- }
- {% endif %}
-```
-
-
-## Toujours mieux avec Plotly pour régler votre thermostat
-Vous pouvez obtenir une courbe comme celle présentée dans [some results](#some-results) avec une sorte de configuration de graphique Plotly uniquement en utilisant les attributs personnalisés du thermostat décrits [ici](#custom-attributes) :
-
-Remplacez les valeurs entre [[ ]] par les votres.
-```
-- type: custom:plotly-graph
- entities:
- - entity: '[[climate]]'
- attribute: temperature
- yaxis: y1
- name: Consigne
- - entity: '[[climate]]'
- attribute: current_temperature
- yaxis: y1
- name: T°
- - entity: '[[climate]]'
- attribute: ema_temp
- yaxis: y1
- name: Ema
- - entity: '[[climate]]'
- attribute: regulated_target_temperature
- yaxis: y1
- name: Regulated T°
- - entity: '[[slope]]'
- name: Slope
- fill: tozeroy
- yaxis: y9
- fillcolor: rgba(100, 100, 100, 0.3)
- line:
- color: rgba(100, 100, 100, 0.9)
- hours_to_show: 4
- refresh_interval: 10
- height: 800
- config:
- scrollZoom: true
- layout:
- margin:
- r: 50
- legend:
- x: 0
- 'y': 1.2
- groupclick: togglegroup
- title:
- side: top right
- yaxis:
- visible: true
- position: 0
- yaxis9:
- visible: true
- fixedrange: false
- range:
- - -0.5
- - 0.5
- position: 1
- xaxis:
- rangeselector:
- 'y': 1.1
- x: 0.7
- buttons:
- - count: 1
- step: hour
- - count: 12
- step: hour
- - count: 1
- step: day
- - count: 7
- step: day
-```
-
-Exemple de courbes obtenues avec Plotly :
-
-
-
-## Et toujours de mieux en mieux avec l'AappDaemon NOTIFIER pour notifier les évènements
-Cette automatisation utilise l'excellente App Daemon nommée NOTIFIER développée par Horizon Domotique que vous trouverez en démonstration [ici](https://www.youtube.com/watch?v=chJylIK0ASo&ab_channel=HorizonDomotique) et le code est [ici](https://github.com/jlpouffier/home-assistant-config/blob/master/appdaemon/apps/notifier.py). Elle permet de notifier les utilisateurs du logement lorsqu'un des évènements touchant à la sécurité survient sur un des Versatile Thermostats.
-
-C'est un excellent exemple de l'utilisation des notifications décrites ici [notification](#notifications).
-
-```
-alias: Surveillance Mode Sécurité chauffage
-description: Envoi une notification si un thermostat passe en mode sécurité ou power
-trigger:
- - platform: event
- event_type: versatile_thermostat_security_event
- id: versatile_thermostat_security_event
- - platform: event
- event_type: versatile_thermostat_power_event
- id: versatile_thermostat_power_event
- - platform: event
- event_type: versatile_thermostat_temperature_event
- id: versatile_thermostat_temperature_event
-condition: []
-action:
- - choose:
- - conditions:
- - condition: trigger
- id: versatile_thermostat_security_event
- sequence:
- - event: NOTIFIER
- event_data:
- action: send_to_jmc
- title: >-
- Radiateur {{ trigger.event.data.name }} - {{
- trigger.event.data.type }} Sécurité
- message: >-
- Le radiateur {{ trigger.event.data.name }} est passé en {{
- trigger.event.data.type }} sécurité car le thermomètre ne répond
- plus.\n{{ trigger.event.data }}
- callback:
- - title: Stopper chauffage
- event: stopper_chauffage
- image_url: /media/local/alerte-securite.jpg
- click_url: /lovelace-chauffage/4
- icon: mdi:radiator-off
- tag: radiateur_security_alerte
- persistent: true
- - conditions:
- - condition: trigger
- id: versatile_thermostat_power_event
- sequence:
- - event: NOTIFIER
- event_data:
- action: send_to_jmc
- title: >-
- Radiateur {{ trigger.event.data.name }} - {{
- trigger.event.data.type }} Délestage
- message: >-
- Le radiateur {{ trigger.event.data.name }} est passé en {{
- trigger.event.data.type }} délestage car la puissance max est
- dépassée.\n{{ trigger.event.data }}
- callback:
- - title: Stopper chauffage
- event: stopper_chauffage
- image_url: /media/local/alerte-delestage.jpg
- click_url: /lovelace-chauffage/4
- icon: mdi:radiator-off
- tag: radiateur_power_alerte
- persistent: true
- - conditions:
- - condition: trigger
- id: versatile_thermostat_temperature_event
- sequence:
- - event: NOTIFIER
- event_data:
- action: send_to_jmc
- title: >-
- Le thermomètre du radiateur {{ trigger.event.data.name }} ne
- répond plus
- message: >-
- Le thermomètre du radiateur {{ trigger.event.data.name }} ne
- répond plus depuis longtemps.\n{{ trigger.event.data }}
- image_url: /media/local/thermometre-alerte.jpg
- click_url: /lovelace-chauffage/4
- icon: mdi:radiator-disabled
- tag: radiateur_thermometre_alerte
- persistent: true
-mode: queued
-max: 30
-```
-
-
-# Les contributions sont les bienvenues !
-
-Si vous souhaitez contribuer, veuillez lire les [directives de contribution](CONTRIBUTING.md)
-
-# Dépannages
-
-
-Utilisation d'un Heatzy
-
-## Utilisation d'un Heatzy
-
-L'utilisation d'un Heatzy est possible à la condition d'utiliser un switch virtuel sur ce modèle :
-```
-- platform: template
- switches:
- chauffage_sdb:
- unique_id: chauffage_sdb
- friendly_name: Chauffage salle de bain
- value_template: "{{ is_state_attr('climate.salle_de_bain', 'preset_mode', 'comfort') }}"
- icon_template: >-
- {% if is_state_attr('climate.salle_de_bain', 'preset_mode', 'comfort') %}
- mdi:radiator
- {% elif is_state_attr('climate.salle_de_bain', 'preset_mode', 'away') %}
- mdi:snowflake
- {% else %}
- mdi:radiator-disabled
- {% endif %}
- turn_on:
- service: climate.set_preset_mode
- entity_id: climate.salle_de_bain
- data:
- preset_mode: "comfort"
- turn_off:
- service: climate.set_preset_mode
- entity_id: climate.salle_de_bain
- data:
- preset_mode: "eco"
-```
-Merci à @gael pour cet exemple.
-
-
-
-Utilisation d'un radiateur avec un fil pilote
-
-## Utilisation d'un radiateur avec un fil pilote
-Comme pour le Heatzy ci-dessus vous pouvez utiliser un switch virtuel qui va changer le preset de votre radiateur en fonction de l'état d'allumage du VTherm.
-Exemple :
-```
-- platform: template
- switches:
- radiateur_soan:
- friendly_name: radiateur_soan_inv
- value_template: "{{ is_state('switch.radiateur_soan', 'off') }}"
- turn_on:
- service: switch.turn_off
- data:
- entity_id: switch.radiateur_soan
- turn_off:
- service: switch.turn_on
- data:
- entity_id: switch.radiateur_soan
- icon_template: "{% if is_state('switch.radiateur_soan', 'on') %}mdi:radiator-disabled{% else %}mdi:radiator{% endif %}"
-```
-
-
-
-
-Utilisation d'un radiateur avec un module Nodon
-
-## Utilisation d'un radiateur avec un fil pilote
-Comme pour le Heatzy ci-dessus vous pouvez utiliser un switch virtuel qui va changer le preset de votre radiateur en fonction de l'état d'allumage du VTherm.
-Exemple :
-```
-- platform: template
- switches:
- chauffage_chb_parents:
- unique_id: chauffage_chb_parents
- friendly_name: Chauffage chambre parents
- value_template: "{{ is_state('select.fp_chb_parents_pilot_wire_mode', 'comfort') }}"
- icon_template: >-
- {% if is_state('select.fp_chb_parents_pilot_wire_mode', 'comfort') %}
- mdi:radiator
- {% elif is_state('select.fp_chb_parents_pilot_wire_mode', 'frost_protection') %}
- mdi:snowflake
- {% else %}
- mdi:radiator-disabled
- {% endif %}
- turn_on:
- service: select.select_option
- target:
- entity_id: select.fp_chb_parents_pilot_wire_mode
- data:
- option: comfort
- turn_off:
- service: select.select_option
- target:
- entity_id: select.fp_chb_parents_pilot_wire_mode
- data:
- option: eco
-```
-
-
-
-
-Seul le premier radiateur chauffe
-
-## Seul le premier radiateur chauffe
-
-En mode `over_switch` si plusieurs radiateurs sont configurés pour un même VTherm, l'alllumage va se faire de façon séquentiel pour lisser au plus possible les pics de consommation.
-Cela est tout à fait normal et voulu. C'est décrit ici : [Pour un thermostat de type ```thermostat_over_switch```](#pour-un-thermostat-de-type-thermostat_over_switch)
-
-
-
-
-Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne
-
-## Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne
-
-### Type `over_switch` ou `over_valve`
-Avec un VTherm de type `over_switch` ou `over_valve`, ce défaut montre juste que les paramètres de l'algorithme TPI sont mal réglés. Voir [Algorithme TPI](#algorithme-tpi) pour optimiser les réglages.
-
-### Type `over_climate`
-Avec un VTherm de type `over_climate`, la régulation est faite par le `climate` sous-jacent directement et VTherm se contente de lui transmettre les consignes. Donc si le radiateur chauffe alors que la température de consigne est dépassée, c'est certainement que sa mesure de température interne est biaisée. Ca arrive très souvent avec les TRV et les clims réversibles qui ont un capteur de température interne, soit trop près de l'élément de chauffe (donc trop froid l'hiver).
-
-Exemple de discussion autour de ces sujets: [#348](https://github.com/jmcollin78/versatile_thermostat/issues/348), [#316](https://github.com/jmcollin78/versatile_thermostat/issues/316), [#312](https://github.com/jmcollin78/versatile_thermostat/discussions/312), [#278](https://github.com/jmcollin78/versatile_thermostat/discussions/278)
-
-Pour s'en sortir, VTherm est équipé d'une fonction nommée auto-régulation qui permet d'adapter la consigne envoyée au sous-jacent jusqu'à ce que la consigne soit respectée. Cette fonction permet de compenser le biais de mesure des thermomètres internes. Si le biais est important la régulation doit être importante. Voir [L'auto-régulation](#lauto-régulation) pour configurer l'auto-régulation.
-
-
-
-Régler les paramètres de détection d'ouverture de fenêtre en mode auto
-
-## Régler les paramètres de détection d'ouverture de fenêtre en mode auto
-
-Si vous n'arrivez pas à régler la fonction de détection des ouvertures en mode auto (cf. [auto](#le-mode-auto)), vous pouvez essayer de modifier les paramètres de l'algorithme de lissage de la température.
-En effet, la détection automatique d'ouverture est basée sur le calcul de la pente de la température (slope). Pour éviter les artefacts due à un capteur de température imprécis, cette pente est calculée sur une température lissée avec un algorithme de lissage nommée Exponential Moving Average (Moyenne mobile exponentielle).
-Cet algorithm possède 3 paramètres :
-1. `lifecycle_sec` : la durée en secondes prise en compte pour le lissage. Plus elle est forte et plus le lissage sera important mais plus il y aura de délai de détection,
-2. `max_alpha` : si deux mesures de température sont éloignées dans le temps, la deuxième aura un poid beaucoup fort. Le paramètre permet de limiter le poid d'une mesure qui arrive bien après la précédente. Cette valeur doit être comprise entre 0 et 1. Plus elle est faible et moins les valeurs éloignées sont prises en compte. La valeur par défaut est de 0,5. Cela fait que lorsqu'une nouvelle valeur de température ne pèsera jamais plus que la moitié de la moyenne mobile,
-3. `precision` : le nombre de chiffre après la virgule conservée pour le calcul de la moyenne mobile.
-
-Pour changer ses paramètres, il faut modifier le fichier `configuration.yaml` et ajouter la section suivante (les valeurs sont les valeurs par défaut):
-```
-versatile_thermostat:
- short_ema_params:
- max_alpha: 0.5
- halflife_sec: 300
- precision: 2
-```
-
-Ces paramètres sont sensibles et assez difficiles à régler. Merci de ne les utiliser que si vous savez ce que vous faites et que vos mesures de température ne sont pas déjà lisses.
-
-
-
-Pourquoi mon Versatile Thermostat se met en Securite ?
-
-## Pourquoi mon Versatile Thermostat se met en Securite ?
-Le mode sécurité est possible sur tous les types de VTherm . Il survient lorsqu'un des 2 thermomètres qui donne la température de la pièce ou la température extérieure n'a pas envoyé de valeur depuis plus de `safety_delay_min` minutes et que le radiateur chauffait à au moins `safety_min_on_percent`.
-
-Comme l'algorithme est basé sur les mesures de température, si elles ne sont plus reçues par le VTherm, il y a un risque de surchauffe et d'incendie. Pour éviter ça, lorsque les conditions rappelées ci-dessus sont détectées, la chauffe est limité au paramètre `safety_default_on_percent`. Cette valeur doit donc être raisonnablement faible (10% est une bonne valeur). Elle permet d'éviter un incendie tout en évitant de couper totalement le radiateur (risque de gel).
-
-Tous ces paramètres se règlent dans la dernière page de la configuration du VTherm : "Paramètres avancés".
-
-### Comment détecter le mode sécurité ?
-Le premier symptôme est une température anormalement basse avec un temps de chauffe faible à chaque cycle et régulier.
-Exemple:
-
-[security mode](images/security-mode-symptome1.png)
-
-Si vous avez installé la carte [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card), le VTherm en question aura cette forme là :
-
-[security mode UI Card](images/security-mode-symptome2.png)
-
-Vous pouvez aussi vérifier dans les attributs du VTherm les dates de réception des différentes dates. **Les attributs sont disponibles dans les Outils de développement / Etats**.
-
-Exemple :
-```
-security_state: true
-last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"
-last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00"
-last_update_datetime: "2023-12-06T18:43:28.351103+01:00"
-...
-safety_delay_min: 60
-```
-
-On voit que :
-1. le VTherm est bien en mode sécurité (`security_state: true`),
-2. l'heure courante est le 06/12/2023 à 18h43:28 (`last_update_datetime: "2023-12-06T18:43:28.351103+01:00"`),
-3. l'heure de dernière réception de la température intérieure est le 06/12/2023 à 18h43:28 (`last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"`). Elle est donc récente,
-4. l'heure de dernière réception de la température extérieure est le 06/12/2023 à 13h04:35 (`last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00`). C'est donc l'heure extérieure qui a plus de 5 h de retard et qui a provoquée le passage en mode sécurité, car le seuil est limité à 60 min (`safety_delay_min: 60`).
-
-### Comment être averti lorsque cela se produit ?
-Pour être averti, le VTherm envoie un évènement dès que ça se produit et un en fin d'alerte sécurité. Vous pouvez capter ces évènements dans une automatisation et envoyer une notification par exemple, faire clignoter un voyant, déclencher une sirène, ... A vous de voir.
-
-Pour manipuler les évènements générés par le VTherm, cf. [Eveènements](#evènements).
-
-### Comment réparer ?
-Cela va dépendre de la cause du problème :
-1. Si un capteur est en défaut, il faut le réparer (remettre des piles, le changer, vérifier l'intégration Météo qui donne la température extérieure, ...),
-2. Si le paramètre `safety_delay_min` est trop petit, cela rsique de générer beaucoup de fausses alertes. Une valeur correcte est de l'ordre de 60 min, surtout si vous avez des capteurs de température à pile.
-3. Certains capteurs de température, n'envoie pas de mesure si la température n'a pas changée. Donc en cas de température très stable pendant longtemps, le mode sécurité peut se déclencher. Ce n'est pas très grave puisqu'il s'enlève dès que le VTherm reçoit à nouveau une température. Sur certain thermomètre (TuYA par exemple), on peut forcer le délai max entre 2 mesures. Il conviendra de mettre un délai max < `safety_delay_min`,
-4. Dès que la température sera a nouveau reçue le mode sécurité s'enlèvera et les valeurs précédentes de preset, température cible et mode seront restaurées.
-
-
-
-Utilisation d'un groupe de personnes comme capteur de présence
-
-## Utilisation d'un groupe de personnes comme capteur de présence
-
-Malheureusement, les groupes de personnes ne sont pas reconnus comme des capteurs de présence. On ne peut donc pas les utiliser directement dans VTherm.
-Le contournement est de créer un template de binary_sensor avec le code suivant :
-
-Fichier `template.yaml` :
-```
-- binary_sensor:
- - name: maison_occupee
- unique_id: maison_occupee
- state: "{{is_state('person.person1', 'home') or is_state('person.person2', 'home') or is_state('input_boolean.force_presence', 'on')}}"
- device_class: occupancy
-```
-
-Vous noterez dans cet exemple, l'utilisation d'un input_boolean nommé force_presence qui permet de forcer le capteur à `True` et ainsi de forcer les VTherm qui l'utilise avec présence active. Ca permet par exemple, de forcer un pré-chauffage du logement lors du départ du travail, ou lorsqu'une personne non reconnue nativement dans HA est présente.
-
-Fichier `configuration.yaml`:
-```
-...
-template: !include templates.yaml
-...
-```
-
-
-
-Activer les logs du Versatile Thermostat
-
-## Activer les logs du Versatile Thermostat
-Des fois, vous aurez besoin d'activer les logs pour afiner les analyses. Pour cela, éditer le fichier `logger.yaml` de votre configuration et configurer les logs comme suit :
-```
-default: xxxx
-logs:
- custom_components.versatile_thermostat: info
-```
-Vous devez recharger la configuration yaml (Outils de dev / Yaml / Toute la configuration Yaml) ou redémarrer Home Assistant pour que ce changement soit pris en compte.
-
-
-
-***
-
-[versatile_thermostat]: https://github.com/jmcollin78/versatile_thermostat
-[buymecoffee]: https://www.buymeacoffee.com/jmcollin78
-[buymecoffeebadge]: https://img.shields.io/badge/Buy%20me%20a%20beer-%245-orange?style=for-the-badge&logo=buy-me-a-beer
-[commits-shield]: https://img.shields.io/github/commit-activity/y/jmcollin78/versatile_thermostat.svg?style=for-the-badge
-[commits]: https://github.com/jmcollin78/versatile_thermostat/commits/master
-[hacs]: https://github.com/custom-components/hacs
-[hacs_badge]: https://img.shields.io/badge/HACS-Custom-41BDF5.svg?style=for-the-badge
-[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge
-[forum]: https://community.home-assistant.io/
-[license-shield]: https://img.shields.io/github/license/jmcollin78/versatile_thermostat.svg?style=for-the-badge
-[maintenance-shield]: https://img.shields.io/badge/maintainer-Joakim%20Sørensen%20%40ludeeus-blue.svg?style=for-the-badge
-[releases-shield]: https://img.shields.io/github/release/jmcollin78/versatile_thermostat.svg?style=for-the-badge
-[releases]: https://github.com/jmcollin78/versatile_thermostat/releases
diff --git a/documentation/en/releases.md b/documentation/en/releases.md
index c2d7c1c..1c80b03 100644
--- a/documentation/en/releases.md
+++ b/documentation/en/releases.md
@@ -2,6 +2,9 @@

+> * **Release 7.1**:
+> - Redesign of the load-shedding function (power management). Load-shedding is now handled centrally (previously, each _VTherm_ was autonomous). This allows for much more efficient management and prioritization of load-shedding on devices that are close to the setpoint. Note that you must have a centralized configuration with power management enabled for this to work. More info [here](./feature-power.md).
+
> * **Release 6.8**:
> - Added a new regulation method for `over_climate` type Versatile Thermostats. This method, called 'Direct Valve Control', allows direct control of a TRV valve and possibly an offset to calibrate the internal thermometer of your TRV. This new method has been tested with Sonoff TRVZB and extended to other TRV types where the valve can be directly controlled via `number` entities. More information [here](over-climate.md#lauto-régulation) and [here](self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne).
diff --git a/documentation/en/self-regulation.md b/documentation/en/self-regulation.md
index c9966d4..26d48a3 100644
--- a/documentation/en/self-regulation.md
+++ b/documentation/en/self-regulation.md
@@ -38,6 +38,8 @@ The opening rate calculation algorithm is based on the _TPI_ algorithm described
If a valve closure rate entity is configured, it will be set to 100 minus the opening rate to force the valve into a particular state.
+Note: for Sonoff TRVZB you should not configure the "closing degree" parameter. This leads to a bug in the TRV and the `hvac_action` is no more working.
+
### Other self-regulation
In the second case, Versatile Thermostat calculates an offset based on the following information:
diff --git a/documentation/fr/feature-power.md b/documentation/fr/feature-power.md
index a7df282..f4d0c92 100644
--- a/documentation/fr/feature-power.md
+++ b/documentation/fr/feature-power.md
@@ -6,11 +6,12 @@
Cette fonction vous permet de réguler la consommation électrique de vos radiateurs. Connue sous le nom de délestage, cette fonction vous permet de limiter la consommation électrique de votre appareil de chauffage si des conditions de surpuissance sont détectées.
Vous aurez besoin d'un **capteur de la puissance totale instantanée consommée** de votre logement ainsi que d'un **capteur donnant la puissance maximale autorisée**.
-Le comportement de cette fonction est basique :
-1. lorsque le _VTherm_ va allumer un équipement,
-2. il compare la dernière valeur connue du capteur de puissance consommée avec la dernière valeur de la puissance maximale autorisée. Si il reste une marge supérieure égale à la puissance déclarée des équipements du _VTherm_ alors le VTherm et ses équipements seront allumés. Sinon ils resteront éteints jusqu'au prochain cycle.
+Le comportement de cette fonction est le suivant :
+1. lorsqu'une nouvelle mesure de la puissance consommée du logement ou de la puissance maximale autorisée est reçue,
+2. si la puissance max est dépassée, la commande centrale va mettre en délestage tous les équipements actifs en commençant par ceux qui sont le plus près de la consigne. Il fait ça jusqu'à ce que suffisament de _VTherm_ soient délestés,
+3. si une réserve de puissance est disponible et que des _VTherms_ sont délestés, alors la commande centrale va délester autant d'équipements que possible en commençant par les plus loin de la consigne (au moment où il a été mis en délestage),
-ATTENTION: ce fonctionnement très basique **n'est pas une fonction de sécurité** mais plus une fonction permettant une optimisation de la consommation au prix d'une dégradation du chauffage. Des dépassements sont possibles selon la fréquence de remontée de vos capteurs de consommation, la puissance réellement utilisée par votre équipements. Vous devez donc toujours garder une marge de sécurité.
+ATTENTION: ce fonctionnement **n'est pas une fonction de sécurité** mais plus une fonction permettant une optimisation de la consommation au prix d'une dégradation du chauffage. Des dépassements sont possibles selon la fréquence de remontée de vos capteurs de consommation, la puissance réellement utilisée par votre équipements. Vous devez donc toujours garder une marge de sécurité.
Cas d'usage type:
1. vous avez un compteur électrique limité à 11 kW,
@@ -26,7 +27,7 @@ Si le vehicle n'est pas en charge, la puissance totale consommé est de 1 kW, un
## Configurer la gestion de la puissance
-Si vous avez choisi la fonctionnalité `Avec détection de la puissance`, vous la configurez de la façon suivante :
+Dans la configuration centralisée, si vous avez choisi la fonctionnalité `Avec détection de la puissance`, vous la configurez de la façon suivante :

@@ -37,10 +38,13 @@ Si vous avez choisi la fonctionnalité `Avec détection de la puissance`, vous l
Notez que toutes les valeurs de puissance doivent avoir les mêmes unités (kW ou W par exemple).
Le fait d'avoir un **capteur de puissance maximale autorisée**, vous permet de modifier la puissance maximale au fil du temps à l'aide d'un planificateur ou d'une automatisation.
+A noter, dû à la centralisation du délestage, il n'est pas possible de sur-charger les capteurs de consommation et de consommation maximale sur les _VTherms_. Cette configuration se fait forcément dans la configuration centralisée. Cf. [Configuration centralisée](./creation.md#configuration-centralisée)
+
>  _*Notes*_
>
> 1. En cas de délestage, le radiateur est réglé sur le préréglage nommé `power`. Il s'agit d'un préréglage caché, vous ne pouvez pas le sélectionner manuellement.
> 2. Gardez toujours une marge, car la puissance max peut être brièvement dépassée en attendant le calcul du prochain cycle typiquement ou par des équipements non régulés.
> 3. Si vous ne souhaitez pas utiliser cette fonctionnalité, décochez la dans le menu 'Fonctions'.
> 4. Si une _VTherm_ controlez plusieurs équipements, la **consommation électrique de votre chauffage** renseigné doit correspondre à la somme des puissances.
-> 5. Si vous utilisez la carte Verstatile Thermostat UI (cf. [ici](additions.md#bien-mieux-avec-le-versatile-thermostat-ui-card)), le délestage est représenté comme suit : .
\ No newline at end of file
+> 5. Si vous utilisez la carte Verstatile Thermostat UI (cf. [ici](additions.md#bien-mieux-avec-le-versatile-thermostat-ui-card)), le délestage est représenté comme suit : ,
+> 6. Un délai pouvant aller jusqu'à 20 sec est possible entre la réception d'une nouvelle valeur du capteur de puissance consommée et la mise en délestage de _VTherm_. Ce délai évite de trop solliciter Home Assistant si vous avez des remontées rapides de votre puissance consommée.
\ No newline at end of file
diff --git a/documentation/fr/feature-presets.md b/documentation/fr/feature-presets.md
index 06cb78f..2226965 100644
--- a/documentation/fr/feature-presets.md
+++ b/documentation/fr/feature-presets.md
@@ -6,7 +6,7 @@
## Configurer les températures préréglées
-Le mode préréglé (preset) vous permet de préconfigurer la température ciblée. Utilisé en conjonction avec Scheduler (voir [scheduler](additions#composant-scheduler-)) vous aurez un moyen puissant et simple d'optimiser la température par rapport à la consommation électrique de votre maison. Les préréglages gérés sont les suivants :
+Le mode préréglé (preset) vous permet de préconfigurer la température ciblée. Utilisé en conjonction avec Scheduler (voir [scheduler](additions.md#composant-scheduler-)) vous aurez un moyen puissant et simple d'optimiser la température par rapport à la consommation électrique de votre maison. Les préréglages gérés sont les suivants :
- **Eco** : l'appareil est en mode d'économie d'énergie
- **Confort** : l'appareil est en mode confort
- **Boost** : l'appareil tourne toutes les vannes à fond
diff --git a/documentation/fr/one-page.md b/documentation/fr/one-page.md
deleted file mode 100644
index ea7ae99..0000000
--- a/documentation/fr/one-page.md
+++ /dev/null
@@ -1,1676 +0,0 @@
-[![GitHub Release][releases-shield]][releases]
-[![GitHub Activity][commits-shield]][commits]
-[![License][license-shield]](LICENSE)
-[![hacs][hacs_badge]][hacs]
-[![BuyMeCoffee][buymecoffeebadge]][buymecoffee]
-
-
-
->  Cette intégration de thermostat vise à simplifier considérablement vos automatisations autour de la gestion du chauffage. Parce que tous les événements autour du chauffage classiques sont gérés nativement par le thermostat (personne à la maison ?, activité détectée dans une pièce ?, fenêtre ouverte ?, délestage de courant ?), vous n'avez pas à vous encombrer de scripts et d'automatismes compliqués pour gérer vos climats. ;-).
-
-- [Changements dans la version 6.0](#changements-dans-la-version-60)
- - [Entités de température pour les pre-réglages](#entités-de-température-pour-les-pre-réglages)
- - [Dans le cas d'une configuration centrale](#dans-le-cas-dune-configuration-centrale)
- - [Refonte du menu de configuration](#refonte-du-menu-de-configuration)
- - [Les options de menu 'Configuration incomplète' et 'Finaliser'](#les-options-de-menu-configuration-incomplète-et-finaliser)
-- [Changements dans la version 5.0](#changements-dans-la-version-50)
-- [Merci pour la bière buymecoffee](#merci-pour-la-bière-buymecoffee)
-- [Quand l'utiliser et ne pas l'utiliser](#quand-lutiliser-et-ne-pas-lutiliser)
- - [Incompatibilités](#incompatibilités)
-- [Pourquoi une nouvelle implémentation du thermostat ?](#pourquoi-une-nouvelle-implémentation-du-thermostat-)
-- [Comment installer cet incroyable Thermostat Versatile ?](#comment-installer-cet-incroyable-thermostat-versatile-)
- - [HACS installation (recommendé)](#hacs-installation-recommendé)
- - [Installation manuelle](#installation-manuelle)
-- [Configuration](#configuration)
- - [Création d'un nouveau Versatile Thermostat](#création-dun-nouveau-versatile-thermostat)
- - [Choix des attributs de base](#choix-des-attributs-de-base)
- - [Sélectionnez des entités pilotées (sous-jacents)](#sélectionnez-des-entités-pilotées-sous-jacents)
- - [Pour un thermostat de type ```thermostat_over_switch```](#pour-un-thermostat-de-type-thermostat_over_switch)
- - [Pour un thermostat de type ```thermostat_over_climate```:](#pour-un-thermostat-de-type-thermostat_over_climate)
- - [L'auto-régulation](#lauto-régulation)
- - [L'auto-régulation en mode Expert](#lauto-régulation-en-mode-expert)
- - [Compensation de la température interne](#compensation-de-la-température-interne)
- - [Synthèse de l'algorithme d'auto-régulation](#synthèse-de-lalgorithme-dauto-régulation)
- - [Le mode auto-fan](#le-mode-auto-fan)
- - [Le démarrage / arrêt automatique](#le-démarrage--arrêt-automatique)
- - [Pour un thermostat de type ```thermostat_over_valve```:](#pour-un-thermostat-de-type-thermostat_over_valve)
- - [Configurez les coefficients de l'algorithme TPI](#configurez-les-coefficients-de-lalgorithme-tpi)
- - [Configurer les températures préréglées](#configurer-les-températures-préréglées)
- - [Configurer les portes/fenêtres en allumant/éteignant les thermostats](#configurer-les-portesfenêtres-en-allumantéteignant-les-thermostats)
- - [Le mode capteur](#le-mode-capteur)
- - [Le mode auto](#le-mode-auto)
- - [Configurer le mode d'activité ou la détection de mouvement](#configurer-le-mode-dactivité-ou-la-détection-de-mouvement)
- - [Configurer la gestion de la puissance](#configurer-la-gestion-de-la-puissance)
- - [Configurer la présence (ou l'absence)](#configurer-la-présence-ou-labsence)
- - [Configuration avancée](#configuration-avancée)
- - [Le contrôle centralisé](#le-contrôle-centralisé)
- - [Le contrôle d'une chaudière centrale](#le-contrôle-dune-chaudière-centrale)
- - [Configuration](#configuration-1)
- - [Comment trouver le bon service ?](#comment-trouver-le-bon-service-)
- - [Les évènements](#les-évènements)
- - [Avertissement](#avertissement)
- - [Synthèse des paramètres](#synthèse-des-paramètres)
-- [Exemples de réglage](#exemples-de-réglage)
- - [Chauffage électrique](#chauffage-électrique)
- - [Chauffage central (chauffage gaz ou fuel)](#chauffage-central-chauffage-gaz-ou-fuel)
- - [Le capteur de température alimenté par batterie](#le-capteur-de-température-alimenté-par-batterie)
- - [Capteur de température réactif (sur secteur)](#capteur-de-température-réactif-sur-secteur)
- - [Mes presets](#mes-presets)
-- [Algorithme](#algorithme)
- - [Algorithme TPI](#algorithme-tpi)
-- [Capteurs](#capteurs)
-- [Services](#services)
- - [Forcer la présence/occupation](#forcer-la-présenceoccupation)
- - [Modifier la température des préréglages](#modifier-la-température-des-préréglages)
- - [Modifier les paramètres de sécurité](#modifier-les-paramètres-de-sécurité)
- - [ByPass Window Check](#bypass-window-check)
-- [Evènements](#evènements)
-- [Attributs personnalisés](#attributs-personnalisés)
-- [Quelques résultats](#quelques-résultats)
-- [Encore mieux](#encore-mieux)
- - [Bien mieux avec le Versatile Thermostat UI Card](#bien-mieux-avec-le-versatile-thermostat-ui-card)
- - [Encore mieux avec le composant Scheduler !](#encore-mieux-avec-le-composant-scheduler-)
- - [Encore bien mieux avec la custom:simple-thermostat front integration](#encore-bien-mieux-avec-la-customsimple-thermostat-front-integration)
- - [Toujours mieux avec Plotly pour régler votre thermostat](#toujours-mieux-avec-plotly-pour-régler-votre-thermostat)
- - [Et toujours de mieux en mieux avec l'AappDaemon NOTIFIER pour notifier les évènements](#et-toujours-de-mieux-en-mieux-avec-laappdaemon-notifier-pour-notifier-les-évènements)
-- [Les contributions sont les bienvenues !](#les-contributions-sont-les-bienvenues)
-- [Dépannages](#dépannages)
- - [Utilisation d'un Heatzy](#utilisation-dun-heatzy)
- - [Utilisation d'un radiateur avec un fil pilote](#utilisation-dun-radiateur-avec-un-fil-pilote)
- - [Utilisation d'un radiateur avec un fil pilote](#utilisation-dun-radiateur-avec-un-fil-pilote-1)
- - [Seul le premier radiateur chauffe](#seul-le-premier-radiateur-chauffe)
- - [Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne](#le-radiateur-chauffe-alors-que-la-température-de-consigne-est-dépassée-ou-ne-chauffe-pas-alors-que-la-température-de-la-pièce-est-bien-en-dessous-de-la-consigne)
- - [Type `over_switch` ou `over_valve`](#type-over_switch-ou-over_valve)
- - [Type `over_climate`](#type-over_climate)
- - [Régler les paramètres de détection d'ouverture de fenêtre en mode auto](#régler-les-paramètres-de-détection-douverture-de-fenêtre-en-mode-auto)
- - [Pourquoi mon Versatile Thermostat se met en Securite ?](#pourquoi-mon-versatile-thermostat-se-met-en-securite-)
- - [Comment détecter le mode sécurité ?](#comment-détecter-le-mode-sécurité-)
- - [Comment être averti lorsque cela se produit ?](#comment-être-averti-lorsque-cela-se-produit-)
- - [Comment réparer ?](#comment-réparer-)
- - [Utilisation d'un groupe de personnes comme capteur de présence](#utilisation-dun-groupe-de-personnes-comme-capteur-de-présence)
- - [Activer les logs du Versatile Thermostat](#activer-les-logs-du-versatile-thermostat)
-
-Ce composant personnalisé pour Home Assistant est une mise à niveau et est une réécriture complète du composant "Awesome thermostat" (voir [Github](https://github.com/dadge/awesome_thermostat)) avec l'ajout de fonctionnalités.
-
-
->  _*Historique des dernières versions*_
-> * **Release 6.5** :
-> - Ajout d'une nouvelle fonction permettant l'arrêt et la relance automatique d'un VTherm `over_climate` [585](https://github.com/jmcollin78/versatile_thermostat/issues/585)
-> - Amélioration de la gestion des ouvertures au démarrage. Permet de mémoriser et de recalculer l'état d'une ouverture au redémarage de Home Assistant [504](https://github.com/jmcollin78/versatile_thermostat/issues/504)
-> * **Release 6.0** :
-> - Ajout d'entités du domaine Number permettant de configurer les températures des presets [354](https://github.com/jmcollin78/versatile_thermostat/issues/354)
-> - Refonte complète du menu de configuration pour supprimer les températures et utililsation d'un menu au lieu d'un tunnel de configuration [354](https://github.com/jmcollin78/versatile_thermostat/issues/354)
-> * **Release 5.4** :
-> - Ajout du pas de température [#311](https://github.com/jmcollin78/versatile_thermostat/issues/311),
-> - ajout de seuils de régulation pour les `over_valve` pour éviter de trop vider la batterie des TRV [#338](https://github.com/jmcollin78/versatile_thermostat/issues/338),
-> - ajout d'une option permettant d'utiliser la température interne d'un TRV pour forcer l' auto-régulation [#348](https://github.com/jmcollin78/versatile_thermostat/issues/348),
-> - ajout d'une fonction de keep-alive pour les VTherm `over_switch` [#345](https://github.com/jmcollin78/versatile_thermostat/issues/345)
-
-
-Autres versions
-
-> * **Release 5.3** : Ajout d'une fonction de pilotage d'une chaudière centrale [#234](https://github.com/jmcollin78/versatile_thermostat/issues/234) - plus d'infos ici: [Le contrôle d'une chaudière centrale](#le-contrôle-dune-chaudière-centrale). Ajout de la possibilité de désactiver le mode sécurité pour le thermomètre extérieur [#343](https://github.com/jmcollin78/versatile_thermostat/issues/343)
-> * **Release 5.2** : Ajout d'un `central_mode` permettant de piloter tous les VTherms de façon centralisée [#158](https://github.com/jmcollin78/versatile_thermostat/issues/158).
-> * **Release 5.1** : Limitation des valeurs envoyées aux valves et au température envoyées au climate sous-jacent.
-> * **Release 5.0** : Ajout d'une configuration centrale permettant de mettre en commun les attributs qui peuvent l'être [#239](https://github.com/jmcollin78/versatile_thermostat/issues/239).
-> * **Release 4.3** : Ajout d'un mode auto-fan pour le type `over_climate` permettant d'activer la ventilation si l'écart de température est important [#223](https://github.com/jmcollin78/versatile_thermostat/issues/223).
-> * **Release 4.2** : Le calcul de la pente de la courbe de température se fait maintenant en °/heure et non plus en °/min [#242](https://github.com/jmcollin78/versatile_thermostat/issues/242). Correction de la détection automatique des ouvertures par l'ajout d'un lissage de la courbe de température .
-> * **Release 4.1** : Ajout d'un mode de régulation **Expert** dans lequel l'utilisateur peut spécifier ses propres paramètres d'auto-régulation au lieu d'utiliser les pre-programmés [#194](https://github.com/jmcollin78/versatile_thermostat/issues/194).
-> * **Release 4.0** : Ajout de la prise en charge de la **Versatile Thermostat UI Card**. Voir [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card). Ajout d'un mode de régulation **Slow** pour les appareils de chauffage à latence lente [#168](https://github.com/jmcollin78/versatile_thermostat/issues/168). Changement de la façon dont **la puissance est calculée** dans le cas de VTherm avec des équipements multi-sous-jacents [#146](https://github.com/jmcollin78/versatile_thermostat/issues/146). Ajout de la prise en charge de AC et Heat pour VTherm via un interrupteur également [#144](https://github.com/jmcollin78/versatile_thermostat/pull/144)
-> * **Release 3.8**: Ajout d'une **fonction d'auto-régulation** pour les thermostats `over climate` dont la régulation est faite par le climate sous-jacent. Cf. [L'auto-régulation](#lauto-régulation) et [#129](https://github.com/jmcollin78/versatile_thermostat/issues/129). Ajout de la **possibilité d'inverser la commande** pour un thermostat `over switch` pour adresser les installations avec fil pilote et diode [#124](https://github.com/jmcollin78/versatile_thermostat/issues/124).
-> * **Release 3.7**: Ajout du type de **Versatile Thermostat `over valve`** pour piloter une vanne TRV directement ou tout autre équipement type gradateur pour le chauffage. La régulation se fait alors directement en agissant sur le pourcentage d'ouverture de l'entité sous-jacente : 0 la vanne est coupée, 100 : la vanne est ouverte à fond. Cf. [#131](https://github.com/jmcollin78/versatile_thermostat/issues/131). Ajout d'une fonction permettant le bypass de la détection d'ouverture [#138](https://github.com/jmcollin78/versatile_thermostat/issues/138). Ajout de la langue Slovaque
-> * **Release 3.6**: Ajout du paramètre `motion_off_delay` pour améliorer la gestion de des mouvements [#116](https://github.com/jmcollin78/versatile_thermostat/issues/116), [#128](https://github.com/jmcollin78/versatile_thermostat/issues/128). Ajout du mode AC (air conditionné) pour un VTherm over switch. Préparation du projet Github pour faciliter les contributions [#127](https://github.com/jmcollin78/versatile_thermostat/issues/127)
-> * **Release 3.5**: Plusieurs thermostats sont possibles en "thermostat over climate" mode [#113](https://github.com/jmcollin78/versatile_thermostat/issues/113)
-> * **Release 3.4**: bug fix et exposition des preset temperatures pour le mode AC [#103](https://github.com/jmcollin78/versatile_thermostat/issues/103)
-> * **Release 3.3**: ajout du mode Air Conditionné (AC). Cette fonction vous permet d'utiliser le mode AC de votre thermostat sous-jacent. Pour l'utiliser, vous devez cocher l'option "Uitliser le mode AC" et définir les valeurs de température pour les presets et pour les presets en cas d'absence
-> * **Release 3.2** : ajout de la possibilité de commander plusieurs switch à partir du même thermostat. Dans ce mode, les switchs sont déclenchés avec un délai pour minimiser la puissance nécessaire à un instant (on minimise les périodes de recouvrement). Voir [Configuration](#sélectionnez-des-entités-pilotées)
-> * **Release 3.1** : ajout d'une détection de fenêtres/portes ouvertes par chute de température. Cette nouvelle fonction permet de stopper automatiquement un radiateur lorsque la température chute brutalement. Voir [Le mode auto](#le-mode-auto)
-> * **Release majeure 3.0** : ajout d'un équipement thermostat et de capteurs (binaires et non binaires) associés. Beaucoup plus proche de la philosphie Home Assistant, vous avez maintenant un accès direct à l'énergie consommée par le radiateur piloté par le thermostat et à plein d'autres capteurs qui seront utiles dans vos automatisations et dashboard.
-> * **release 2.3** : ajout de la mesure de puissance et d'énergie du radiateur piloté par le thermostat.
-> * **release 2.2** : ajout de fonction de sécurité permettant de ne pas laisser éternellement en chauffe un radiateur en cas de panne du thermomètre
-> * **release majeure 2.0** : ajout du thermostat "over climate" permettant de transformer n'importe quel thermostat en Versatile Thermostat et lui ajouter toutes les fonctions de ce dernier.
-
-
-
-Changements dans la version 6.0
-# Changements dans la version 6.0
-
-## Entités de température pour les pre-réglages
-Les températures des presets sont maintenant directement acessibles sous la forme d'entités reliés au VTherm.
-Exemple :
-
-
-
-Les entités Boost, Confort, Eco et Hors-gel permettent de régler directement les températures de ces présets sans avoir à reconfigurer le VTHerm dans les écrans de configuration.
-Ces modifications sont persistentent à un redémarrage et sont prises en compte immédiatement par le VTherm.
-
-En fonction des fonctions activées, la liste des températures peut être plus ou moins complète :
-1. Si la gestion de présence est activée, les presets en cas d'absence sont créés. Ils sont suffixés par 'abs' pour absence,
-2. Si la gestion de la climatisation (Mode AC) est activé, les presets en mode clim sont créés. Ils sont suffixés par 'clim' pour climatisation. Seul le preset Hors gel n'a pas d'équivalent en mode clim,
-3. Les différentes combinaison absent et clim peuvent être créés en fonction de la configuration du VTherm
-
-Si un VTherm utilise les preset de la configuration centrale, ces entités ne sont pas créées, car les températures des presets sont gérés par la configuration centrale.
-
-### Dans le cas d'une configuration centrale
-Si vous avez configuré une configuration centrale, celle-ci possède aussi ses propres presets qui répondent au même règles qu'énoncées ci-dessus.
-Exemple d'une configuration centrale avec gestion de présence et mode AC (climatisation) :
-
-
-
-Dans le cas d'un changement d'une température de la configuration centrale, tous les VTherm qui utilisent ce preset sont immédiatement mis à jour.
-
-## Refonte du menu de configuration
-Le menu de configuration a été totalement revu. Il s'adapte dynamiquement aux choix de l'utilisateur et permet d'accéder directement aux réglages de la fonction voulue sans avoir à dérouler tous le tunnel de configuration.
-
-Pour créer un nouveau VTherm, il faudra d'abord choisir le type de VTherm :
-
-
-
-Puis, vous accédez maintenant au menu de configuration suivant :
-
-
-
-Chaque partie à configurer est accessible directement, sans avoir à dérouler tout le tunnel de configuration comme précédemment.
-
-Vous noterez l'option de menu nommée `Fonctions` qui permet de choisir quelles fonctions vont être implémentées pour ce VTherm :
-
-
-
-En fonction de vos choix, le menu principal s'adaptera pour ajouter les options nécessaires.
-
-Exemple de menu avec toutes les fonctions cochées :
-
-
-Vous pouvez constater que les options 'Détection des ouvertures', 'Détection de mouvement', 'Gestion de la puissance' et 'Gestion de présence' ont été ajoutées. Vous pouvez alors les configurer.
-
-### Les options de menu 'Configuration incomplète' et 'Finaliser'
-
-La dernière option du menu est spéciale. Elle permet de valider la création du VTherm lorsque toutes les fonctions ont été correctement configurées.
-Si l'une options n'est pas correctement configurée, la dernière option est la suivante :
-
-
-
-Sa sélection ne fait rien mais vous empêche de finaliser la création (resp. la modification) du VTherm.
-**Vous devez alors chercher dans les options laquelle manque**.
-
-Une fois que toute la configuration est valide, la dernière option se transforme en :
-
-
-
-Cliquez sur cette option pour créér (resp. modifier) le VTherm :
-
-
-
-
-
-Changements dans la version 5.0
-
-# Changements dans la version 5.0
-
-Vous pouvez maintenant définir une configuration centrale qui va vous permettre de mettre en commun sur tous vos VTherms (ou seulement une partie), certains attributs. Pour utiliser cette possibilité, vous devez :
-1. Créer un VTherm de type "Configuration Centrale",
-2. Saisir les attributs de cette configuration centrale
-
-Pour l'utiliser ensuite dans les autres VTherms, vous devez les reconfigurer et à chaque fois que c'est possible cocher la case "Utiliser la configuration centrale". Cette case à cocher apparait dans tous les groupes d'attributs qui peuvent avoir recours à la configuration centrale : attributs principaux, TPI, ouvertures, mouvement, puissance, présence et paramètres avancés.
-
-Les attributs configurable dans la configuration centrale est listée ici : [Synthèse des paramètres](#synthèse-des-paramètres).
-
-Lors d'un changement sur la configuration centrale, tous les VTherms seront rechargés pour tenir compte de ces changements.
-
-En conséquence toute la phase de paramètrage d'un VTherm a été profondemment modifiée pour pouvoir utiliser la configuration centrale ou surcharger les valeurs de la configuration centrale par des valeurs propre au VTherm en cours de configuration.
-
-
-
-# Merci pour la bière [buymecoffee](https://www.buymeacoffee.com/jmcollin78)
-Un grand merci à @salabur, @pvince83, @bergoglio, @EPicLURcher, @ecolorado66, @Kriss1670, @maia, @f.maymil, @moutte69, @Jerome, @Gunnar M, @Greg.o, @John Burgess, @abyssmal, @capinfo26, @Helge, @MattG @Mexx62, @Someone, @Lajull, @giopeco, @fredericselier, @philpagan, @studiogriffanti, @Edwin, @Sebbou, @Gerard R., @John Burgess, @Sylvoliv, @cdenfert, @stephane.l, @jms92100 pour les bières. Ca fait très plaisir et ça m'encourage à continuer !
-
-
-# Quand l'utiliser et ne pas l'utiliser
-Ce thermostat peut piloter 3 types d'équipements :
-1. un radiateur qui ne fonctionne qu'en mode marche/arrêt (nommé ```thermostat_over_switch```). La configuration minimale nécessaire pour utiliser ce type thermostat est :
- 1. un équipement comme un radiateur (un ```switch``` ou équivalent),
- 2. une sonde de température pour la pièce (ou un input_number),
- 3. un capteur de température externe (pensez à l'intégration météo si vous n'en avez pas)
-2. un autre thermostat qui a ses propres modes de fonctionnement (nommé ```thermostat_over_climate```). Pour ce type de thermostat la configuration minimale nécessite :
- 1. un équipement - comme une climatisation, une valve thermostatique - qui est pilotée par sa propre entity de type ```climate```,
-3. un équipement qui peut prendre une valeur de 0 à 100% (nommée ```thermostat_over_valve```). A 0 le chauffage est coupé, 100% il est ouvert à fond. Ce type permet de piloter une valve thermostatique (cf. valve Shelly) qui expose une entité de type `number.` permetttant de piloter directement l'ouverture de la vanne. Versatile Thermostat régule la température de la pièce en jouant sur le pourcentage d'ouverture, à l'aide des capteurs de température intérieur et extérieur en utilisant l'algorithme TPI décrit ci-dessous.
-
-Le type `over_climate` vous permet d'ajouter à votre équipement existant toutes les fonctionnalités apportées par VersatileThermostat. L'entité climate VersatileThermostat contrôlera votre entité climate sous-jacente, l'éteindra si les fenêtres sont ouvertes, la fera passer en mode Eco si personne n'est présent, etc. Voir [ici] (#pourquoi-un-nouveau-thermostat-implémentation). Pour ce type de thermostat, tous les cycles de chauffage sont contrôlés par l'entité climate sous-jacente et non par le thermostat polyvalent lui-même. Une fonction facultative d'auto-régulation permet au Versatile Thermostat d'ajuster la température donnée en consigne au sous-jacent afin d'atteindre la consigne.
-
-Les installations avec fil pilote et diode d'activation bénéficie d'une option qui permet d'inverser la commande on/off du radiateur sous-jacent. Pour cela, utilisez le type `over switch` et cochez l'option d'inversion de la commande.
-
-## Incompatibilités
-Certains thermostat de type TRV sont réputés incompatibles avec le Versatile Thermostat. C'est le cas des vannes suivantes :
-1. les vannes POPP de Danfoss avec retour de température. Il est impossible d'éteindre cette vanne et elle s'auto-régule d'elle-même causant des conflits avec le VTherm,
-2. Les thermostats « Homematic » (et éventuellement Homematic IP) sont connus pour rencontrer des problèmes avec le Versatile Thermostat en raison des limitations du protocole RF sous-jacent. Ce problème se produit particulièrement lorsque vous essayez de contrôler plusieurs thermostats Homematic à la fois dans une seule instance de VTherm. Afin de réduire la charge du cycle de service, vous pouvez par ex. regroupez les thermostats avec des procédures spécifiques à Homematic (par exemple en utilisant un thermostat mural) et laissez Versatile Thermostat contrôler uniquement le thermostat mural directement. Une autre option consiste à contrôler un seul thermostat et à propager les changements de mode CVC et de température par un automatisme,
-3. les thermostats de type Heatzy qui ne supportent pas les commandes de type set_temperature
-4. les thermostats de type Rointe ont tendance a se réveiller tout seul. Le reste fonctionne normalement.
-5. les TRV de type Aqara SRTS-A01 et MOES TV01-ZB qui n'ont pas le retour d'état `hvac_action` permettant de savoir si elle chauffe ou pas. Donc les retours d'état sont faussés, le reste à l'air fonctionnel.
-6. La clim Airwell avec l'intégration "Midea AC LAN". Si 2 commandes de VTherm sont trop rapprochées, la clim s'arrête d'elle même.
-7. Les climates basés sur l'intégration Overkiz ne fonctionnent pas. Il parait impossible d'éteindre ni même de changer la température sur ces systèmes.
-
-# Pourquoi une nouvelle implémentation du thermostat ?
-
-Ce composant nommé __Versatile thermostat__ gère les cas d'utilisation suivants :
-- Configuration via l'interface graphique d'intégration standard (à l'aide du flux Config Entry),
-- Utilisations complètes du **mode préréglages**,
-- Désactiver le mode préréglé lorsque la température est **définie manuellement** sur un thermostat,
-- Éteindre/allumer un thermostat lorsqu'une **porte ou des fenêtres sont ouvertes/fermées** après un certain délai,
-- Changer de preset lorsqu'une **activité est détectée** ou non dans une pièce pendant un temps défini,
-- Utiliser un algorithme **TPI (Time Proportional Interval)** grâce à l'algorithme [[Argonaute](https://forum.hacf.fr/u/argonaute/summary)] ,
-- Ajouter une **gestion de délestage** ou une régulation pour ne pas dépasser une puissance totale définie. Lorsque la puissance maximale est dépassée, un préréglage caché de « puissance » est défini sur l'entité climatique. Lorsque la puissance passe en dessous du maximum, le préréglage précédent est restauré.
-- La **gestion de la présence à domicile**. Cette fonctionnalité vous permet de modifier dynamiquement la température du préréglage en tenant compte d'un capteur de présence de votre maison.
-- Des **services pour interagir avec le thermostat** à partir d'autres intégrations : vous pouvez forcer la présence / la non-présence à l'aide d'un service, et vous pouvez modifier dynamiquement la température des préréglages et changer les paramètres de sécurité.
-- Ajouter des capteurs pour voir les états internes du thermostat,
-- Contrôle centralisé de tous les Versatile Thermostat pour les stopper tous, les passer tous en hors-gel, les forcer en mode Chauffage (l'hiver), les forcer en mode Climatisation (l'été).
-- Contrôle d'une chaudière centrale et des VTherm qui doivent contrôler cette chaudière.
-
-# Comment installer cet incroyable Thermostat Versatile ?
-
-## HACS installation (recommendé)
-
-1. Installez [HACS](https://hacs.xyz/). De cette façon, vous obtenez automatiquement les mises à jour.
-2. L'intégration Versatile Thermostat est maintenant proposée directement depuis l'interface HACF (onglet intégrations),
-3. recherchez et installez "Versatile Thermostat" dans HACS et cliquez sur "installer".
-4. Redémarrez Home Assistant.
-5. Ensuite, vous pouvez ajouter une intégration de Versatile Thermostat dans la page Paramètres / Intégrations. Vous ajoutez autant de thermostats dont vous avez besoin (généralement un par radiateur ou par groupe de radiateurs qui doivent être gérés ou par pompe dans le cas d'un chauffage centralisé)
-
-
-## Installation manuelle
-
-1. À l'aide de l'outil de votre choix, ouvrez le répertoire (dossier) de votre configuration HA (où vous trouverez `configuration.yaml`).
-2. Si vous n'avez pas de répertoire (dossier) `custom_components`, vous devez le créer.
-3. Dans le répertoire (dossier) `custom_components`, créez un nouveau dossier appelé `versatile_thermostat`.
-4. Téléchargez _tous_ les fichiers du répertoire `custom_components/versatile_thermostat/` (dossier) dans ce référentiel.
-5. Placez les fichiers que vous avez téléchargés dans le nouveau répertoire (dossier) que vous avez créé.
-6. Redémarrez l'assistant domestique
-7. Configurer la nouvelle intégration du Versatile Thermostat
-
-# Configuration
-
--- VTherm = Versatile Thermostat dans la suite de ce document --
-
->  _*Notes*_
->
-> Trois façons de configurer les VTherms sont disponibles :
-> 1. Chaque Versatile Thermostat est entièrement configurée de manière indépendante. Choisissez cette option si vous ne souhaitez avoir aucune configuration ou gestion centrale.
-> 2. Certains aspects sont configurés de manière centralisée. Cela permet par ex. définir la température min/max, la détection de fenêtre ouverte,… au niveau d'une instance centrale et unique. Pour chaque VTherm que vous configurez, vous pouvez alors choisir d'utiliser la configuration centrale ou de la remplacer par des paramètres personnalisés.
-> 3. En plus de cette configuration centralisée, tous les VTherm peuvent être contrôlées par une seule entité de type `select`. Cette fonction est nommé `central_mode`. Cela permet de stopper / démarrer / mettre en hors gel / etc tous les VTherms en une seule fois. Pour chaque VTherm, l'utilisateur indique si il est concerné par ce `central_mode`.
-
-
-## Création d'un nouveau Versatile Thermostat
-
-Cliquez sur le bouton Ajouter une intégration dans la page d'intégration
-
-
-
-puis
-
-
-
-La configuration peut être modifiée via la même interface. Sélectionnez simplement le thermostat à modifier, appuyez sur "Configurer" et vous pourrez modifier certains paramètres ou la configuration.
-
-Suivez ensuite les étapes de configuration en sélectionnant dans le menu l'option à configurer.
-
-## Choix des attributs de base
-
-Choisisez le menu "Principaux attributs".
-
-
-
-Donnez les principaux attributs obligatoires :
-1. un nom (sera le nom de l'intégration et aussi le nom de l'entité climate)
-2. le type de thermostat ```thermostat_over_switch``` pour piloter un radiateur commandé par un switch ou ```thermostat_over_climate``` pour piloter un autre thermostat, ou ```thermostat_over_valve``` Cf. [ci-dessus](#pourquoi-une-nouvelle-implémentation-du-thermostat)
-4. un identifiant d'entité de capteur de température qui donne la température de la pièce dans laquelle le radiateur est installé,
-5. une entité capteur de température donnant la température extérieure. Si vous n'avez pas de capteur externe, vous pouvez utiliser l'intégration météo locale
-6. une durée de cycle en minutes. A chaque cycle, le radiateur s'allumera puis s'éteindra pendant une durée calculée afin d'atteindre la température ciblée (voir [preset](#configure-the-preset-temperature) ci-dessous). En mode ```over_climate```, le cycle ne sert qu'à faire des controles de base mais ne régule pas directement la température. C'est le ```climate``` sous-jacent qui le fait,
-7. les températures minimales et maximales du thermostat,
-8. une puissance de l'équipement ce qui va activer les capteurs de puissance et énergie consommée par l'appareil,
-9. la possibilité de controler le thermostat de façon centralisée. Cf [controle centralisé](#le-contrôle-centralisé),
-10. la liste des fonctionnalités qui seront utilisées pour ce thermostat. En fonction de vos choix, les écrans de configuration suivants s'afficheront ou pas.
-
->  _*Notes*_
-> 1. avec les types ```over_switch``` et ```over_valve```, les calculs sont effectués à chaque cycle. Donc en cas de changement de conditions, il faudra attendre le prochain cycle pour voir un changement. Pour cette raison, le cycle ne doit pas être trop long. **5 min est une bonne valeur**,
-> 2. si le cycle est trop court, le radiateur ne pourra jamais atteindre la température cible. Pour le radiateur à accumulation par exemple il sera sollicité inutilement.
-
-## Sélectionnez des entités pilotées (sous-jacents)
-
-En fonction de votre choix sur le type de thermostat, vous devrez choisir une ou plusieurs entités de type `switch`, `climate` ou `number`. Seules les entités compatibles avec le type sont présentées.
-
->  _*Comment choisir le type*_
-> Le choix du type est important. Même si il toujours possible de le modifier ensuite via l'IHM de configuration, il est préférable de se poser les quelques questions suivantes :
-> 1. **quel type d'équipement je vais piloter ?** Dans l'ordre voici ce qu'il faut faire :
-> 1. si vous avez une vanne thermostatique (TRV) commandable dans Home Assistant via une entité de type ```number``` (par exemple une _Shelly TRV_), choisissez le type `over_valve`. C'est le type le plus direct et qui assure la meilleure régulation,
-> 2. si vous avez un radiateur électrique (avec ou sans fil pilote) et qu'une entité de type ```switch``` permet de l'allumer ou de l'éteindre, alors le type ```over_switch``` est préférable. La régulation sera faite par le Versatile Thermostat en fonction de la température mesuré par votre thermomètre, à l'endroit ou vous l'avez placé,
-> 3. dans tous les autres cas, utilisez le mode ```over_climate```. Vous gardez votre entité ```climate``` d'origine et le Versatile Thermostat "ne fait que" piloter le on/off et la température cible de votre thermostat d'origine. La régulation est faite par votre thermostat d'origine dans ce cas. Ce mode est particulièrement adapté aux climatisations réversible tout-en-un dont l'exposition dans Home Assistant se limite à une entité de type ```climate```
-> 2. **quelle type de régulation je veux ?** Si l'équipement piloté possède son propre mécanisme de régulation (clim, certaine vanne TRV) et que cette régulation fonctionne bien, optez pour un ```over_climate```
-
-### Pour un thermostat de type ```thermostat_over_switch```
-
-
-Certains équipements nécessitent d'être périodiquement sollicités pour empêcher un arrêt de sécurité. Connu sous le nom de "keep-alive" cette fonction est activable en entrant un nombre de secondes non nul dans le champ d'intervalle keep-alive du thermostat. Pour désactiver la fonction ou en cas de doute, laissez-le vide ou entrez zéro (valeur par défaut).
-
-L'algorithme à utiliser est aujourd'hui limité à TPI est disponible. Voir [algorithme](#algorithme).
-Si plusieurs entités de type sont configurées, la thermostat décale les activations afin de minimiser le nombre de switch actif à un instant t. Ca permet une meilleure répartition de la puissance puisque chaque radiateur va s'allumer à son tour.
-Exemple de déclenchement synchronisé :
-
-
-Il est possible de choisir un thermostat over switch qui commande une climatisation en cochant la case "AC Mode". Dans ce cas, seul le mode refroidissement sera visible.
-
-Si votre équipement est commandé par un fil pilote avec un diode, vous aurez certainement besoin de cocher la case "Inverser la case". Elle permet de mettre le switch à On lorsqu'on doit étiendre l'équipement et à Off lorsqu'on doit l'allumer.
-
-### Pour un thermostat de type ```thermostat_over_climate```:
-
-
-Il est possible de choisir un thermostat over climate qui commande une climatisation réversible en cochant la case "AC Mode". Dans ce cas, selon l'équipement commandé vous aurez accès au chauffage et/ou au réfroidissement.
-
-#### L'auto-régulation
-Depuis la release 3.8, vous avez la possibilité d'activer la fonction d'auto-régulation. Cette fonction autorise VersatileThermostat à adapter la consigne de température donnée au climate sous-jacent afin que la température de la pièce atteigne réellement la consigne.
-Pour faire ça, le VersatileThermostat calcule un décalage basé sur les informations suivantes :
-1. la différence actuelle entre la température réelle et la température de consigne, appelé erreur brute,
-2. l'accumulation des erreurs passées,
-3. la différence entre la température extérieure et la consigne
-
-Ces trois informations sont combinées pour calculer le décalage qui sera ajouté à la consigne courante et envoyé au climate sous-jacent.
-
-La fonction d'auto-régulation se paramètre avec :
-1. une dégré de régulation :
- 1. Légère - pour des faibles besoin en auto-régulation. Dans ce mode, le décalage maximal sera de 1,5°,
- 2. Medium - pour une auto-régulation moyenne. Un décalage maximal de 2° est possible dans ce mode,
- 3. Forte - pour un fort besoin d'auto-régulation. Le décalage maximal est de 3° dans ce mode et l'auto-régulation réagira fortement aux changements de température.
-2. Un seuil d'auto-régulation : valeur en dessous de laquelle une nouvelle régulation ne sera pas appliquée. Imaginons qu'à un instant t, le décalage soit de 2°. Si au prochain calcul, le décalage est de 2.4°, il sera pas appliqué. Il ne sera appliqué que la différence entre 2 décalages sera au moins égal à ce seuil,
-3. Période minimal entre 2 auto-régulation : ce nombre, exprimé en minute, indique la durée entre 2 changements de régulation.
-
-Ces trois paramètres permettent de moduler la régulation et éviter de multiplier les envois de régulation. Certains équipements comme les TRV, les chaudières n'aiment pas qu'on change la consigne de température trop souvent.
-
->  _*Conseil de mise en place*_
-> 1. Ne démarrez pas tout de suite l'auto-régulation. Regardez comment se passe la régulation naturelle de votre équipement. Si vous constatez que la température de consigne n'est pas atteinte ou qu'elle met trop de temps à être atteinte, démarrez la régulation,
-> 2. D'abord commencez par une légère auto-régulation et gardez les deux paramètres avec leur valeurs par défaut. Attendez quelques jours et vérifiez si la situation s'est améliorée,
-> 3. Si ce n'est pas suffisant, passez en auto-régulation Medium, attendez une stabilisation,
-> 4. Si ce n'est toujours pas suffisant, passez en auto-régulation Forte,
-> 5. Si ce n'est toujours pas bon, il faudra passer en mode expert pour pouvoir régler les paramètres de régulation de façon fine. Voir en-dessous
-
-L'auto-régulation consiste à forcer l'équipement a aller plus loin en lui forçant sa température de consigne régulièrement. Sa consommation peut donc être augmentée, ainsi que son usure.
-
-#### L'auto-régulation en mode Expert
-
-En mode **Expert** pouvez régler finement les paramètres de l'auto-régulation pour atteindre vos objeetifs et optimiser au mieux. L'algorithme calcule l'écart entre la consigne et la température réelle de la pièce. Cet écard est appelé erreur.
-Les paramètres réglables sont les suivants :
-1. `kp` : le facteur appliqué à l'erreur brute,
-2. `ki` : le facteur appliqué à l'accumulation des erreurs,
-3. `k_ext` : le facteur appliqué à la différence entre la température intérieure et la température externe,
-4. `offset_max` : le maximum de correction (offset) que la régulation peut appliquer,
-5. `stabilization_threshold` : un seuil de stabilisation qui lorsqu'il est atteint par l'erreur remet à 0, l'accumulation des erreurs,
-6. `accumulated_error_threshold` : le maximum pour l'accumulation d'erreur.
-
-Pour le tuning il faut tenir compte de ces observations :
-1. `kp * erreur` va donner l'offset lié à l'erreur brute. Cet offset est directement proportionnel à l'erreur et sera à 0 lorsque la target sera atteinte,
-2. l'accumulation de l'erreur permet de corriger le stabilisation de la courbe alors qu'il reste une erreur. L'erreur s'accumule et l'offset augmente donc progressivement ce qui devrait finir par stabiliser sur la température cible. Pour que ce paramètre fondamental est un effet il faut qu'il soit pas trop petit. Une valeur moyenne est 30
-3. `ki * accumulated_error_threshold` va donner l'offset maximal lié à l'accumulation de l'erreur,
-4. `k_ext` permet d'appliquer tout de suite (sans attendre une accumulation des erreurs) une correction lorsque la température extérieure est très différente de la température cible. Si la stabilisation se fait trop haut lorsqu'il les écarts de température sont importants, c'est que ce paramètre est trop fort. Il devrait pouvoir être annulé totalement pour laisser faire les 2 premiers offset
-
-Les valeurs préprogrammées sont les suivantes :
-
-Slow régulation :
-
- kp: 0.2 # 20% of the current internal regulation offset are caused by the current difference of target temperature and room temperature
- ki: 0.8 / 288.0 # 80% of the current internal regulation offset are caused by the average offset of the past 24 hours
- k_ext: 1.0 / 25.0 # this will add 1°C to the offset when it's 25°C colder outdoor than indoor
- offset_max: 2.0 # limit to a final offset of -2°C to +2°C
- stabilization_threshold: 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: 2.0 * 288 # this allows up to 2°C long term offset in both directions
-
-Light régulation :
-
- kp: 0.2
- ki: 0.05
- k_ext: 0.05
- offset_max: 1.5
- stabilization_threshold: 0.1
- accumulated_error_threshold: 10
-
-
-Medium régulation :
-
- kp: 0.3
- ki: 0.05
- k_ext: 0.1
- offset_max: 2
- stabilization_threshold: 0.1
- accumulated_error_threshold: 20
-
-
-Strong régulation :
-
- """Strong parameters for regulation
- A set of parameters which doesn't take into account the external temp
- and concentrate to internal temp error + accumulated error.
- This should work for cold external conditions which else generates
- high external_offset"""
-
- kp: 0.4
- ki: 0.08
- k_ext: 0.0
- offset_max: 5
- stabilization_threshold: 0.1
- accumulated_error_threshold: 50
-
-Pour utiliser le mode Expert il vous faut déclarer les valeurs que vous souhaitez utiliser pour chacun de ces paramètres dans votre `configuration.yaml` sous la forme suivante :
-```
-versatile_thermostat:
- auto_regulation_expert:
- kp: 0.4
- ki: 0.08
- k_ext: 0.0
- offset_max: 5
- stabilization_threshold: 0.1
- accumulated_error_threshold: 50
-```
-et bien sur, configurer le mode auto-régulation du VTherm en mode Expert. Tous les VTherm en mode **Expert** utiliseront ces mêmes paramètres.
-
-Pour que les modifications soient prises en compte, il faut soit **relancer totalement Home Assistant** soit juste l'intégration Versatile Thermostat (Outils de dev / Yaml / rechargement de la configuration / Versatile Thermostat).
-
-#### Compensation de la température interne
-Quelque fois, il arrive que le thermomètre interne du sous-jacent (TRV, climatisation, ...) soit tellement faux que l' auto-régulation ne suffise pas à réguler.
-Cela arrive lorsque le thermomètre interne est trop près de la source de chaleur. La température interne monte alors beaucoup plus vite que la température de la pièce, ce qui génère des défauts dans la régulation.
-Exemple :
-1. la température de la pièce est 18°, la consigne est à 20°,
-2. la température interne de l'équipement est de 22°,
-3. si VTherm envoie 21° comme consigne (= 20° + 1° d'auto-regulation), alors l'équipement ne chauffera pas car sa température interne (22°) est au-dessus de la consigne (21°)
-
-Pour palier à ça, une nouvelle option facultative a été ajoutée en version 5.4 : 
-
-Lorsqu'elle est activée, cette fonction ajoutera l'écart entre la température interne et la température de la pièce à la consigne pour forcer le chauffage.
-Dans l'exemple ci-dessus, l'écart est de +4° (22° - 18°), donc VTherm enverra 25° (21°+4°) à l'équipement le forçant ainsi à chauffer.
-
-Cet écart est calculé pour chaque sous-jacent car chacun à sa propre température interne. Pensez à un VTherm qui serait relié à 3 TRV chacun avec sa température interne par exemple.
-
-On obtient alors une auto-régulation bien plus efficace qui évite l'eccueil des gros écarts de température interne défaillante.
-
-#### Synthèse de l'algorithme d'auto-régulation
-L'algorithme d'auto-régulation peut être synthétisé comme suit:
-
-1. initialiser la température cible comme la consigne du VTherm,
-1. Si l'auto-régulation est activée,
- 1. calcule de la température régulée (valable pour un VTherm),
- 2. prendre cette température comme cible,
-2. Pour chaque sous-jacent du VTherm,
- 1. Si "utiliser la température interne" est cochée,
- 1. calcule de l'écart (trv internal temp - room temp),
- 2. ajout de l'écart à la température cible,
- 3. envoie de la température cible ( = temp regulee + (temp interne - temp pièce)) au sous-jacent
-
-
-
-#### Le mode auto-fan
-Ce mode introduit en 4.3 permet de forcer l'usage de la ventilation si l'écart de température est important. En effet, en activant la ventilation, la répartition se fait plus rapidement ce qui permet de gagner du temps dans l'atteinte de la température cible.
-Vous pouvez choisir quelle ventilation vous voulez activer entre les paramètres suivants : Faible, Moyenne, Forte, Turbo.
-
-Il faut évidemment que votre équipement sous-jacent soit équipée d'une ventilation et quelle soit pilotable pour que cela fonctionne.
-Si votre équipement ne comprend pas le mode Turbo, le mode Forte` sera utilisé en remplacement.
-Une fois l'écart de température redevenu faible, la ventilation se mettra dans un mode "normal" qui dépend de votre équipement à savoir (dans l'ordre) : `Silence (mute)`, `Auto (auto)`, `Faible (low)`. La première valeur qui est possible pour votre équipement sera choisie.
-
-#### Le démarrage / arrêt automatique
-Cette fonction a été introduite en 6.5.0. Elle permet d'autoriser VTherm a stopper un équipement qui n'a pas besoin d'être allumé et de le redémarrer lorsque les conditions le réclame. Cette fonction est munie de 3 réglages qui permettent d'arrêter / relancer plus ou moins rapidement l'équipement.
-
-Pour l'utiliser, vous devez :
-1. Ajouter la fonction `Avec démmarrage et extinction automatique` dans le menu 'Fonctions',
-2. Paramétrer le niveau de détection dans l'option 'Allumage/extinction automatique' qui s'affiche lorsque la fonction a été activée. Vous choisissez le niveau de détection entre 'Lent', 'Moyen' et 'Rapide'. Les arrêts/relances seront plus nombreux avec le niveau 'Rapide'.
-
-Une fois paramétré, vous aurez maintenant une nouvelle entité de type `switch` qui vous permet d'autoriser ou non l'arrêt/relance automatique sans toucher à la configuration. Cette entité est disponible sur l'appareil VTherm et se nomme `switch._enable_auto_start_stop`. Cochez la pour autoriser le démarrage et extinction automatique.
-
-L'algorithme de détection est décrit [ici](https://github.com/jmcollin78/versatile_thermostat/issues/585).
-
-### Pour un thermostat de type ```thermostat_over_valve```:
-
-Vous pouvez choisir jusqu'à entité du domaine ```number``` ou ```ìnput_number``` qui vont commander les vannes.
-L'algorithme à utiliser est aujourd'hui limité à TPI est disponible. Voir [algorithme](#algorithme).
-
-Il est possible de choisir un thermostat over valve qui commande une climatisation en cochant la case "AC Mode". Dans ce cas, seul le mode refroidissement sera visible.
-
-## Configurez les coefficients de l'algorithme TPI
-
-Si vous avez choisi un thermostat de type ```over_switch``` ou ```over_valve``` et que vous sélectionnez l'option "TPI" vous menu, vous arriverez sur cette page :
-
-
-
-Vous devez donner :
-1. le coefficient coef_int de l'algorithme TPI,
-2. le coefficient coef_ext de l'algorithme TPI
-
-
-Pour plus d'informations sur l'algorithme TPI et son réglage, veuillez vous référer à [algorithm](#algorithm).
-
-## Configurer les températures préréglées
-
-Le mode préréglé (preset) vous permet de préconfigurer la température ciblée. Utilisé en conjonction avec Scheduler (voir [scheduler](#even-better-with-scheduler-component) vous aurez un moyen puissant et simple d'optimiser la température par rapport à la consommation électrique de votre maison. Les préréglages gérés sont les suivants :
- - **Eco** : l'appareil est en mode d'économie d'énergie
- - **Confort** : l'appareil est en mode confort
- - **Boost** : l'appareil tourne toutes les vannes à fond
-
- Si le mode AC est utilisé, vous pourrez aussi configurer les températures lorsque l'équipement en mode climatisation.
-
-**Aucun** est toujours ajouté dans la liste des modes, car c'est un moyen de ne pas utiliser les preset mais une **température manuelle** à la place.
-
-Les pré-réglages se font (depuis v6.0) directement depuis les entités du VTherm ou de la configuration centrale si vous utilisez la configuration centrale.
-
->  _*Notes*_
-> 1. En modifiant manuellement la température cible, réglez le préréglage sur Aucun (pas de préréglage). De cette façon, vous pouvez toujours définir une température cible même si aucun préréglage n'est disponible.
-> 2. Le préréglage standard ``Away`` est un préréglage caché qui n'est pas directement sélectionnable. Versatile Thermostat utilise la gestion de présence ou la gestion de mouvement pour régler automatiquement et dynamiquement la température cible en fonction d'une présence dans le logement ou d'une activité dans la pièce. Voir [gestion de la présence](#configure-the-presence-management).
-> 3. Si vous utilisez la gestion du délestage, vous verrez un préréglage caché nommé ``power``. Le préréglage de l'élément chauffant est réglé sur « puissance » lorsque des conditions de surpuissance sont rencontrées et que le délestage est actif pour cet élément chauffant. Voir [gestion de l'alimentation](#configure-the-power-management).
-> 4. si vous utilisez la configuration avancée, vous verrez le préréglage défini sur ``sécurité`` si la température n'a pas pu être récupérée après un certain délai
-> 5. Si vous ne souhaitez pas utiliser le préréglage, indiquez 0 comme température. Le préréglage sera alors ignoré et ne s'affichera pas dans le composant front
-
-## Configurer les portes/fenêtres en allumant/éteignant les thermostats
-
-Vous devez avoir choisi la fonctionnalité ```Avec détection des ouvertures``` dans la première page pour arriver sur cette page.
-La détecttion des ouvertures peut se faire de 2 manières:
-1. soit avec un capteur placé sur l'ouverture (mode capteur),
-2. soit en détectant une chute brutale de température (mode auto)
-
-### Le mode capteur
-En mode capteur, vous devez renseigner les informations suivantes:
-
-
-1. un identifiant d'entité d'un **capteur de fenêtre/porte**. Cela devrait être un binary_sensor ou un input_boolean. L'état de l'entité doit être 'on' lorsque la fenêtre est ouverte ou 'off' lorsqu'elle est fermée
-2. un **délai en secondes** avant tout changement. Cela permet d'ouvrir rapidement une fenêtre sans arrêter le chauffage.
-
-
-### Le mode auto
-En mode auto, la configuration est la suivante:
-
-
-1. un seuil de détection en degré par minute. Lorsque la température chute au delà de ce seuil, le thermostat s'éteindra. Plus cette valeur est faible et plus la détection sera rapide (en contre-partie d'un risque de faux positif),
-2. un seuil de fin de détection en degré par minute. Lorsque la chute de température repassera au-dessus cette valeur, le thermostat se remettra dans le mode précédent (mode et preset),
-3. une durée maximale de détection. Au delà de cette durée, le thermostat se remettra dans son mode et preset précédent même si la température continue de chuter.
-
-Pour régler les seuils il est conseillé de commencer avec les valeurs de référence et d'ajuster les seuils de détection. Quelques essais m'ont donné les valeurs suivantes (pour un bureau):
-- seuil de détection : 0,05 °C/min
-- seuil de non détection: 0 °C/min
-- durée max : 60 min.
-
-Un nouveau capteur "slope" a été ajouté pour tous les thermostats. Il donne la pente de la courbe de température en °C/min (ou °K/min). Cette pente est lissée et filtrée pour éviter les valeurs abérrantes des thermomètres qui viendraient pertuber la mesure.
-
-
-Pour bien régler il est conseillé d'affocher sur un même graphique historique la courbe de température et la pente de la courbe (le "slope") :
-
-
-Et c'est tout ! votre thermostat s'éteindra lorsque les fenêtres seront ouvertes et se rallumera lorsqu'il sera fermé.
-
->  _*Notes*_
-> 1. Si vous souhaitez utiliser **plusieurs capteurs de porte/fenêtre** pour automatiser votre thermostat, créez simplement un groupe avec le comportement habituel (https://www.home-assistant.io/integrations/binary_sensor.group/)
-> 2. Si vous n'avez pas de capteur de fenêtre/porte dans votre chambre, laissez simplement l'identifiant de l'entité du capteur vide,
-> 3. **Un seul mode est permis**. On ne peut pas configurer un thermostat avec un capteur et une détection automatique. Les 2 modes risquant de se contredire, il n'est pas possible d'avoir les 2 modes en même temps,
-> 4. Il est déconseillé d'utiliser le mode automatique pour un équipement soumis à des variations de température fréquentes et normales (couloirs, zones ouvertes, ...)
-
-## Configurer le mode d'activité ou la détection de mouvement
-
-Si vous avez choisi la fonctionnalité ```Avec détection de mouvement```, cliquez sur 'Valider' sur la page précédente et vous y arriverez :
-
-
-
-Nous allons maintenant voir comment configurer le nouveau mode Activité.
-Ce dont nous avons besoin:
-- un **capteur de mouvement**. ID d'entité d'un capteur de mouvement. Les états du capteur de mouvement doivent être « on » (mouvement détecté) ou « off » (aucun mouvement détecté)
-- une durée de **délai de mouvement** (en secondes) définissant combien de temps nous attendons la confirmation du mouvement avant de considérer le mouvement. Ce paramètre peut être supérieur à la temporision de votre détecteur de mouvement, sinon la détection se fera à chaque mouvement signalé par le détecteur,
-- une durée de fin **délai de mouvement** (en secondes) définissant combien de temps nous attendons la confirmation d'une fin de mouvement avant de ne plus considérer le mouvement.
-- un **préréglage de "mouvement" **. Nous utiliserons la température de ce préréglage lorsqu'une activité sera détectée.
-- un **préréglage "pas de mouvement"**. Nous utiliserons la température de ce deuxième préréglage lorsqu'aucune activité n'est détectée.
-
-Alors imaginons que nous voulions avoir le comportement suivant :
-- nous avons une pièce avec un thermostat réglé en mode activité, le mode "mouvement" choisi est confort (21,5°C), le mode "pas de mouvement" choisi est Eco (18.5°C) et la temporisation du mouvement est de 30 sec lors de la détection et de 5 minutes sur fin de détection.
-- la pièce est vide depuis un moment (aucune activité détectée), la température de cette pièce est de 18,5°
-- quelqu'un entre dans la pièce, une activité est détectée si le mouvement est présent pendant au moins 30 sec. La température passe alors à 21,5°
-- si le mouvement est présent pendant moins de 30 sec (passage rapide), la température reste sur 18,5°,
-- imaginons que la température soit passée sur 21,5°, lorsque la personne quitte la pièce, au bout de 5 min la température est ramenée à 18,5°.
-- si la personne revient avant les 5 minutes, la température reste sur 21,5°
-
-Pour que cela fonctionne, le thermostat doit être en mode préréglé « Activité ».
-
->  _*Notes*_
- 1. Sachez que comme pour les autres modes prédéfinis, ``Activity`` ne sera proposé que s'il est correctement configuré. En d'autres termes, les 4 clés de configuration doivent être définies si vous souhaitez voir l'activité dans l'interface de l'assistant domestique
-
-## Configurer la gestion de la puissance
-
-Si vous avez choisi la fonctionnalité ```Avec détection de la puissance```, cliquez sur 'Valider' sur la page précédente et vous arriverez ici :
-
-
-
-Cette fonction vous permet de réguler la consommation électrique de vos radiateurs. Connue sous le nom de délestage, cette fonction vous permet de limiter la consommation électrique de votre appareil de chauffage si des conditions de surpuissance sont détectées. Donnez un **capteur à la consommation électrique actuelle de votre maison**, un **capteur à la puissance max** qu'il ne faut pas dépasser, la **consommation électrique totale des équipements du VTherm** (en étape 1 de la configuration) et l'algorithme ne démarrera pas un radiateur si la puissance maximale sera dépassée après le démarrage du radiateur.
-
-Notez que toutes les valeurs de puissance doivent avoir les mêmes unités (kW ou W par exemple).
-Cela vous permet de modifier la puissance maximale au fil du temps à l'aide d'un planificateur ou de ce que vous voulez.
-
->  _*Notes*_
-> 1. En cas de délestage, le radiateur est réglé sur le préréglage nommé ```power```. Il s'agit d'un préréglage caché, vous ne pouvez pas le sélectionner manuellement.
-> 2. Je l'utilise pour éviter de dépasser la limite de mon contrat d'électricité lorsqu'un véhicule électrique est en charge. Cela crée une sorte d'autorégulation.
-> 3. Gardez toujours une marge, car la puissance max peut être brièvement dépassée en attendant le calcul du prochain cycle typiquement ou par des équipements non régulés.
-> 4. Si vous ne souhaitez pas utiliser cette fonctionnalité, laissez simplement l'identifiant des entités vide
-> 5. Si vous controlez plusieurs radiateurs, la **consommation électrique de votre chauffage** renseigné doit correspondre à la somme des puissances.
-
-## Configurer la présence (ou l'absence)
-
-Si sélectionnée en première page, cette fonction vous permet de modifier dynamiquement la température de tous les préréglages du thermostat configurés lorsque personne n'est à la maison ou lorsque quelqu'un rentre à la maison. Pour cela, vous devez configurer la température qui sera utilisée pour chaque préréglage lorsque la présence est désactivée. Lorsque le capteur de présence s'éteint, ces températures seront utilisées. Lorsqu'il se rallume, la température "normale" configurée pour le préréglage est utilisée. Voir [gestion des préréglages](#configure-the-preset-temperature).
-Pour configurer la présence remplissez ce formulaire :
-
-
-
-Pour cela, vous devez configurer :
-1. Un **capteur d'occupation** dont l'état doit être 'on' ou 'home' si quelqu'un est présent ou 'off' ou 'not_home' sinon,
-2. La **température utilisée en Eco** prédéfinie en cas d'absence,
-3. La **température utilisée en Confort** préréglée en cas d'absence,
-4. La **température utilisée en Boost** préréglée en cas d'absence
-
-Si le mode AC est utilisé, vous pourrez aussi configurer les températures lorsque l'équipement en mode climatisation.
-
-ATTENTION : les groupes de personnes ne fonctionnent pas en tant que capteur de présence. Ils ne sont pas reconnus comme un capteur de présence. Vous devez utiliser, un template comme décrit ici [Utilisation d'un groupe de personnes comme capteur de présence](#utilisation-dun-groupe-de-personnes-comme-capteur-de-présence).
-
->  _*Notes*_
-> 1. le changement de température est immédiat et se répercute sur le volet avant. Le calcul prendra en compte la nouvelle température cible au prochain calcul du cycle,
-> 2. vous pouvez utiliser le capteur direct person.xxxx ou un groupe de capteurs de Home Assistant. Le capteur de présence gère les états ``on`` ou ``home`` comme présents et les états ``off`` ou ``not_home`` comme absents.
-
-## Configuration avancée
-
-Ces paramètres permettent d'affiner le réglage du thermostat.
-Le formulaire de configuration avancée est le suivant :
-
-
-
-Le premier délai (minimal_activation_delay_sec) en secondes est le délai minimum acceptable pour allumer le chauffage. Lorsque le calcul donne un délai de mise sous tension inférieur à cette valeur, le chauffage reste éteint.
-
-Le deuxième délai (``safety_delay_min``) est le délai maximal entre deux mesures de température avant de régler le préréglage sur ``security``. Si le capteur de température ne donne plus de mesures de température, le thermostat et le radiateur passeront en mode ``security`` après ce délai. Ceci est utile pour éviter une surchauffe si la batterie de votre capteur de température est trop faible.
-
-Le troisième paramétre (``safety_min_on_percent``) est la valeur minimal de ``on_percent`` en dessous de laquelle le préréglage sécurité ne sera pas activé. Ce paramètre permet de ne pas mettre en sécurité un thermostat, si le radiateur piloté ne chauffe pas suffisament.
-Mettre ce paramètre à ``0.00`` déclenchera le préréglage sécurité quelque soit la dernière consigne de chauffage, à l'inverse ``1.00`` ne déclenchera jamais le préréglage sécurité ( ce qui revient à désactiver la fonction).
-
-Le quatrième param§tre (``safety_default_on_percent``) est la valeur de ``on_percent`` qui sera utilisée lorsque le thermostat passe en mode ``security``. Si vous mettez ``0`` alors le thermostat sera coupé lorsqu'il passe en mode ``security``, mettre 0,2% par exemple permet de garder un peu de chauffage (20% dans ce cas), même en mode ``security``. Ca évite de retrouver son logement totalement gelé lors d'une panne de thermomètre.
-
-Note: les paramètres `safety_min_on_percent` et `safety_default_on_percent` ne s'applique pas aux VTherms `over_climate`.
-
-Depuis la version 5.3 il est possible de désactiver la mise en sécurité suite à une absence de données du thermomètre extérieure. En effet, celui-ci ayant la plupart du temps un impact faible sur la régulation (dépendant de votre paramètrage), il est possible qu'il soit absent sans mettre en danger le logement. Pour cela, il faut ajouter les lignes suivantes dans votre `configuration.yaml` :
-```
-versatile_thermostat:
-...
- safety_mode:
- check_outdoor_sensor: false
-```
-Par défaut, le thermomètre extérieur peut déclencher une mise en sécurité si il n'envoit plus de valeur.
-
-Voir [exemple de réglages](#examples-tuning) pour avoir des exemples de réglage communs
-
->  _*Notes*_
-> 1. Lorsque le capteur de température viendra à la vie et renverra les températures, le préréglage sera restauré à sa valeur précédente,
-> 2. Attention, deux températures sont nécessaires : la température interne et la température externe et chacune doit donner la température, sinon le thermostat sera en préréglage "security",
-> 3. Un service est disponible qui permet de régler les 3 paramètres de sécurité. Ca peut servir à adapter la fonction de sécurité à votre usage,
-> 4. Pour un usage naturel, le ``safety_default_on_percent`` doit être inférieur à ``safety_min_on_percent``,
-> 5. Les thermostats de type ``thermostat_over_climate`` ne sont pas concernés par le mode security.
-
-## Le contrôle centralisé
-
-Depuis la release 5.2, si vous avez défini une configuration centralisée, vous avez une nouvelle entité nommée `select.central_mode` qui permet de piloter tous les VTherms avec une seule action. Pour qu'un VTherm soit contrôlable de façon centralisée, il faut que son attribut de configuration nommé `use_central_mode` soit vrai.
-
-Cette entité se présente sous la forme d'une liste de choix qui contient les choix suivants :
-1. `Auto` : le mode 'normal' dans lequel chaque VTherm se comporte comme dans les versions précédentes,
-2. `Stooped` : tous les VTherms sont mis à l'arrêt (`hvac_off`),
-3. `Heat only` : tous les VTherms sont mis en mode chauffage lorsque ce mode est supporté par le VTherm, sinon il est stoppé,
-3. `Cool only` : tous les VTherms sont mis en mode climatisation lorsque ce mode est supporté par le VTherm, sinon il est stoppé,
-4. `Frost protection` : tous les VTherms sont mis en preset hors-gel lorsque ce preset est supporté par le VTherm, sinon il est stoppé.
-
-Il est donc possible de contrôler tous les VTherms (que ceux que l'on désigne explicitement) avec un seul contrôle.
-Exemple de rendu :
-
-
-
-## Le contrôle d'une chaudière centrale
-
-Depuis la release 5.3, vous avez la possibilité de contrôler une chaudière centralisée. A partir du moment où il est possible de déclencher ou stopper cette chaudière depuis Home Assistant, alors Versatile Thermostat va pouvoir la commander directement.
-
-Le principe mis en place est globalement le suivant :
-1. une nouvelle entité de type `binary_sensor` et nommée par défaut `binary_sensor.central_boiler` est ajoutée,
-2. dans la configuration des VTherms vous indiquez si le VTherm doit contrôler la chaudière. En effet, dans une installation hétérogène, certains VTherm doivent commander la chaudière et d'autres non. Vous devez donc indiquer dans chaque configuration de VTherm si il contrôle la chaudière ou pas,
-3. le `binary_sensor.central_boiler` écoute les changements d'états des équipements des VTherm marqués comme contrôlant la chaudière,
-4. dès que le nombre d'équipements pilotés par le VTherm demandant du chauffage (ie son `hvac_action` passe à `Heating`) dépasse un seuil paramétrable, alors le `binary_sensor.central_boiler` passe à `on` et **si un service d'activation a été configuré, alors ce service est appelé**,
-5. si le nombre d'équipements nécessitant du chauffage repasse en dessous du seuil, alors le `binary_sensor.central_boiler` passe à `off` et si **un service de désactivation a été configuré, alors ce service est appelé**,
-6. vous avez accès à deux entités :
- - une de type `number` nommé par défaut `number.boiler_activation_threshold`, donne le seuil de déclenchement. Ce seuil est en nombre d'équipements (radiateurs) qui demande du chauffage.
- - une de type `sensor` nommé par défaut `sensor.nb_device_active_for_boiler`, donne le nombre d'équipements qui demande du chauffage. Par exemple, un VTherm ayant 4 vannes dont 3 demandes du chauffage fera passé ce capteur à 3. Seuls les équipements des VTherms qui sont marqués pour contrôler la chaudière centrale sont comptabilisés.
-
-Vous avez donc en permanence, les informations qui permettent de piloter et régler le déclenchement de la chaudière.
-
-Toutes ces entités sont rattachés au service de configuration centrale :
-
-
-### Configuration
-Pour configurer cette fonction, vous devez avoir une configuration centralisée (cf. [Configuration](#configuration)) et cochez la case 'Ajouter une chuadière centrale' :
-
-
-
-Sur la page suivante vous pouvez donner la configuration des services à appeler lors de l'allumage / extinction de la chaudière :
-
-
-
-Les services se configurent comme indiqués dans la page :
-1. le format général est `entity_id/service_id[/attribut:valeur]` (où `/attribut:valeur` est facultatif),
-2. `entity_id` est le nom de l'entité qui commande la chaudière sous la forme `domain.entity_name`. Par exemple: `switch.chaudiere` pour les chaudière commandée par un switch ou `climate.chaudière` pour une chaudière commandée par un thermostat ou tout autre entité qui permet le contrôle de la chaudière (il n'y a pas de limitation). On peut aussi commuter des entrées (`helpers`) comme des `input_boolean` ou `input_number`.
-3. `service_id` est le nom du service à appeler sous la forme `domain.service_name`. Par exemple: `switch.turn_on`, `switch.turn_off`, `climate.set_temperature`, `climate.set_hvac_mode` sont des exemples valides.
-4. pour certain service vous aurez besoin d'un paramètre. Cela peut être le 'Mode CVC' `climate.set_hvac_mode` ou la température cible pour `climate.set_temperature`. Ce paramètre doit être configuré sous la forme `attribut:valeur` en fin de chaine.
-
-Exemples (à ajuster à votre cas) :
-- `climate.chaudiere/climate.set_hvac_mode/hvac_mode:heat` : pour allumer le thermostat de la chaudière en mode chauffage,
-- `climate.chaudiere/climate.set_hvac_mode/hvac_mode:off` : pour stopper le thermostat de la chaudière,
-- `switch.pompe_chaudiere/switch.turn_on` : pour allumer le swicth qui alimente la pompe de la chaudière,
-- `switch.pompe_chaudiere/switch.turn_off` : pour allumer le swicth qui alimente la pompe de la chaudière,
-- ...
-
-### Comment trouver le bon service ?
-Pour trouver le services a utiliser, le mieux est d'aller dans "Outils de développement / Services", chercher le service a appelé, l'entité à commander et l'éventuel paramètre à donner.
-Cliquez sur 'Appeler le service'. Si votre chaudière s'allume vous avez la bonne configuration. Passez alors en mode Yaml et recopiez les paramètres.
-
-Exemple:
-
-Sous "Outils de développement / Service" :
-
-
-
-En mode yaml :
-
-
-
-Le service à configurer est alors le suivant: `climate.empty_thermostast/climate.set_hvac_mode/hvac_mode:heat` (notez la suppression du blanc dans `hvac_mode:heat`)
-
-Faite alors de même pour le service d'extinction et vous êtes parés.
-
-### Les évènements
-
-A chaque allumage ou extinction réussie de la chaudière un évènement est envoyé par Versatile Thermostat. Il peut avantageusement être capté par une automatisation, par exemple pour notifier un changement.
-Les évènements ressemblent à ça :
-
-Un évènement d'allumage :
-```
-event_type: versatile_thermostat_central_boiler_event
-data:
- central_boiler: true
- entity_id: binary_sensor.central_boiler
- name: Central boiler
- state_attributes: null
-origin: LOCAL
-time_fired: "2024-01-14T11:33:52.342026+00:00"
-context:
- id: 01HM3VZRJP3WYYWPNSDAFARW1T
- parent_id: null
- user_id: null
-```
-
-Un évènement d'extinction :
-```
-event_type: versatile_thermostat_central_boiler_event
-data:
- central_boiler: false
- entity_id: binary_sensor.central_boiler
- name: Central boiler
- state_attributes: null
-origin: LOCAL
-time_fired: "2024-01-14T11:43:52.342026+00:00"
-context:
- id: 01HM3VZRJP3WYYWPNSDAFBRW1T
- parent_id: null
- user_id: null
-```
-
-### Avertissement
-
->  _*Notes*_
-> Le contrôle par du logiciel ou du matériel de type domotique d'une chaudière centrale peut induire des risques pour son bon fonctionnement. Assurez-vous avant d'utiliser ces fonctions, que votre chaudière possède bien des fonctions de sécurité et que celles-ci fonctionnent. Allumer une chaudière si tous les robinets sont fermés peut générer de la sur-pression par exemple.
-
-
-Synthèse des paramètres
-
-## Synthèse des paramètres
-
-| Paramètre | Libellé | "over switch" | "over climate" | "over valve" | "configuration centrale" |
-| ----------------------------------------- | --------------------------------------------------------------------------------- | ------------- | ------------------- | ------------ | ------------------------ |
-| ``name`` | Nom | X | X | X | - |
-| ``thermostat_type`` | Type de thermostat | X | X | X | - |
-| ``temperature_sensor_entity_id`` | Temperature sensor entity id | X | X (auto-regulation) | X | - |
-| ``external_temperature_sensor_entity_id`` | Température de l'exterieur sensor entity id | X | X (auto-regulation) | X | X |
-| ``cycle_min`` | Durée du cycle (minutes) | X | X | X | - |
-| ``temp_min`` | Température minimale permise | X | X | X | X |
-| ``temp_max`` | Température maximale permise | X | X | X | X |
-| ``device_power`` | Puissance de l'équipement | X | X | X | - |
-| ``use_central_mode`` | Autorisation du contrôle centralisé | X | X | X | - |
-| ``use_window_feature`` | Avec détection des ouvertures | X | X | X | - |
-| ``use_motion_feature`` | Avec détection de mouvement | X | X | X | - |
-| ``use_power_feature`` | Avec gestion de la puissance | X | X | X | - |
-| ``use_presence_feature`` | Avec détection de présence | X | X | X | - |
-| ``heater_entity1_id`` | 1er radiateur | X | - | - | - |
-| ``heater_entity2_id`` | 2ème radiateur | X | - | - | - |
-| ``heater_entity3_id`` | 3ème radiateur | X | - | - | - |
-| ``heater_entity4_id`` | 4ème radiateur | X | - | - | - |
-| ``heater_keep_alive`` | Intervalle de rafraichissement du switch | X | - | - | - |
-| ``proportional_function`` | Algorithme | X | - | - | - |
-| ``climate_entity1_id`` | Thermostat sous-jacent | - | X | - | - |
-| ``climate_entity2_id`` | 2ème thermostat sous-jacent | - | X | - | - |
-| ``climate_entity3_id`` | 3ème thermostat sous-jacent | - | X | - | - |
-| ``climate_entity4_id`` | 4ème thermostat sous-jacent | - | X | - | - |
-| ``valve_entity1_id`` | Vanne sous-jacente | - | - | X | - |
-| ``valve_entity2_id`` | 2ème vanne sous-jacente | - | - | X | - |
-| ``valve_entity3_id`` | 3ème vanne sous-jacente | - | - | X | - |
-| ``valve_entity4_id`` | 4ème vanne sous-jacente | - | - | X | - |
-| ``ac_mode`` | utilisation de l'air conditionné (AC) ? | X | X | X | - |
-| ``tpi_coef_int`` | Coefficient à utiliser pour le delta de température interne | X | - | X | X |
-| ``tpi_coef_ext`` | Coefficient à utiliser pour le delta de température externe | X | - | X | X |
-| ``frost_temp`` | Température en preset Hors-gel | X | X | X | X |
-| ``window_sensor_entity_id`` | Détecteur d'ouverture (entity id) | X | X | X | - |
-| ``window_delay`` | Délai avant extinction (secondes) | X | X | X | X |
-| ``window_auto_open_threshold`` | Seuil haut de chute de température pour la détection automatique (en °/min) | X | X | X | X |
-| ``window_auto_close_threshold`` | Seuil bas de chute de température pour la fin de détection automatique (en °/min) | X | X | X | X |
-| ``window_auto_max_duration`` | Durée maximum d'une extinction automatique (en min) | X | X | X | X |
-| ``motion_sensor_entity_id`` | Détecteur de mouvement entity id | X | X | X | - |
-| ``motion_delay`` | Délai avant prise en compte du mouvement (seconds) | X | X | X | - |
-| ``motion_off_delay`` | Délai avant prise en compte de la fin de mouvement (seconds) | X | X | X | X |
-| ``motion_preset`` | Preset à utiliser si mouvement détecté | X | X | X | X |
-| ``no_motion_preset`` | Preset à utiliser si pas de mouvement détecté | X | X | X | X |
-| ``power_sensor_entity_id`` | Capteur de puissance totale (entity id) | X | X | X | X |
-| ``max_power_sensor_entity_id`` | Capteur de puissance Max (entity id) | X | X | X | X |
-| ``power_temp`` | Température si délestaqe | X | X | X | X |
-| ``presence_sensor_entity_id`` | Capteur de présence entity id (true si quelqu'un est présent) | X | X | X | - |
-| ``minimal_activation_delay`` | Délai minimal d'activation | X | - | - | X |
-| ``safety_delay_min`` | Délai maximal entre 2 mesures de températures | X | - | X | X |
-| ``safety_min_on_percent`` | Pourcentage minimal de puissance pour passer en mode sécurité | X | - | X | X |
-| ``auto_regulation_mode`` | Le mode d'auto-régulation | - | X | - | - |
-| ``auto_regulation_dtemp`` | La seuil d'auto-régulation | - | X | - | - |
-| ``auto_regulation_period_min`` | La période minimale d'auto-régulation | - | X | - | - |
-| ``inverse_switch_command`` | Inverse la commande du switch (pour switch avec fil pilote) | X | - | - | - |
-| ``auto_fan_mode`` | Mode de ventilation automatique | - | X | - | - |
-| ``auto_regulation_use_device_temp`` | Utilisation de la température interne du sous-jacent | - | X | - | - |
-| ``use_central_boiler_feature`` | Ajout du controle d'une chaudière centrale | - | - | - | X |
-| ``central_boiler_activation_service`` | Service d'activation de la chaudière | - | - | - | X |
-| ``central_boiler_deactivation_service`` | Service de desactivation de la chaudière | - | - | - | X |
-| ``used_by_controls_central_boiler`` | Indique si le VTherm contrôle la chaudière centrale | X | X | X | - |
-| ``use_auto_start_stop_feature`` | Indique si la fonction de démarrage/extinction automatique est activée | - | X | - | - |
-| ``auto_start_stop_lvel`` | Le niveau de détection de l'auto start/stop | - | X | - | - |
-
-
-# Exemples de réglage
-
-## Chauffage électrique
-- cycle : entre 5 et 10 minutes,
-- minimal_activation_delay_sec : 30 secondes
-
-## Chauffage central (chauffage gaz ou fuel)
-- cycle : entre 30 et 60 min,
-- minimal_activation_delay_sec : 300 secondes (à cause du temps de réponse)
-
-## Le capteur de température alimenté par batterie
-- safety_delay_min : 60 min (parce que ces capteurs sont paresseux)
-- safety_min_on_percent : 0,5 (50% - on passe en preset ``security`` si le radiateur chauffait plus de 50% du temps)
-- safety_default_on_percent : 0,1 (10% - en preset ``security``, on garde un fond de chauffe de 20% du temps)
-
-Il faut comprendre ces réglages comme suit :
-
-> Si le thermomètre n'envoie plus la température pendant 1 heure et que le pourcentage de chauffe (``on_percent``) était supérieur à 50 %, alors on ramène ce pourcentage de chauffe à 10 %.
-
-A vous d'adapter ces réglages à votre cas !
-
-Ce qui est important c'est de ne pas prendre trop de risque avec ces paramètres : supposez que vous êtes absent pour une longue période, que les piles de votre thermomètre arrivent en fin de vie, votre radiateur va chauffer 10% du temps pendant toute la durée de la panne.
-
-Versatile Thermostat vous permet d'être notifié lorsqu'un évènement de ce type survient. Mettez en place, les alertes qui vont bien dès l'utilisation de ce thermostat. Cf. (#notifications)
-
-## Capteur de température réactif (sur secteur)
-- safety_delay_min : 15 min
-- safety_min_on_percent : 0,7 (70% - on passe en preset ``security`` si le radiateur chauffait plus de 70% du temps)
-- safety_default_on_percent : 0,25 (25% - en preset ``security``, on garde un fond de chauffe de 25% du temps)
-
-## Mes presets
-Ceci est juste un exemple de la façon dont j'utilise le préréglage. A vous de vous adapter à votre configuration mais cela peut être utile pour comprendre son fonctionnement.
-``Hors gel`` : 10 °C
-``Éco`` : 17 °C
-``Confort`` : 19 °C
-``Boost`` : 20 °C
-
-Lorsque la présence est désactivée :
-``Hors gel`` : 10 °C
-``Éco`` : 16,5 °C
-``Confort`` : 17 °C
-``Boost`` : 18 °C
-
-Le détecteur de mouvement de mon bureau est configuré pour utiliser ``Boost`` lorsqu'un mouvement est détecté et ``Eco`` sinon.
-
-# Algorithme
-Cette intégration utilise un algorithme proportionnel. Un algorithme proportionnel est utile pour éviter l'oscillation autour de la température cible. Cet algorithme est basé sur un cycle qui alterne le chauffage et l'arrêt du chauffage. La proportion de chauffage par rapport à l'absence de chauffage est déterminée par la différence entre la température et la température cible. Plus grande est la différence et plus grande est la proportion de chauffage à l'intérieur du cycle.
-
-Cet algorithme fait converger la température et arrête d'osciller.
-
-## Algorithme TPI
-L'algorithme TPI consiste à calculer à chaque cycle un pourcentage d'état On vs Off pour le radiateur en utilisant la température cible, la température actuelle dans la pièce et la température extérieure actuelle. Cet algorithme n'est donc valable que pour les Versatile Thermostat qui régulent : `over_switch` et `over_valve`.
-
-Le pourcentage est calculé avec cette formule :
-
- on_percent = coef_int * (température cible - température actuelle) + coef_ext * (température cible - température extérieure)
- Ensuite, l'algo fait en sorte que 0 <= on_percent <= 1
-
-Les valeurs par défaut pour coef_int et coef_ext sont respectivement : ``0.6`` et ``0.01``. Ces valeurs par défaut conviennent à une pièce standard bien isolée.
-
-Pour régler ces coefficients, gardez à l'esprit que :
-1. **si la température cible n'est pas atteinte** après une situation stable, vous devez augmenter le ``coef_ext`` (le ``on_percent`` est trop bas),
-2. **si la température cible est dépassée** après une situation stable, vous devez diminuer le ``coef_ext`` (le ``on_percent`` est trop haut),
-3. **si l'atteinte de la température cible est trop lente**, vous pouvez augmenter le ``coef_int`` pour donner plus de puissance au réchauffeur,
-4. **si l'atteinte de la température cible est trop rapide et que des oscillations apparaissent** autour de la cible, vous pouvez diminuer le ``coef_int`` pour donner moins de puissance au radiateur.
-
-En type `over_valve` le `on_percent` est ramené à une valeur entre 0 et 100% et sert directement à commander l'ouverture de la vanne.
-
-Voir quelques situations à [examples](#some-results).
-
-# Capteurs
-
-Avec le thermostat sont disponibles des capteurs qui permettent de visualiser les alertes et l'état interne du thermostat. Ils sont disponibles dans les entités de l'appareil associé au thermostat :
-
-
-
-Dans l'ordre, il y a :
-1. l'entité principale climate de commande du thermostat,
-2. l'énergie consommée par le thermostat (valeur qui s'incrémente en permanence),
-3. l'heure de réception de la dernière température extérieure,
-4. l'heure de réception de la dernière température intérieure,
-5. la puissance moyenne de l'appareil sur le cycle (pour les TPI seulement),
-6. le temps passé à l'état éteint dans le cycle (TPI seulement),
-7. le temps passé à l'état allumé dans le cycle (TPI seulement),
-8. l'état de délestage,
-9. le pourcentage de puissance sur le cycle (TPI seulement),
-10. l'état de présence (si la gestion de la présence est configurée),
-11. l'état de sécurité,
-12. l'état de l'ouverture (si la gestion des ouvertures est configurée),
-13. l'état du mouvement (si la gestion du mouvements est configurée)
-14. le pourcentage d'ouverture de la vanne (pour le type `over_valve`)
-
-Pour colorer les capteurs, ajouter ces lignes et personnalisez les au besoin, dans votre configuration.yaml :
-
-```
-frontend:
- themes:
- versatile_thermostat_theme:
- state-binary_sensor-safety-on-color: "#FF0B0B"
- state-binary_sensor-power-on-color: "#FF0B0B"
- state-binary_sensor-window-on-color: "rgb(156, 39, 176)"
- state-binary_sensor-motion-on-color: "rgb(156, 39, 176)"
- state-binary_sensor-presence-on-color: "lightgreen"
- state-binary_sensor-running-on-color: "orange"
-```
-et choisissez le thème ```versatile_thermostat_theme``` dans la configuration du panel. Vous obtiendrez quelque-chose qui va ressembler à ça :
-
-
-
-# Services
-
-Cette implémentation personnalisée offre des services spécifiques pour faciliter l'intégration avec d'autres composants Home Assistant.
-
-## Forcer la présence/occupation
-Ce service permet de forcer l'état de présence indépendamment du capteur de présence. Cela peut être utile si vous souhaitez gérer la présence via un service et non via un capteur. Par exemple, vous pouvez utiliser votre réveil pour forcer l'absence lorsqu'il est allumé.
-
-Le code pour appeler ce service est le suivant :
-```
-service : versatile_thermostat.set_presence
-Les données:
- présence : "off"
-cible:
- entity_id : climate.my_thermostat
-```
-
-## Modifier la température des préréglages
-Ce service est utile si vous souhaitez modifier dynamiquement la température préréglée. Au lieu de changer de préréglage, certains cas d'utilisation doivent modifier la température du préréglage. Ainsi, vous pouvez garder le Programmateur inchangé pour gérer le préréglage et ajuster la température du préréglage.
-Si le préréglage modifié est actuellement sélectionné, la modification de la température cible est immédiate et sera prise en compte au prochain cycle de calcul.
-
-Vous pouvez modifier l'une ou les deux températures (lorsqu'elles sont présentes ou absentes) de chaque préréglage.
-
-Utilisez le code suivant pour régler la température du préréglage :
-```
-service : versatile_thermostat.set_preset_temperature
-date:
- preset : boost
- temperature : 17,8
- temperature_away : 15
-target:
- entity_id : climate.my_thermostat
-```
-
-Ou pour changer le pré-réglage du mode Air Conditionné (AC) ajoutez un préfixe `_ac`` au nom du preset comme ceci :
-```
-service: versatile_thermostat.set_preset_temperature
-data:
- preset: boost_ac
- temperature: 25
- temperature_away: 30
-target:
- entity_id: climate.my_thermostat
-```
-
->  _*Notes*_
- - après un redémarrage, les préréglages sont réinitialisés à la température configurée. Si vous souhaitez que votre changement soit permanent, vous devez modifier le préréglage de la température dans la configuration de l'intégration.
-
-## Modifier les paramètres de sécurité
-Ce service permet de modifier dynamiquement les paramètres de sécurité décrits ici [Configuration avancée](#configuration-avancée).
-Si le thermostat est en mode ``security`` les nouveaux paramètres sont appliqués immédiatement.
-
-Pour changer les paramètres de sécurité utilisez le code suivant :
-```
-service : versatile_thermostat.set_safety
-data:
- min_on_percent: "0.5"
- default_on_percent: "0.1"
- delay_min: 60
-target:
- entity_id : climate.my_thermostat
-```
-
-## ByPass Window Check
-Ce service permet d'activer ou non un bypass de la vérification des fenetres.
-Il permet de continuer à chauffer même si la fenetre est detectée ouverte.
-Mis à ``true`` les modifications de status de la fenetre n'auront plus d'effet sur le thermostat, remis à ``false`` cela s'assurera de désactiver le thermostat si la fenetre est toujours ouverte.
-
-Pour changer le paramètre de bypass utilisez le code suivant :
-```
-service : versatile_thermostat.set_window_bypass
-data:
- window_bypass: true
-target:
- entity_id : climate.my_thermostat
-```
-
-# Evènements
-Les évènements marquant du thermostat sont notifiés par l'intermédiaire du bus de message.
-Les évènements notifiés sont les suivants:
-
-- ``versatile_thermostat_security_event`` : un thermostat entre ou sort du preset ``security``
-- ``versatile_thermostat_power_event`` : un thermostat entre ou sort du preset ``power``
-- ``versatile_thermostat_temperature_event`` : une ou les deux mesures de température d'un thermostat n'ont pas été mis à jour depuis plus de `safety_delay_min`` minutes
-- ``versatile_thermostat_hvac_mode_event`` : le thermostat est allumé ou éteint. Cet évènement est aussi diffusé au démarrage du thermostat
-- ``versatile_thermostat_preset_event`` : un nouveau preset est sélectionné sur le thermostat. Cet évènement est aussi diffusé au démarrage du thermostat
-- ``versatile_thermostat_central_boiler_event`` : un évènement indiquant un changement dans l'état de la chaudière.
-
-Si vous avez bien suivi, lorsqu'un thermostat passe en mode sécurité, 3 évènements sont déclenchés :
-1. ``versatile_thermostat_temperature_event`` pour indiquer qu'un thermomètre ne répond plus,
-2. ``versatile_thermostat_preset_event`` pour indiquer le passage en preset ```security```,
-3. ``versatile_thermostat_hvac_mode_event`` pour indiquer l'extinction éventuelle du thermostat
-
-Chaque évènement porte les valeurs clés de l'évènement (températures, preset courant, puissance courante, ...) ainsi que les états du thermostat.
-
-Vous pouvez très facilement capter ses évènements dans une automatisation par exemple pour notifier les utilisateurs.
-
-# Attributs personnalisés
-
-Pour régler l'algorithme, vous avez accès à tout le contexte vu et calculé par le thermostat via des attributs dédiés. Vous pouvez voir (et utiliser) ces attributs dans l'IHM "Outils de développement / états" de HA. Entrez votre thermostat et vous verrez quelque chose comme ceci :
-
-
-Les attributs personnalisés sont les suivants :
-
-| Attribut | Signification |
-| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| ``hvac_modes`` | La liste des modes supportés par le thermostat |
-| ``temp_min`` | La température minimale |
-| ``temp_max`` | La température maximale |
-| ``preset_modes`` | Les préréglages visibles pour ce thermostat. Les préréglages cachés ne sont pas affichés ici |
-| ``temperature_actuelle`` | La température actuelle telle que rapportée par le capteur |
-| ``temperature`` | La température cible |
-| ``action_hvac`` | L'action en cours d'exécution par le réchauffeur. Peut être inactif, chauffage |
-| ``preset_mode`` | Le préréglage actuellement sélectionné. Peut être l'un des 'preset_modes' ou un préréglage caché comme power |
-| ``[eco/confort/boost]_temp`` | La température configurée pour le préréglage xxx |
-| ``[eco/confort/boost]_away_temp`` | La température configurée pour le préréglage xxx lorsque la présence est désactivée ou not_home |
-| ``temp_power`` | La température utilisée lors de la détection de la perte |
-| ``on_percent`` | Le pourcentage sur calculé par l'algorithme TPI |
-| ``on_time_sec`` | La période On en sec. Doit être ```on_percent * cycle_min``` |
-| ``off_time_sec`` | La période d'arrêt en sec. Doit être ```(1 - on_percent) * cycle_min``` |
-| ``cycle_min`` | Le cycle de calcul en minutes |
-| ``function`` | L'algorithme utilisé pour le calcul du cycle |
-| ``tpi_coef_int`` | Le ``coef_int`` de l'algorithme TPI |
-| ``tpi_coef_ext`` | Le ``coef_ext`` de l'algorithme TPI |
-| ``saved_preset_mode`` | Le dernier preset utilisé avant le basculement automatique du preset |
-| ``saved_target_temp`` | La dernière température utilisée avant la commutation automatique |
-| ``window_state`` | Le dernier état connu du capteur de fenêtre. Aucun si la fenêtre n'est pas configurée |
-| ``is_window_bypass`` | True si le bypass de la détection d'ouverture et activé |
-| ``motion_state`` | Le dernier état connu du capteur de mouvement. Aucun si le mouvement n'est pas configuré |
-| ``overpowering_state`` | Le dernier état connu du capteur surpuissant. Aucun si la gestion de l'alimentation n'est pas configurée |
-| ``presence_state`` | Le dernier état connu du capteur de présence. Aucun si la gestion de présence n'est pas configurée |
-| ``safety_delay_min`` | Le délai avant d'activer le mode de sécurité lorsque un des 2 capteurs de température n'envoie plus de mesures |
-| ``safety_min_on_percent`` | Pourcentage de chauffe en dessous duquel le thermostat ne passera pas en sécurité |
-| ``safety_default_on_percent`` | Pourcentage de chauffe utilisé lorsque le thermostat est en sécurité |
-| ``last_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température interne |
-| ``last_ext_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température extérieure |
-| ``security_state`` | L'état de sécurité. vrai ou faux |
-| ``minimal_activation_delay_sec`` | Le délai d'activation minimal en secondes |
-| ``last_update_datetime`` | La date et l'heure au format ISO8866 de cet état |
-| ``friendly_name`` | Le nom du thermostat |
-| ``supported_features`` | Une combinaison de toutes les fonctionnalités prises en charge par ce thermostat. Voir la documentation officielle sur l'intégration climatique pour plus d'informations |
-| ``valve_open_percent`` | Le pourcentage d'ouverture de la vanne |
-| ``regulated_target_temperature`` | La température de consigne calculée par l'auto-régulation |
-| ``is_inversed`` | True si la commande est inversée (fil pilote avec diode) |
-| ``is_controlled_by_central_mode`` | True si le VTherm peut être controlé de façon centrale |
-| ``last_central_mode`` | Le dernier mode central utilisé (None si le VTherm n'est pas controlé en central) |
-| ``is_used_by_central_boiler`` | Indique si le VTherm peut contrôler la chaudière centrale |
-| ``auto_start_stop_enable`` | Indique si le VTherm est autorisé à s'auto démarrer/arrêter |
-| ``auto_start_stop_level`` | Indique le niveau d'auto start/stop |
-| ``hvac_off_reason`` | Indique la raison de l'arrêt (hvac_off) du VTherm. Ce peut être Window, Auto-start/stop ou Manuel |
-
-# Quelques résultats
-
-**Convergence de la température vers la cible configurée par preset:**
-
-
-[Cycle de marche/arrêt calculé par l'intégration :](https://)
-
-
-**Coef_int trop élevé (oscillations autour de la cible)**
-
-
-**Évolution du calcul de l'algorithme**
-
-Voir le code de ce composant [[ci-dessous](#even-better-with-apex-chart-to-tune-your-thermostat)]
-
-**Thermostat finement réglé**
-Merci [impuR_Shozz](https://forum.hacf.fr/u/impur_shozz/summary) !
-On peut voir une stabilité autour de la température cible (consigne) et lorsqu'à cible le on_percent (puissance) est proche de 0,3 ce qui semble une très bonne valeur.
-
-
-
-Enjoy !
-
-# Encore mieux
-
-## Bien mieux avec le Versatile Thermostat UI Card
-Une carte spéciale pour le Versatile Thermostat a été développée (sur la base du Better Thermostat). Elle est dispo ici [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card) et propose une vision moderne de tous les status du VTherm :
-
-
-
-## Encore mieux avec le composant Scheduler !
-
-Afin de profiter de toute la puissance du Versatile Thermostat, je vous invite à l'utiliser avec https://github.com/nielsfaber/scheduler-component
-En effet, le composant scheduler propose une gestion de la base climatique sur les modes prédéfinis. Cette fonctionnalité a un intérêt limité avec le thermostat générique mais elle devient très puissante avec le Versatile Thermostat :
-
-À partir d'ici, je suppose que vous avez installé Versatile Thermostat et Scheduler Component.
-
-Dans Scheduler, ajoutez un planning :
-
-
-
-Choisissez le groupe "climat", choisissez une (ou plusieurs) entité(s), sélectionnez "MAKE SCHEME" et cliquez sur suivant :
-(il est possible de choisir "SET PRESET", mais je préfère utiliser "MAKE SCHEME")
-
-
-
-Définissez votre schéma de mode et enregistrez :
-
-
-
-
-Dans cet exemple, j'ai réglé le mode ECO pendant la nuit et le jour lorsqu'il n'y a personne à la maison BOOST le matin et CONFORT le soir.
-
-
-J'espère que cet exemple vous aidera, n'hésitez pas à me faire part de vos retours !
-
-## Encore bien mieux avec la custom:simple-thermostat front integration
-Le ``custom:simple-thermostat`` [ici](https://github.com/nervetattoo/simple-thermostat) est une excellente intégration qui permet une certaine personnalisation qui s'adapte bien à ce thermostat.
-Vous pouvez avoir quelque chose comme ça très facilement 
-Exemple de configuration :
-
-```
- type: custom:simple-thermostat
- entity: climate.thermostat_sam2
- layout:
- step: row
- label:
- temperature: T°
- state: Etat
- hide:
- state: false
- control:
- hvac:
- _name: Mode
- preset:
- _name: Preset
- sensors:
- - entity: sensor.total_puissance_radiateur_sam2
- icon: mdi:lightning-bolt-outline
- header:
- toggle:
- entity: input_boolean.etat_ouverture_porte_sam
- name: Porte sam
-```
-
-Vous pouvez personnaliser ce composant à l'aide du composant HACS card-mod pour ajuster les couleurs des alertes. Exemple pour afficher en rouge les alertes sécurité et délestage :
-
-```
- card_mod:
- style: |
- {% if is_state('binary_sensor.thermostat_chambre_security_state', 'on') %}
- ha-card .body .sensor-heading ha-icon[icon="mdi:alert-outline"] {
- color: red;
- }
- {% endif %}
- {% if is_state('binary_sensor.thermostat_chambre_overpowering_state', 'on') %}
- ha-card .body .sensor-heading ha-icon[icon="mdi:flash"] {
- color: red;
- }
- {% endif %}
-```
-
-
-## Toujours mieux avec Plotly pour régler votre thermostat
-Vous pouvez obtenir une courbe comme celle présentée dans [some results](#some-results) avec une sorte de configuration de graphique Plotly uniquement en utilisant les attributs personnalisés du thermostat décrits [ici](#custom-attributes) :
-
-Remplacez les valeurs entre [[ ]] par les votres.
-```
-- type: custom:plotly-graph
- entities:
- - entity: '[[climate]]'
- attribute: temperature
- yaxis: y1
- name: Consigne
- - entity: '[[climate]]'
- attribute: current_temperature
- yaxis: y1
- name: T°
- - entity: '[[climate]]'
- attribute: ema_temp
- yaxis: y1
- name: Ema
- - entity: '[[climate]]'
- attribute: regulated_target_temperature
- yaxis: y1
- name: Regulated T°
- - entity: '[[slope]]'
- name: Slope
- fill: tozeroy
- yaxis: y9
- fillcolor: rgba(100, 100, 100, 0.3)
- line:
- color: rgba(100, 100, 100, 0.9)
- hours_to_show: 4
- refresh_interval: 10
- height: 800
- config:
- scrollZoom: true
- layout:
- margin:
- r: 50
- legend:
- x: 0
- 'y': 1.2
- groupclick: togglegroup
- title:
- side: top right
- yaxis:
- visible: true
- position: 0
- yaxis9:
- visible: true
- fixedrange: false
- range:
- - -0.5
- - 0.5
- position: 1
- xaxis:
- rangeselector:
- 'y': 1.1
- x: 0.7
- buttons:
- - count: 1
- step: hour
- - count: 12
- step: hour
- - count: 1
- step: day
- - count: 7
- step: day
-```
-
-Exemple de courbes obtenues avec Plotly :
-
-
-
-## Et toujours de mieux en mieux avec l'AappDaemon NOTIFIER pour notifier les évènements
-Cette automatisation utilise l'excellente App Daemon nommée NOTIFIER développée par Horizon Domotique que vous trouverez en démonstration [ici](https://www.youtube.com/watch?v=chJylIK0ASo&ab_channel=HorizonDomotique) et le code est [ici](https://github.com/jlpouffier/home-assistant-config/blob/master/appdaemon/apps/notifier.py). Elle permet de notifier les utilisateurs du logement lorsqu'un des évènements touchant à la sécurité survient sur un des Versatile Thermostats.
-
-C'est un excellent exemple de l'utilisation des notifications décrites ici [notification](#notifications).
-
-```
-alias: Surveillance Mode Sécurité chauffage
-description: Envoi une notification si un thermostat passe en mode sécurité ou power
-trigger:
- - platform: event
- event_type: versatile_thermostat_security_event
- id: versatile_thermostat_security_event
- - platform: event
- event_type: versatile_thermostat_power_event
- id: versatile_thermostat_power_event
- - platform: event
- event_type: versatile_thermostat_temperature_event
- id: versatile_thermostat_temperature_event
-condition: []
-action:
- - choose:
- - conditions:
- - condition: trigger
- id: versatile_thermostat_security_event
- sequence:
- - event: NOTIFIER
- event_data:
- action: send_to_jmc
- title: >-
- Radiateur {{ trigger.event.data.name }} - {{
- trigger.event.data.type }} Sécurité
- message: >-
- Le radiateur {{ trigger.event.data.name }} est passé en {{
- trigger.event.data.type }} sécurité car le thermomètre ne répond
- plus.\n{{ trigger.event.data }}
- callback:
- - title: Stopper chauffage
- event: stopper_chauffage
- image_url: /media/local/alerte-securite.jpg
- click_url: /lovelace-chauffage/4
- icon: mdi:radiator-off
- tag: radiateur_security_alerte
- persistent: true
- - conditions:
- - condition: trigger
- id: versatile_thermostat_power_event
- sequence:
- - event: NOTIFIER
- event_data:
- action: send_to_jmc
- title: >-
- Radiateur {{ trigger.event.data.name }} - {{
- trigger.event.data.type }} Délestage
- message: >-
- Le radiateur {{ trigger.event.data.name }} est passé en {{
- trigger.event.data.type }} délestage car la puissance max est
- dépassée.\n{{ trigger.event.data }}
- callback:
- - title: Stopper chauffage
- event: stopper_chauffage
- image_url: /media/local/alerte-delestage.jpg
- click_url: /lovelace-chauffage/4
- icon: mdi:radiator-off
- tag: radiateur_power_alerte
- persistent: true
- - conditions:
- - condition: trigger
- id: versatile_thermostat_temperature_event
- sequence:
- - event: NOTIFIER
- event_data:
- action: send_to_jmc
- title: >-
- Le thermomètre du radiateur {{ trigger.event.data.name }} ne
- répond plus
- message: >-
- Le thermomètre du radiateur {{ trigger.event.data.name }} ne
- répond plus depuis longtemps.\n{{ trigger.event.data }}
- image_url: /media/local/thermometre-alerte.jpg
- click_url: /lovelace-chauffage/4
- icon: mdi:radiator-disabled
- tag: radiateur_thermometre_alerte
- persistent: true
-mode: queued
-max: 30
-```
-
-
-# Les contributions sont les bienvenues !
-
-Si vous souhaitez contribuer, veuillez lire les [directives de contribution](CONTRIBUTING.md)
-
-# Dépannages
-
-
-Utilisation d'un Heatzy
-
-## Utilisation d'un Heatzy
-
-L'utilisation d'un Heatzy est possible à la condition d'utiliser un switch virtuel sur ce modèle :
-```
-- platform: template
- switches:
- chauffage_sdb:
- unique_id: chauffage_sdb
- friendly_name: Chauffage salle de bain
- value_template: "{{ is_state_attr('climate.salle_de_bain', 'preset_mode', 'comfort') }}"
- icon_template: >-
- {% if is_state_attr('climate.salle_de_bain', 'preset_mode', 'comfort') %}
- mdi:radiator
- {% elif is_state_attr('climate.salle_de_bain', 'preset_mode', 'away') %}
- mdi:snowflake
- {% else %}
- mdi:radiator-disabled
- {% endif %}
- turn_on:
- service: climate.set_preset_mode
- entity_id: climate.salle_de_bain
- data:
- preset_mode: "comfort"
- turn_off:
- service: climate.set_preset_mode
- entity_id: climate.salle_de_bain
- data:
- preset_mode: "eco"
-```
-Merci à @gael pour cet exemple.
-
-
-
-Utilisation d'un radiateur avec un fil pilote
-
-## Utilisation d'un radiateur avec un fil pilote
-Comme pour le Heatzy ci-dessus vous pouvez utiliser un switch virtuel qui va changer le preset de votre radiateur en fonction de l'état d'allumage du VTherm.
-Exemple :
-```
-- platform: template
- switches:
- radiateur_soan:
- friendly_name: radiateur_soan_inv
- value_template: "{{ is_state('switch.radiateur_soan', 'off') }}"
- turn_on:
- service: switch.turn_off
- data:
- entity_id: switch.radiateur_soan
- turn_off:
- service: switch.turn_on
- data:
- entity_id: switch.radiateur_soan
- icon_template: "{% if is_state('switch.radiateur_soan', 'on') %}mdi:radiator-disabled{% else %}mdi:radiator{% endif %}"
-```
-
-
-
-
-Utilisation d'un radiateur avec un module Nodon
-
-## Utilisation d'un radiateur avec un fil pilote
-Comme pour le Heatzy ci-dessus vous pouvez utiliser un switch virtuel qui va changer le preset de votre radiateur en fonction de l'état d'allumage du VTherm.
-Exemple :
-```
-- platform: template
- switches:
- chauffage_chb_parents:
- unique_id: chauffage_chb_parents
- friendly_name: Chauffage chambre parents
- value_template: "{{ is_state('select.fp_chb_parents_pilot_wire_mode', 'comfort') }}"
- icon_template: >-
- {% if is_state('select.fp_chb_parents_pilot_wire_mode', 'comfort') %}
- mdi:radiator
- {% elif is_state('select.fp_chb_parents_pilot_wire_mode', 'frost_protection') %}
- mdi:snowflake
- {% else %}
- mdi:radiator-disabled
- {% endif %}
- turn_on:
- service: select.select_option
- target:
- entity_id: select.fp_chb_parents_pilot_wire_mode
- data:
- option: comfort
- turn_off:
- service: select.select_option
- target:
- entity_id: select.fp_chb_parents_pilot_wire_mode
- data:
- option: eco
-```
-
-
-
-
-Seul le premier radiateur chauffe
-
-## Seul le premier radiateur chauffe
-
-En mode `over_switch` si plusieurs radiateurs sont configurés pour un même VTherm, l'alllumage va se faire de façon séquentiel pour lisser au plus possible les pics de consommation.
-Cela est tout à fait normal et voulu. C'est décrit ici : [Pour un thermostat de type ```thermostat_over_switch```](#pour-un-thermostat-de-type-thermostat_over_switch)
-
-
-
-
-Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne
-
-## Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne
-
-### Type `over_switch` ou `over_valve`
-Avec un VTherm de type `over_switch` ou `over_valve`, ce défaut montre juste que les paramètres de l'algorithme TPI sont mal réglés. Voir [Algorithme TPI](#algorithme-tpi) pour optimiser les réglages.
-
-### Type `over_climate`
-Avec un VTherm de type `over_climate`, la régulation est faite par le `climate` sous-jacent directement et VTherm se contente de lui transmettre les consignes. Donc si le radiateur chauffe alors que la température de consigne est dépassée, c'est certainement que sa mesure de température interne est biaisée. Ca arrive très souvent avec les TRV et les clims réversibles qui ont un capteur de température interne, soit trop près de l'élément de chauffe (donc trop froid l'hiver).
-
-Exemple de discussion autour de ces sujets: [#348](https://github.com/jmcollin78/versatile_thermostat/issues/348), [#316](https://github.com/jmcollin78/versatile_thermostat/issues/316), [#312](https://github.com/jmcollin78/versatile_thermostat/discussions/312), [#278](https://github.com/jmcollin78/versatile_thermostat/discussions/278)
-
-Pour s'en sortir, VTherm est équipé d'une fonction nommée auto-régulation qui permet d'adapter la consigne envoyée au sous-jacent jusqu'à ce que la consigne soit respectée. Cette fonction permet de compenser le biais de mesure des thermomètres internes. Si le biais est important la régulation doit être importante. Voir [L'auto-régulation](#lauto-régulation) pour configurer l'auto-régulation.
-
-
-
-Régler les paramètres de détection d'ouverture de fenêtre en mode auto
-
-## Régler les paramètres de détection d'ouverture de fenêtre en mode auto
-
-Si vous n'arrivez pas à régler la fonction de détection des ouvertures en mode auto (cf. [auto](#le-mode-auto)), vous pouvez essayer de modifier les paramètres de l'algorithme de lissage de la température.
-En effet, la détection automatique d'ouverture est basée sur le calcul de la pente de la température (slope). Pour éviter les artefacts due à un capteur de température imprécis, cette pente est calculée sur une température lissée avec un algorithme de lissage nommée Exponential Moving Average (Moyenne mobile exponentielle).
-Cet algorithm possède 3 paramètres :
-1. `lifecycle_sec` : la durée en secondes prise en compte pour le lissage. Plus elle est forte et plus le lissage sera important mais plus il y aura de délai de détection,
-2. `max_alpha` : si deux mesures de température sont éloignées dans le temps, la deuxième aura un poid beaucoup fort. Le paramètre permet de limiter le poid d'une mesure qui arrive bien après la précédente. Cette valeur doit être comprise entre 0 et 1. Plus elle est faible et moins les valeurs éloignées sont prises en compte. La valeur par défaut est de 0,5. Cela fait que lorsqu'une nouvelle valeur de température ne pèsera jamais plus que la moitié de la moyenne mobile,
-3. `precision` : le nombre de chiffre après la virgule conservée pour le calcul de la moyenne mobile.
-
-Pour changer ses paramètres, il faut modifier le fichier `configuration.yaml` et ajouter la section suivante (les valeurs sont les valeurs par défaut):
-```
-versatile_thermostat:
- short_ema_params:
- max_alpha: 0.5
- halflife_sec: 300
- precision: 2
-```
-
-Ces paramètres sont sensibles et assez difficiles à régler. Merci de ne les utiliser que si vous savez ce que vous faites et que vos mesures de température ne sont pas déjà lisses.
-
-
-
-Pourquoi mon Versatile Thermostat se met en Securite ?
-
-## Pourquoi mon Versatile Thermostat se met en Securite ?
-Le mode sécurité est possible sur tous les types de VTherm . Il survient lorsqu'un des 2 thermomètres qui donne la température de la pièce ou la température extérieure n'a pas envoyé de valeur depuis plus de `safety_delay_min` minutes et que le radiateur chauffait à au moins `safety_min_on_percent`.
-
-Comme l'algorithme est basé sur les mesures de température, si elles ne sont plus reçues par le VTherm, il y a un risque de surchauffe et d'incendie. Pour éviter ça, lorsque les conditions rappelées ci-dessus sont détectées, la chauffe est limité au paramètre `safety_default_on_percent`. Cette valeur doit donc être raisonnablement faible (10% est une bonne valeur). Elle permet d'éviter un incendie tout en évitant de couper totalement le radiateur (risque de gel).
-
-Tous ces paramètres se règlent dans la dernière page de la configuration du VTherm : "Paramètres avancés".
-
-### Comment détecter le mode sécurité ?
-Le premier symptôme est une température anormalement basse avec un temps de chauffe faible à chaque cycle et régulier.
-Exemple:
-
-[security mode](images/security-mode-symptome1.png)
-
-Si vous avez installé la carte [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card), le VTherm en question aura cette forme là :
-
-[security mode UI Card](images/security-mode-symptome2.png)
-
-Vous pouvez aussi vérifier dans les attributs du VTherm les dates de réception des différentes dates. **Les attributs sont disponibles dans les Outils de développement / Etats**.
-
-Exemple :
-```
-security_state: true
-last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"
-last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00"
-last_update_datetime: "2023-12-06T18:43:28.351103+01:00"
-...
-safety_delay_min: 60
-```
-
-On voit que :
-1. le VTherm est bien en mode sécurité (`security_state: true`),
-2. l'heure courante est le 06/12/2023 à 18h43:28 (`last_update_datetime: "2023-12-06T18:43:28.351103+01:00"`),
-3. l'heure de dernière réception de la température intérieure est le 06/12/2023 à 18h43:28 (`last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"`). Elle est donc récente,
-4. l'heure de dernière réception de la température extérieure est le 06/12/2023 à 13h04:35 (`last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00`). C'est donc l'heure extérieure qui a plus de 5 h de retard et qui a provoquée le passage en mode sécurité, car le seuil est limité à 60 min (`safety_delay_min: 60`).
-
-### Comment être averti lorsque cela se produit ?
-Pour être averti, le VTherm envoie un évènement dès que ça se produit et un en fin d'alerte sécurité. Vous pouvez capter ces évènements dans une automatisation et envoyer une notification par exemple, faire clignoter un voyant, déclencher une sirène, ... A vous de voir.
-
-Pour manipuler les évènements générés par le VTherm, cf. [Eveènements](#evènements).
-
-### Comment réparer ?
-Cela va dépendre de la cause du problème :
-1. Si un capteur est en défaut, il faut le réparer (remettre des piles, le changer, vérifier l'intégration Météo qui donne la température extérieure, ...),
-2. Si le paramètre `safety_delay_min` est trop petit, cela rsique de générer beaucoup de fausses alertes. Une valeur correcte est de l'ordre de 60 min, surtout si vous avez des capteurs de température à pile.
-3. Certains capteurs de température, n'envoie pas de mesure si la température n'a pas changée. Donc en cas de température très stable pendant longtemps, le mode sécurité peut se déclencher. Ce n'est pas très grave puisqu'il s'enlève dès que le VTherm reçoit à nouveau une température. Sur certain thermomètre (TuYA par exemple), on peut forcer le délai max entre 2 mesures. Il conviendra de mettre un délai max < `safety_delay_min`,
-4. Dès que la température sera a nouveau reçue le mode sécurité s'enlèvera et les valeurs précédentes de preset, température cible et mode seront restaurées.
-
-
-
-Utilisation d'un groupe de personnes comme capteur de présence
-
-## Utilisation d'un groupe de personnes comme capteur de présence
-
-Malheureusement, les groupes de personnes ne sont pas reconnus comme des capteurs de présence. On ne peut donc pas les utiliser directement dans VTherm.
-Le contournement est de créer un template de binary_sensor avec le code suivant :
-
-Fichier `template.yaml` :
-```
-- binary_sensor:
- - name: maison_occupee
- unique_id: maison_occupee
- state: "{{is_state('person.person1', 'home') or is_state('person.person2', 'home') or is_state('input_boolean.force_presence', 'on')}}"
- device_class: occupancy
-```
-
-Vous noterez dans cet exemple, l'utilisation d'un input_boolean nommé force_presence qui permet de forcer le capteur à `True` et ainsi de forcer les VTherm qui l'utilise avec présence active. Ca permet par exemple, de forcer un pré-chauffage du logement lors du départ du travail, ou lorsqu'une personne non reconnue nativement dans HA est présente.
-
-Fichier `configuration.yaml`:
-```
-...
-template: !include templates.yaml
-...
-```
-
-
-
-Activer les logs du Versatile Thermostat
-
-## Activer les logs du Versatile Thermostat
-Des fois, vous aurez besoin d'activer les logs pour afiner les analyses. Pour cela, éditer le fichier `logger.yaml` de votre configuration et configurer les logs comme suit :
-```
-default: xxxx
-logs:
- custom_components.versatile_thermostat: info
-```
-Vous devez recharger la configuration yaml (Outils de dev / Yaml / Toute la configuration Yaml) ou redémarrer Home Assistant pour que ce changement soit pris en compte.
-
-
-
-***
-
-[versatile_thermostat]: https://github.com/jmcollin78/versatile_thermostat
-[buymecoffee]: https://www.buymeacoffee.com/jmcollin78
-[buymecoffeebadge]: https://img.shields.io/badge/Buy%20me%20a%20beer-%245-orange?style=for-the-badge&logo=buy-me-a-beer
-[commits-shield]: https://img.shields.io/github/commit-activity/y/jmcollin78/versatile_thermostat.svg?style=for-the-badge
-[commits]: https://github.com/jmcollin78/versatile_thermostat/commits/master
-[hacs]: https://github.com/custom-components/hacs
-[hacs_badge]: https://img.shields.io/badge/HACS-Custom-41BDF5.svg?style=for-the-badge
-[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge
-[forum]: https://community.home-assistant.io/
-[license-shield]: https://img.shields.io/github/license/jmcollin78/versatile_thermostat.svg?style=for-the-badge
-[maintenance-shield]: https://img.shields.io/badge/maintainer-Joakim%20Sørensen%20%40ludeeus-blue.svg?style=for-the-badge
-[releases-shield]: https://img.shields.io/github/release/jmcollin78/versatile_thermostat.svg?style=for-the-badge
-[releases]: https://github.com/jmcollin78/versatile_thermostat/releases
diff --git a/documentation/fr/presentation.md b/documentation/fr/presentation.md
index b924646..10aff23 100644
--- a/documentation/fr/presentation.md
+++ b/documentation/fr/presentation.md
@@ -18,7 +18,7 @@ Ce composant nommé __Versatile thermostat__ gère les cas d'utilisation suivant
- Configuration via l'interface graphique d'intégration standard (à l'aide du flux Config Entry),
- Utilisations complètes du **mode préréglages**,
- Désactiver le mode préréglé lorsque la température est **définie manuellement** sur un thermostat,
-- Éteindre/allumer un thermostat ou chager de preset lorsqu'une **porte ou des fenêtres sont ouvertes/fermées** après un certain délai,
+- Éteindre/allumer un thermostat ou changer de preset lorsqu'une **porte ou des fenêtres sont ouvertes/fermées** après un certain délai,
- Changer de preset lorsqu'une **activité est détectée** ou non dans une pièce pendant un temps défini,
- Utiliser un algorithme **TPI (Time Proportional Interval)** grâce à l'algorithme [[Argonaute](https://forum.hacf.fr/u/argonaute/summary)] ,
- Ajouter une **gestion de délestage** ou une régulation pour ne pas dépasser une puissance totale définie. Lorsque la puissance maximale est dépassée, un préréglage caché de « puissance » est défini sur l'entité climatique. Lorsque la puissance passe en dessous du maximum, le préréglage précédent est restauré.
diff --git a/documentation/fr/releases.md b/documentation/fr/releases.md
index 613ab38..7ec5d19 100644
--- a/documentation/fr/releases.md
+++ b/documentation/fr/releases.md
@@ -2,6 +2,9 @@

+> * **Release 7.1**:
+> - Refonte de la fonction de délestage (gestion de la puissance). Le délestage est maintenant géré de façon centralisé (auparavent chaque _VTherm_ était autonome). Cela permet une gestion bien plus efficace et de prioriser le délestage sur les équipements qui sont proches de la consigne. Attention, vous devez impérativement avoir une configuration centralisée avec gestion de la puissance pour que cela fonctionne. Plus d'infos [ici](./feature-power.md)
+
> * **Release 6.8**:
> - Ajout d'une nouvelle méthode de régulation pour les Versatile Thermostat de type `over_climate`. Cette méthode nommée 'Contrôle direct de la vanne' permet de contrôler directement la vanne d'un TRV et éventuellement un décalage pour calibrer le thermomètre interne de votre TRV. Cette nouvelle méthode a été testée avec des Sonoff TRVZB et généralisée pour d'autre type de TRV pour lesquels la vanne est directement commandable via des entités de type `number`. Plus d'informations [ici](over-climate.md#lauto-régulation) et [ici](self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne).
diff --git a/documentation/fr/self-regulation.md b/documentation/fr/self-regulation.md
index 049e916..661256d 100644
--- a/documentation/fr/self-regulation.md
+++ b/documentation/fr/self-regulation.md
@@ -39,6 +39,8 @@ L'algorithme de calcul du taux d'ouverture est basé sur le _TPI_ qui est décri
Si une entité de type taux de fermeture de la vanne est configurée, il sera positionné avec la valeur 100 - taux d'ouverture pour forcer la vanne dans un état.
+Note: pour les Sonoff TRVZB, vous ne devez pas configurer les "closing degree". Cela rend inopérant le `hvac_action` qui est utilisé par _VTherm_ et qui indique que l'équipement est en chauffe.
+
### autres auto-régulation
Dans ce deuxième cas, le Versatile Thermostat calcule un décalage basé sur les informations suivantes :
diff --git a/pyproject.toml b/pyproject.toml
index e69de29..8b0dac4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[tool.black]
+# don't work. Options are in the devcontainer.yaml
+line-length = 180
\ No newline at end of file
diff --git a/tests/commons.py b/tests/commons.py
index 1b12423..7a39b94 100644
--- a/tests/commons.py
+++ b/tests/commons.py
@@ -59,8 +59,6 @@ from .const import ( # pylint: disable=unused-import
MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG,
MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG,
MOCK_TH_OVER_SWITCH_TPI_CONFIG,
- MOCK_PRESETS_CONFIG,
- MOCK_PRESETS_AC_CONFIG,
MOCK_WINDOW_CONFIG,
MOCK_MOTION_CONFIG,
MOCK_POWER_CONFIG,
@@ -89,7 +87,7 @@ FULL_SWITCH_CONFIG = (
| MOCK_TH_OVER_SWITCH_CENTRAL_MAIN_CONFIG
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
- | MOCK_PRESETS_CONFIG
+ # | MOCK_PRESETS_CONFIG
| MOCK_FULL_FEATURES
| MOCK_WINDOW_CONFIG
| MOCK_MOTION_CONFIG
@@ -104,7 +102,6 @@ FULL_SWITCH_AC_CONFIG = (
| MOCK_TH_OVER_SWITCH_CENTRAL_MAIN_CONFIG
| MOCK_TH_OVER_SWITCH_AC_TYPE_CONFIG
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
- | MOCK_PRESETS_AC_CONFIG
| MOCK_FULL_FEATURES
| MOCK_WINDOW_CONFIG
| MOCK_MOTION_CONFIG
@@ -118,7 +115,7 @@ PARTIAL_CLIMATE_CONFIG = (
| MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
| MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
| MOCK_TH_OVER_CLIMATE_TYPE_CONFIG
- | MOCK_PRESETS_CONFIG
+ # | MOCK_PRESETS_CONFIG
| MOCK_ADVANCED_CONFIG
)
@@ -127,7 +124,7 @@ PARTIAL_CLIMATE_CONFIG_USE_DEVICE_TEMP = (
| MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
| MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
| MOCK_TH_OVER_CLIMATE_TYPE_USE_DEVICE_TEMP_CONFIG
- | MOCK_PRESETS_CONFIG
+ # | MOCK_PRESETS_CONFIG
| MOCK_ADVANCED_CONFIG
)
@@ -136,7 +133,7 @@ PARTIAL_CLIMATE_NOT_REGULATED_CONFIG = (
| MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
| MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
| MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG
- | MOCK_PRESETS_CONFIG
+ # | MOCK_PRESETS_CONFIG
| MOCK_ADVANCED_CONFIG
)
@@ -145,7 +142,7 @@ PARTIAL_CLIMATE_AC_CONFIG = (
| MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG
| MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
| MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
- | MOCK_PRESETS_CONFIG
+ # | MOCK_PRESETS_CONFIG
| MOCK_ADVANCED_CONFIG
)
@@ -153,7 +150,7 @@ FULL_4SWITCH_CONFIG = (
MOCK_TH_OVER_4SWITCH_USER_CONFIG
| MOCK_TH_OVER_4SWITCH_TYPE_CONFIG
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
- | MOCK_PRESETS_CONFIG
+ # | MOCK_PRESETS_CONFIG
| MOCK_WINDOW_CONFIG
| MOCK_MOTION_CONFIG
| MOCK_POWER_CONFIG
@@ -592,7 +589,10 @@ class MockNumber(NumberEntity):
async def create_thermostat(
- hass: HomeAssistant, entry: MockConfigEntry, entity_id: str
+ hass: HomeAssistant,
+ entry: MockConfigEntry,
+ entity_id: str,
+ temps: dict | None = None,
) -> BaseThermostat:
"""Creates and return a TPI Thermostat"""
entry.add_to_hass(hass)
@@ -601,6 +601,11 @@ async def create_thermostat(
entity = search_entity(hass, entity_id, CLIMATE_DOMAIN)
+ if entity and temps:
+ await set_all_climate_preset_temp(
+ hass, entity, temps, entity.entity_id.replace("climate.", "")
+ )
+
return entity
@@ -741,9 +746,11 @@ async def send_power_change_event(entity: BaseThermostat, new_power, date, sleep
)
},
)
- await entity.power_manager._async_power_sensor_changed(power_event)
+ vtherm_api = VersatileThermostatAPI.get_vtherm_api()
+ await vtherm_api.central_power_manager._power_sensor_changed(power_event)
+ await vtherm_api.central_power_manager._do_immediate_shedding()
if sleep:
- await asyncio.sleep(0.1)
+ await entity.hass.async_block_till_done()
async def send_max_power_change_event(
@@ -767,9 +774,11 @@ async def send_max_power_change_event(
)
},
)
- await entity.power_manager._async_max_power_sensor_changed(power_event)
+ vtherm_api = VersatileThermostatAPI.get_vtherm_api()
+ await vtherm_api.central_power_manager._max_power_sensor_changed(power_event)
+ await vtherm_api.central_power_manager._do_immediate_shedding()
if sleep:
- await asyncio.sleep(0.1)
+ await entity.hass.async_block_till_done()
async def send_window_change_event(
@@ -1101,3 +1110,9 @@ class SideEffects:
def add_or_update_side_effect(self, key: str, new_value: Any):
"""Update the value of a side effect"""
self._current_side_effects[key] = new_value
+
+
+async def do_central_power_refresh(hass):
+ """Do a central power refresh"""
+ await VersatileThermostatAPI.get_vtherm_api().central_power_manager.refresh_state()
+ return hass.async_block_till_done()
diff --git a/tests/conftest.py b/tests/conftest.py
index b6be1f5..41e5723 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -19,6 +19,8 @@
from unittest.mock import patch
import pytest
+# https://github.com/miketheman/pytest-socket/pull/275
+from pytest_socket import socket_allow_hosts
from homeassistant.core import StateMachine
@@ -26,6 +28,12 @@ from custom_components.versatile_thermostat.config_flow import (
VersatileThermostatBaseConfigFlow,
)
+from custom_components.versatile_thermostat.const import (
+ CONF_POWER_SENSOR,
+ CONF_MAX_POWER_SENSOR,
+ CONF_USE_POWER_FEATURE,
+ CONF_PRESET_POWER,
+)
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
@@ -35,12 +43,6 @@ from .commons import (
FULL_CENTRAL_CONFIG_WITH_BOILER,
)
-# https://github.com/miketheman/pytest-socket/pull/275
-from pytest_socket import socket_allow_hosts
-
-# ...
-
-
# ...
def pytest_runtest_setup():
"""setup tests"""
@@ -51,16 +53,6 @@ def pytest_runtest_setup():
pytest_plugins = "pytest_homeassistant_custom_component" # pylint: disable=invalid-name
-# Permet d'exclure certains test en mode d'ex
-# sequential = pytest.mark.sequential
-
-
-# This fixture allow to execute some tests first and not in //
-# @pytest.fixture
-# def order():
-# return 1
-#
-
# This fixture enables loading custom integrations in all tests.
# Remove to enable selective use of this fixture
@pytest.fixture(autouse=True)
@@ -167,3 +159,24 @@ async def init_central_config_with_boiler_fixture(
await create_central_config(hass, FULL_CENTRAL_CONFIG_WITH_BOILER)
yield
+
+
+@pytest.fixture(name="init_central_power_manager")
+async def init_central_power_manager_fixture(
+ hass, init_central_config
+): # pylint: disable=unused-argument
+ """Initialize the central power_manager"""
+ vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
+
+ # 1. creation / init
+ vtherm_api.central_power_manager.post_init(
+ {
+ CONF_POWER_SENSOR: "sensor.the_power_sensor",
+ CONF_MAX_POWER_SENSOR: "sensor.the_max_power_sensor",
+ CONF_USE_POWER_FEATURE: True,
+ CONF_PRESET_POWER: 13,
+ }
+ )
+ assert vtherm_api.central_power_manager.is_configured
+
+ yield
diff --git a/tests/const.py b/tests/const.py
index 0940ddd..56a10dc 100644
--- a/tests/const.py
+++ b/tests/const.py
@@ -140,25 +140,6 @@ MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG = {
CONF_AUTO_REGULATION_PERIOD_MIN: 1,
}
-# TODO remove this later
-MOCK_PRESETS_CONFIG = {
- PRESET_FROST_PROTECTION + PRESET_TEMP_SUFFIX: 7,
- PRESET_ECO + PRESET_TEMP_SUFFIX: 16,
- PRESET_COMFORT + PRESET_TEMP_SUFFIX: 17,
- PRESET_BOOST + PRESET_TEMP_SUFFIX: 18,
-}
-
-# TODO remove this later
-MOCK_PRESETS_AC_CONFIG = {
- PRESET_FROST_PROTECTION + PRESET_TEMP_SUFFIX: 7,
- PRESET_ECO + PRESET_TEMP_SUFFIX: 17,
- PRESET_COMFORT + PRESET_TEMP_SUFFIX: 19,
- PRESET_BOOST + PRESET_TEMP_SUFFIX: 20,
- PRESET_ECO + PRESET_AC_SUFFIX + PRESET_TEMP_SUFFIX: 25,
- PRESET_COMFORT + PRESET_AC_SUFFIX + PRESET_TEMP_SUFFIX: 23,
- PRESET_BOOST + PRESET_AC_SUFFIX + PRESET_TEMP_SUFFIX: 21,
-}
-
MOCK_WINDOW_CONFIG = {
CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
# Not used normally only for tests to avoid rewrite all tests
@@ -184,12 +165,16 @@ MOCK_MOTION_CONFIG = {
CONF_NO_MOTION_PRESET: PRESET_ECO,
}
-MOCK_POWER_CONFIG = {
+MOCK_CENTRAL_POWER_CONFIG = {
CONF_POWER_SENSOR: "sensor.power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.power_max_sensor",
CONF_PRESET_POWER: 10,
}
+MOCK_POWER_CONFIG = {
+ CONF_PRESET_POWER: 10,
+}
+
MOCK_PRESENCE_CONFIG = {
CONF_PRESENCE_SENSOR: "person.presence_sensor",
}
diff --git a/tests/test_binary_sensors.py b/tests/test_binary_sensors.py
index d2c3f15..c74f9bc 100644
--- a/tests/test_binary_sensors.py
+++ b/tests/test_binary_sensors.py
@@ -1,4 +1,4 @@
-# pylint: disable=wildcard-import, unused-wildcard-import, unused-argument, line-too-long
+# pylint: disable=wildcard-import, unused-wildcard-import, unused-argument, line-too-long, protected-access
""" Test the normal start of a Thermostat """
from unittest.mock import patch
@@ -107,9 +107,16 @@ async def test_overpowering_binary_sensors(
skip_hass_states_is_state,
skip_turn_on_off_heater,
skip_send_event,
+ init_central_power_manager,
):
"""Test the overpowering binary sensors in thermostat type"""
+ temps = {
+ "eco": 17,
+ "comfort": 18,
+ "boost": 19,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
@@ -122,9 +129,6 @@ async def test_overpowering_binary_sensors(
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
@@ -136,15 +140,13 @@ async def test_overpowering_binary_sensors(
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: BaseThermostat = await create_thermostat(
- hass, entry, "climate.theoverswitchmockname"
+ hass, entry, "climate.theoverswitchmockname", temps
)
assert entity
@@ -153,35 +155,55 @@ async def test_overpowering_binary_sensors(
)
assert overpowering_binary_sensor
- now: datetime = datetime.now(tz=get_tz(hass))
+ now: datetime = NowClass.get_now(hass)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# Overpowering should be not set because poer have not been received
await entity.async_set_preset_mode(PRESET_COMFORT)
await entity.async_set_hvac_mode(HVACMode.HEAT)
await send_temperature_change_event(entity, 15, now)
- assert await entity.power_manager.check_overpowering() is False
+ assert entity.power_manager.is_overpowering_detected is False
assert entity.power_manager.overpowering_state is STATE_UNKNOWN
await overpowering_binary_sensor.async_my_climate_changed()
assert overpowering_binary_sensor.state is STATE_OFF
assert overpowering_binary_sensor.device_class == BinarySensorDeviceClass.POWER
- await send_power_change_event(entity, 100, now)
- await send_max_power_change_event(entity, 150, now)
- assert await entity.power_manager.check_overpowering() is True
- assert entity.power_manager.overpowering_state is STATE_ON
+ # Send power mesurement
+ side_effects = SideEffects(
+ {
+ "sensor.the_power_sensor": State("sensor.the_power_sensor", 150),
+ "sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 100),
+ },
+ State("unknown.entity_id", "unknown"),
+ )
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.thermostat_switch.ThermostatOverSwitch.is_device_active", return_value="True"):
+ # fmt: on
+ await send_power_change_event(entity, 150, now)
+ await send_max_power_change_event(entity, 100, now)
- # Simulate the event reception
- await overpowering_binary_sensor.async_my_climate_changed()
- assert overpowering_binary_sensor.state == STATE_ON
+ assert entity.power_manager.is_overpowering_detected is True
+ assert entity.power_manager.overpowering_state is STATE_ON
+
+ # Simulate the event reception
+ await overpowering_binary_sensor.async_my_climate_changed()
+ assert overpowering_binary_sensor.state == STATE_ON
# set max power to a low value
- await send_max_power_change_event(entity, 201, now)
- assert await entity.power_manager.check_overpowering() is False
- assert entity.power_manager.overpowering_state is STATE_OFF
- # Simulate the event reception
- await overpowering_binary_sensor.async_my_climate_changed()
- assert overpowering_binary_sensor.state == STATE_OFF
+ side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 251))
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
+ # fmt: on
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+ await send_max_power_change_event(entity, 251, now)
+ assert entity.power_manager.is_overpowering_detected is False
+ assert entity.power_manager.overpowering_state is STATE_OFF
+ # Simulate the event reception
+ await overpowering_binary_sensor.async_my_climate_changed()
+ assert overpowering_binary_sensor.state == STATE_OFF
@pytest.mark.parametrize("expected_lingering_tasks", [True])
diff --git a/tests/test_bugs.py b/tests/test_bugs.py
index e19ba34..8a1e075 100644
--- a/tests/test_bugs.py
+++ b/tests/test_bugs.py
@@ -266,7 +266,9 @@ async def test_bug_272(
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
-async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
+async def test_bug_407(
+ hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
+):
"""Test the followin case in power management:
1. a heater is active (heating). So the power consumption takes the heater power into account. We suppose the power consumption is near the threshold,
2. the user switch preset let's say from Comfort to Boost,
@@ -275,6 +277,12 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
"""
+ temps = {
+ "eco": 17,
+ "comfort": 18,
+ "boost": 19,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
@@ -287,9 +295,6 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
@@ -301,34 +306,43 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: ThermostatOverSwitch = await create_thermostat(
- hass, entry, "climate.theoverswitchmockname"
+ hass, entry, "climate.theoverswitchmockname", temps
)
assert entity
tpi_algo = entity._prop_algorithm
assert tpi_algo
- tz = get_tz(hass) # pylint: disable=invalid-name
- now: datetime = datetime.now(tz=tz)
+ now: datetime = NowClass.get_now(hass)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await send_temperature_change_event(entity, 16, now)
await send_ext_temperature_change_event(entity, 10, now)
# 1. An already active heater will not switch to overpowering
+ side_effects = SideEffects(
+ {
+ "sensor.the_power_sensor": State("sensor.the_power_sensor", 100),
+ "sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 110),
+ },
+ State("unknown.entity_id", "unknown"),
+ )
+
with patch(
"homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock,
return_value=True,
+ ), patch(
+ "homeassistant.core.StateMachine.get",
+ side_effect=side_effects.get_side_effects(),
):
await entity.async_set_hvac_mode(HVACMode.HEAT)
await entity.async_set_preset_mode(PRESET_COMFORT)
@@ -337,16 +351,17 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert entity.target_temperature == 18
# waits that the heater starts
- await asyncio.sleep(0.1)
+ await hass.async_block_till_done()
+
assert mock_service_call.call_count >= 1
assert entity.is_device_active is True
# Send power max mesurement
- await send_max_power_change_event(entity, 110, datetime.now())
+ await send_max_power_change_event(entity, 110, now)
# Send power mesurement (theheater is already in the power measurement)
- await send_power_change_event(entity, 100, datetime.now())
+ await send_power_change_event(entity, 100, now)
# No overpowering yet
- assert await entity.power_manager.check_overpowering() is False
+ assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is < power_max
assert entity.preset_mode is PRESET_COMFORT
assert entity.power_manager.overpowering_state is STATE_OFF
@@ -359,36 +374,57 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock,
return_value=True,
+ ), patch(
+ "homeassistant.core.StateMachine.get",
+ side_effect=side_effects.get_side_effects(),
):
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
# change preset to Boost
await entity.async_set_preset_mode(PRESET_BOOST)
# waits that the heater starts
await asyncio.sleep(0.1)
+ # doesn't work for call_later
+ # await hass.async_block_till_done()
- assert await entity.power_manager.check_overpowering() is False
+ # simulate a refresh for central power (not necessary)
+ await do_central_power_refresh(hass)
+
+ assert entity.power_manager.is_overpowering_detected is False
assert entity.hvac_mode is HVACMode.HEAT
assert entity.preset_mode is PRESET_BOOST
assert entity.power_manager.overpowering_state is STATE_OFF
assert entity.target_temperature == 19
assert mock_service_call.call_count >= 1
- # 3. if heater is stopped (is_device_active==False), then overpowering should be started
+ # 3. if heater is stopped (is_device_active==False) and power is over max, then overpowering should be started
+ side_effects.add_or_update_side_effect("sensor.the_power_sensor", State("sensor.the_power_sensor", 150))
with patch(
"homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock,
return_value=False,
+ ), patch(
+ "homeassistant.core.StateMachine.get",
+ side_effect=side_effects.get_side_effects(),
):
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
# change preset to Boost
await entity.async_set_preset_mode(PRESET_COMFORT)
# waits that the heater starts
await asyncio.sleep(0.1)
- assert await entity.power_manager.check_overpowering() is True
+ # simulate a refresh for central power (not necessary)
+ await do_central_power_refresh(hass)
+
+ assert entity.power_manager.is_overpowering_detected is False
assert entity.hvac_mode is HVACMode.HEAT
- assert entity.preset_mode is PRESET_POWER
- assert entity.power_manager.overpowering_state is STATE_ON
+ assert entity.preset_mode is PRESET_COMFORT
+ assert entity.power_manager.overpowering_state is STATE_OFF
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@@ -445,8 +481,6 @@ async def test_bug_500_3(hass: HomeAssistant, init_vtherm_api) -> None:
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
CONF_WINDOW_SENSOR: "sensor.theWindowSensor",
CONF_USE_POWER_CENTRAL_CONFIG: False,
- CONF_POWER_SENSOR: "sensor.thePowerSensor",
- CONF_MAX_POWER_SENSOR: "sensor.theMaxPowerSensor",
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
CONF_PRESENCE_SENSOR: "sensor.thePresenceSensor",
CONF_USE_MOTION_FEATURE: True, # motion sensor need to be checked AND a motion sensor set
@@ -456,7 +490,7 @@ async def test_bug_500_3(hass: HomeAssistant, init_vtherm_api) -> None:
flow = VersatileThermostatBaseConfigFlow(config)
assert flow._infos[CONF_USE_WINDOW_FEATURE] is True
- assert flow._infos[CONF_USE_POWER_FEATURE] is True
+ assert flow._infos[CONF_USE_POWER_FEATURE] is False
assert flow._infos[CONF_USE_PRESENCE_FEATURE] is True
assert flow._infos[CONF_USE_MOTION_FEATURE] is True
diff --git a/tests/test_central_config.py b/tests/test_central_config.py
index 11cf3bb..2785157 100644
--- a/tests/test_central_config.py
+++ b/tests/test_central_config.py
@@ -188,6 +188,18 @@ async def test_full_over_switch_wo_central_config(
hass: HomeAssistant, skip_hass_states_is_state, init_vtherm_api
):
"""Tests that a VTherm without any central_configuration is working with its own attributes"""
+
+ temps = {
+ "frost": 10,
+ "eco": 17,
+ "comfort": 18,
+ "boost": 21,
+ "frost_away": 13,
+ "eco_away": 13,
+ "comfort_away": 13,
+ "boost_away": 13,
+ }
+
# Add a Switch VTherm
entry = MockConfigEntry(
domain=DOMAIN,
@@ -202,19 +214,11 @@ async def test_full_over_switch_wo_central_config(
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
CONF_STEP_TEMPERATURE: 0.3,
- "frost_temp": 10,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 21,
- "frost_away_temp": 13,
- "eco_away_temp": 13,
- "comfort_away_temp": 13,
- "boost_away_temp": 13,
CONF_USE_WINDOW_FEATURE: True,
CONF_USE_MOTION_FEATURE: True,
CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: True,
- CONF_HEATER: "switch.mock_switch",
+ CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_INVERSE_SWITCH: False,
CONF_TPI_COEF_INT: 0.3,
@@ -233,8 +237,6 @@ async def test_full_over_switch_wo_central_config(
CONF_MOTION_PRESET: "comfort",
CONF_NO_MOTION_PRESET: "eco",
CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor",
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor",
CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor",
CONF_USE_MAIN_CENTRAL_CONFIG: False,
CONF_USE_TPI_CENTRAL_CONFIG: False,
@@ -249,7 +251,7 @@ async def test_full_over_switch_wo_central_config(
with patch("homeassistant.core.ServiceRegistry.async_call"):
entity: ThermostatOverSwitch = await create_thermostat(
- hass, entry, "climate.theoverswitchmockname"
+ hass, entry, "climate.theoverswitchmockname", temps
)
assert entity
assert entity.name == "TheOverSwitchMockName"
@@ -300,10 +302,13 @@ async def test_full_over_switch_wo_central_config(
assert entity.motion_manager.motion_preset == "comfort"
assert entity.motion_manager.no_motion_preset == "eco"
- assert entity.power_manager.power_sensor_entity_id == "sensor.mock_power_sensor"
assert (
- entity.power_manager.max_power_sensor_entity_id
- == "sensor.mock_max_power_sensor"
+ VersatileThermostatAPI.get_vtherm_api().central_power_manager.power_sensor_entity_id
+ is None
+ )
+ assert (
+ VersatileThermostatAPI.get_vtherm_api().central_power_manager.max_power_sensor_entity_id
+ is None
)
assert (
@@ -317,7 +322,7 @@ async def test_full_over_switch_wo_central_config(
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_full_over_switch_with_central_config(
- hass: HomeAssistant, skip_hass_states_is_state, init_central_config
+ hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
):
"""Tests that a VTherm with central_configuration is working with the central_config attributes"""
# Add a Switch VTherm
@@ -334,15 +339,11 @@ async def test_full_over_switch_with_central_config(
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
CONF_STEP_TEMPERATURE: 0.3,
- "frost_temp": 10,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 21,
CONF_USE_WINDOW_FEATURE: True,
CONF_USE_MOTION_FEATURE: True,
CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: True,
- CONF_HEATER: "switch.mock_switch",
+ CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_INVERSE_SWITCH: False,
CONF_TPI_COEF_INT: 0.3,
@@ -361,8 +362,6 @@ async def test_full_over_switch_with_central_config(
CONF_MOTION_PRESET: "comfort",
CONF_NO_MOTION_PRESET: "eco",
CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor",
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor",
CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor",
CONF_USE_MAIN_CENTRAL_CONFIG: True,
CONF_USE_TPI_CENTRAL_CONFIG: True,
@@ -426,10 +425,13 @@ async def test_full_over_switch_with_central_config(
assert entity.motion_manager.motion_preset == "boost"
assert entity.motion_manager.no_motion_preset == "frost"
- assert entity.power_manager.power_sensor_entity_id == "sensor.mock_power_sensor"
assert (
- entity.power_manager.max_power_sensor_entity_id
- == "sensor.mock_max_power_sensor"
+ VersatileThermostatAPI.get_vtherm_api().central_power_manager.power_sensor_entity_id
+ == "sensor.the_power_sensor"
+ )
+ assert (
+ VersatileThermostatAPI.get_vtherm_api().central_power_manager.max_power_sensor_entity_id
+ == "sensor.the_max_power_sensor"
)
assert (
diff --git a/tests/test_central_power_manager.py b/tests/test_central_power_manager.py
new file mode 100644
index 0000000..f2a4852
--- /dev/null
+++ b/tests/test_central_power_manager.py
@@ -0,0 +1,702 @@
+# pylint: disable=protected-access, unused-argument, line-too-long
+""" Test the Central Power management """
+from unittest.mock import patch, AsyncMock, MagicMock, PropertyMock
+from datetime import datetime, timedelta
+import logging
+
+from custom_components.versatile_thermostat.feature_power_manager import (
+ FeaturePowerManager,
+)
+from custom_components.versatile_thermostat.central_feature_power_manager import (
+ CentralFeaturePowerManager,
+)
+from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
+
+logging.getLogger().setLevel(logging.DEBUG)
+
+
+@pytest.mark.parametrize(
+ "use_power_feature, power_entity_id, max_power_entity_id, power_temp, is_configured",
+ [
+ (True, "sensor.power_id", "sensor.max_power_id", 13, True),
+ (True, None, "sensor.max_power_id", 13, False),
+ (True, "sensor.power_id", None, 13, False),
+ (True, "sensor.power_id", "sensor.max_power_id", None, False),
+ (False, "sensor.power_id", "sensor.max_power_id", 13, False),
+ ],
+)
+async def test_central_power_manager_init(
+ hass: HomeAssistant,
+ use_power_feature,
+ power_entity_id,
+ max_power_entity_id,
+ power_temp,
+ is_configured,
+):
+ """Test creation and post_init of the Central Power Manager"""
+ vtherm_api: VersatileThermostatAPI = MagicMock(spec=VersatileThermostatAPI)
+ central_power_manager = CentralFeaturePowerManager(hass, vtherm_api)
+
+ assert central_power_manager.is_configured is False
+ assert central_power_manager.current_max_power is None
+ assert central_power_manager.current_power is None
+ assert central_power_manager.power_temperature is None
+ assert central_power_manager.name == "centralPowerManager"
+
+ # 2. post_init
+ central_power_manager.post_init(
+ {
+ CONF_POWER_SENSOR: power_entity_id,
+ CONF_MAX_POWER_SENSOR: max_power_entity_id,
+ CONF_USE_POWER_FEATURE: use_power_feature,
+ CONF_PRESET_POWER: power_temp,
+ }
+ )
+
+ assert central_power_manager.is_configured == is_configured
+ assert central_power_manager.current_max_power is None
+ assert central_power_manager.current_power is None
+ assert central_power_manager.power_temperature == power_temp
+
+ # 3. start listening
+ await central_power_manager.start_listening()
+ assert len(central_power_manager._active_listener) == (2 if is_configured else 0)
+
+ # 4. stop listening
+ central_power_manager.stop_listening()
+ assert len(central_power_manager._active_listener) == 0
+
+
+@pytest.mark.parametrize(
+ "vtherm_configs, results",
+ [
+ # simple sort
+ (
+ [
+ {
+ "name": "vtherm1",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 13,
+ "target_temperature": 12,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm2",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 18,
+ "target_temperature": 12,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm3",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 12,
+ "target_temperature": 18,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ ],
+ ["vtherm2", "vtherm1", "vtherm3"],
+ ),
+ # Ignore power not configured and not on
+ (
+ [
+ {
+ "name": "vtherm1",
+ "is_configured": False,
+ "is_on": True,
+ "current_temperature": 13,
+ "target_temperature": 12,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm2",
+ "is_configured": True,
+ "is_on": False,
+ "current_temperature": 18,
+ "target_temperature": 12,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm3",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 12,
+ "target_temperature": 18,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ ],
+ ["vtherm3"],
+ ),
+ # None current_temperature are in last
+ (
+ [
+ {
+ "name": "vtherm1",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 13,
+ "target_temperature": 12,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm2",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": None,
+ "target_temperature": 12,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm3",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 12,
+ "target_temperature": 18,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ ],
+ ["vtherm1", "vtherm3", "vtherm2"],
+ ),
+ # None target_temperature are in last
+ (
+ [
+ {
+ "name": "vtherm1",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 13,
+ "target_temperature": 12,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm2",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 18,
+ "target_temperature": None,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ {
+ "name": "vtherm3",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 12,
+ "target_temperature": 18,
+ "saved_target_temp": 18,
+ "is_overpowering_detected": False,
+ },
+ ],
+ ["vtherm1", "vtherm3", "vtherm2"],
+ ),
+ # simple sort with overpowering detected
+ (
+ [
+ {
+ "name": "vtherm1",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 13,
+ # "target_temperature": 12,
+ "saved_target_temp": 21,
+ "is_overpowering_detected": True,
+ },
+ {
+ "name": "vtherm2",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 18,
+ # "target_temperature": 12,
+ "saved_target_temp": 17,
+ "is_overpowering_detected": True,
+ },
+ {
+ "name": "vtherm3",
+ "is_configured": True,
+ "is_on": True,
+ "current_temperature": 12,
+ # "target_temperature": 18,
+ "saved_target_temp": 16,
+ "is_overpowering_detected": True,
+ },
+ ],
+ ["vtherm2", "vtherm3", "vtherm1"],
+ ),
+ ],
+)
+async def test_central_power_manageer_find_vtherms(
+ hass: HomeAssistant, vtherm_configs, results
+):
+ """Test the find_all_vtherm_with_power_management_sorted_by_dtemp"""
+ vtherm_api: VersatileThermostatAPI = MagicMock(spec=VersatileThermostatAPI)
+ central_power_manager = CentralFeaturePowerManager(hass, vtherm_api)
+
+ vtherms = []
+ for vtherm_config in vtherm_configs:
+ vtherm = MagicMock(spec=BaseThermostat)
+ vtherm.name = vtherm_config.get("name")
+ vtherm.is_on = vtherm_config.get("is_on")
+ vtherm.current_temperature = vtherm_config.get("current_temperature")
+ vtherm.target_temperature = vtherm_config.get("target_temperature")
+ vtherm.saved_target_temp = vtherm_config.get("saved_target_temp")
+ vtherm.power_manager.is_configured = vtherm_config.get("is_configured")
+ vtherm.power_manager.is_overpowering_detected = vtherm_config.get("is_overpowering_detected")
+ vtherms.append(vtherm)
+
+ with patch(
+ "custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.get_climate_components_entities",
+ return_value=vtherms,
+ ):
+ vtherm_sorted = (
+ central_power_manager.find_all_vtherm_with_power_management_sorted_by_dtemp()
+ )
+
+ # extract results
+ vtherm_results = [vtherm.name for vtherm in vtherm_sorted]
+
+ assert vtherm_results == results
+
+
+@pytest.mark.parametrize(
+ "current_power, current_max_power, vtherm_configs, expected_results",
+ [
+ # simple nominal test (initialize overpowering state in VTherm)
+ (
+ 1000,
+ 5000,
+ [
+ {
+ "name": "vtherm1",
+ "device_power": 100,
+ "is_device_active": False,
+ "is_over_climate": False,
+ "nb_underlying_entities": 1,
+ "on_percent": 0,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_UNKNOWN,
+ },
+ {
+ "name": "vtherm2",
+ "device_power": 10000,
+ "is_device_active": True,
+ "is_over_climate": False,
+ "nb_underlying_entities": 4,
+ "on_percent": 100,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_UNKNOWN,
+ },
+ {
+ "name": "vtherm3",
+ "device_power": 5000,
+ "is_device_active": True,
+ "is_over_climate": True,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_UNKNOWN,
+ },
+ {"name": "vtherm4", "device_power": 1000, "is_device_active": True, "is_over_climate": True, "is_overpowering_detected": False, "overpowering_state": STATE_OFF},
+ ],
+ # init vtherm1 to False
+ {"vtherm3": False, "vtherm2": False, "vtherm1": False},
+ ),
+ # Un-shedding only (will be taken in reverse order)
+ (
+ 1000,
+ 2000,
+ [
+ # should be not unshedded (too much power will be added)
+ {
+ "name": "vtherm1",
+ "device_power": 800,
+ "is_device_active": False,
+ "is_over_climate": False,
+ "nb_underlying_entities": 1,
+ "on_percent": 1,
+ "is_overpowering_detected": True,
+ "overpowering_state": STATE_ON,
+ },
+ # already stay unshedded cause already unshedded
+ {
+ "name": "vtherm2",
+ "device_power": 100,
+ "is_device_active": True,
+ "is_over_climate": True,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_OFF,
+ },
+ # should be unshedded
+ {
+ "name": "vtherm3",
+ "device_power": 200,
+ "is_device_active": False,
+ "is_over_climate": True,
+ "is_overpowering_detected": True,
+ "overpowering_state": STATE_ON,
+ },
+ # should be unshedded
+ {
+ "name": "vtherm4",
+ "device_power": 300,
+ "is_device_active": False,
+ "is_over_climate": False,
+ "nb_underlying_entities": 1,
+ "on_percent": 1,
+ "is_overpowering_detected": True,
+ "overpowering_state": STATE_ON,
+ },
+ ],
+ {"vtherm4": False, "vtherm3": False},
+ ),
+ # Shedding
+ (
+ 2000,
+ 1000,
+ [
+ # should be overpowering
+ {
+ "name": "vtherm1",
+ "device_power": 300,
+ "is_device_active": True,
+ "is_over_climate": False,
+ "nb_underlying_entities": 1,
+ "on_percent": 1,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_OFF,
+ },
+ # should be overpowering with many underlmying entities
+ {
+ "name": "vtherm2",
+ "device_power": 400,
+ "is_device_active": True,
+ "is_over_climate": False,
+ "nb_underlying_entities": 4,
+ "on_percent": 0.1,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_UNKNOWN,
+ },
+ # over_climate should be overpowering
+ {
+ "name": "vtherm3",
+ "device_power": 100,
+ "is_device_active": True,
+ "is_over_climate": True,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_OFF,
+ },
+ # should pass cause not active
+ {
+ "name": "vtherm4",
+ "device_power": 800,
+ "is_device_active": False,
+ "is_over_climate": False,
+ "nb_underlying_entities": 1,
+ "on_percent": 1,
+ "is_overpowering_detected": False,
+ },
+ # should be not overpowering (already overpowering)
+ {
+ "name": "vtherm5",
+ "device_power": 400,
+ "is_device_active": True,
+ "is_over_climate": False,
+ "nb_underlying_entities": 4,
+ "on_percent": 0.1,
+ "is_overpowering_detected": True,
+ "overpowering_state": STATE_ON,
+ },
+ # should be overpowering with many underluying entities
+ {
+ "name": "vtherm6",
+ "device_power": 400,
+ "is_device_active": True,
+ "is_over_climate": False,
+ "nb_underlying_entities": 4,
+ "on_percent": 0.1,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_UNKNOWN,
+ },
+ # should not be overpowering (we have enough)
+ {
+ "name": "vtherm7",
+ "device_power": 1000,
+ "is_device_active": True,
+ "is_over_climate": True,
+ "is_overpowering_detected": False,
+ "overpowering_state": STATE_UNKNOWN,
+ },
+ ],
+ {"vtherm1": True, "vtherm2": True, "vtherm3": True, "vtherm6": True},
+ ),
+ ],
+)
+# @pytest.mark.skip
+async def test_central_power_manageer_calculate_shedding(
+ hass: HomeAssistant,
+ current_power,
+ current_max_power,
+ vtherm_configs,
+ expected_results,
+):
+ """Test the calculate_shedding of the CentralPowerManager"""
+ vtherm_api: VersatileThermostatAPI = MagicMock(spec=VersatileThermostatAPI)
+ central_power_manager = CentralFeaturePowerManager(hass, vtherm_api)
+
+ registered_calls = {}
+
+ def register_call(vtherm, overpowering):
+ """Register a call to set_overpowering"""
+ registered_calls.update({vtherm.name: overpowering})
+
+ vtherms = []
+ for vtherm_config in vtherm_configs:
+ vtherm = MagicMock(spec=BaseThermostat)
+ vtherm.name = vtherm_config.get("name")
+ vtherm.is_device_active = vtherm_config.get("is_device_active")
+ vtherm.is_over_climate = vtherm_config.get("is_over_climate")
+ vtherm.nb_underlying_entities = vtherm_config.get("nb_underlying_entities")
+ if not vtherm_config.get("is_over_climate"):
+ vtherm.proportional_algorithm = MagicMock()
+ vtherm.on_percent = vtherm.proportional_algorithm.on_percent = vtherm_config.get("on_percent")
+ else:
+ vtherm.on_percent = None
+ vtherm.proportional_algorithm = None
+
+ vtherm.power_manager = MagicMock(spec=FeaturePowerManager)
+ vtherm.power_manager._vtherm = vtherm
+
+ vtherm.power_manager.is_overpowering_detected = vtherm_config.get(
+ "is_overpowering_detected"
+ )
+ vtherm.power_manager.device_power = vtherm_config.get("device_power")
+ vtherm.power_manager.overpowering_state = vtherm_config.get("overpowering_state")
+
+ async def mock_set_overpowering(
+ overpowering, power_consumption_max=0, v=vtherm
+ ):
+ register_call(v, overpowering)
+
+ vtherm.power_manager.set_overpowering = mock_set_overpowering
+
+ vtherms.append(vtherm)
+
+ # fmt:off
+ with patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.find_all_vtherm_with_power_management_sorted_by_dtemp", return_value=vtherms), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.current_max_power", new_callable=PropertyMock, return_value=current_max_power), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.current_power", new_callable=PropertyMock, return_value=current_power), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.is_configured", new_callable=PropertyMock, return_value=True):
+ # fmt:on
+
+ await central_power_manager.calculate_shedding()
+
+ # Check registered calls
+ assert registered_calls == expected_results
+
+
+@pytest.mark.parametrize(
+ "dsecs, power, nb_call",
+ [
+ (0, 1000, 1),
+ (0, None, 0),
+ (0, STATE_UNAVAILABLE, 0),
+ (0, STATE_UNKNOWN, 0),
+ (21, 1000, 1),
+ (19, 1000, 1),
+ ],
+)
+async def test_central_power_manager_power_event(
+ hass: HomeAssistant, dsecs, power, nb_call
+):
+ """Tests the Power sensor event"""
+ vtherm_api: VersatileThermostatAPI = MagicMock(spec=VersatileThermostatAPI)
+ central_power_manager = CentralFeaturePowerManager(hass, vtherm_api)
+
+ assert central_power_manager.current_power is None
+ assert central_power_manager.power_temperature is None
+ assert central_power_manager.name == "centralPowerManager"
+
+ # 2. post_init
+ central_power_manager.post_init(
+ {
+ CONF_POWER_SENSOR: "sensor.power_entity_id",
+ CONF_MAX_POWER_SENSOR: "sensor.max_power_entity_id",
+ CONF_USE_POWER_FEATURE: True,
+ CONF_PRESET_POWER: 13,
+ }
+ )
+
+ assert central_power_manager.is_configured is True
+ assert central_power_manager.current_max_power is None
+ assert central_power_manager.current_power is None
+ assert central_power_manager.power_temperature == 13
+
+ # 3. start listening (not really useful but don't eat bread)
+ await central_power_manager.start_listening()
+ assert len(central_power_manager._active_listener) == 2
+
+ now: datetime = NowClass.get_now(hass)
+ # vtherm_api._set_now(now) vtherm_api is a MagicMock
+ vtherm_api.now = now
+
+ # 4. Call the _power_sensor_changed
+ side_effects = SideEffects(
+ {
+ "sensor.power_entity_id": State("sensor.power_entity_id", power),
+ "sensor.max_power_entity_id": State("sensor.max_power_entity_id", power),
+ },
+ State("unknown.entity_id", "unknown"),
+ )
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.calculate_shedding", new_callable=AsyncMock) as mock_calculate_shedding:
+ # fmt:on
+ # set a default value to see if it has been replaced
+ central_power_manager._current_power = -999
+ await central_power_manager._power_sensor_changed(event=Event(
+ event_type=EVENT_STATE_CHANGED,
+ data={
+ "entity_id": "sensor.power_entity_id",
+ "new_state": State("sensor.power_entity_id", power),
+ "old_state": State("sensor.power_entity_id", STATE_UNAVAILABLE),
+ }))
+
+ if nb_call > 0:
+ await central_power_manager._do_immediate_shedding()
+
+ expected_power = power if isinstance(power, (int, float)) else -999
+ assert central_power_manager.current_power == expected_power
+ assert mock_calculate_shedding.call_count == nb_call
+
+ # Do another call x seconds later
+ now = now + timedelta(seconds=dsecs)
+ vtherm_api.now = now
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.calculate_shedding", new_callable=AsyncMock) as mock_calculate_shedding:
+ # fmt:on
+ central_power_manager._current_power = -999
+
+ await central_power_manager._power_sensor_changed(event=Event(
+ event_type=EVENT_STATE_CHANGED,
+ data={
+ "entity_id": "sensor.power_entity_id",
+ "new_state": State("sensor.power_entity_id", power),
+ "old_state": State("sensor.power_entity_id", STATE_UNAVAILABLE),
+ }))
+
+ if nb_call > 0:
+ await central_power_manager._do_immediate_shedding()
+
+ assert central_power_manager.current_power == expected_power
+ assert mock_calculate_shedding.call_count == nb_call
+
+
+@pytest.mark.parametrize(
+ "dsecs, max_power, nb_call",
+ [
+ (0, 1000, 1),
+ (0, None, 0),
+ (0, STATE_UNAVAILABLE, 0),
+ (0, STATE_UNKNOWN, 0),
+ (21, 1000, 1),
+ (19, 1000, 1),
+ ],
+)
+async def test_central_power_manager_max_power_event(
+ hass: HomeAssistant, dsecs, max_power, nb_call
+):
+ """Tests the Power sensor event"""
+ vtherm_api: VersatileThermostatAPI = MagicMock(spec=VersatileThermostatAPI)
+ central_power_manager = CentralFeaturePowerManager(hass, vtherm_api)
+
+ assert central_power_manager.current_power is None
+ assert central_power_manager.power_temperature is None
+ assert central_power_manager.name == "centralPowerManager"
+
+ # 2. post_init
+ central_power_manager.post_init(
+ {
+ CONF_POWER_SENSOR: "sensor.power_entity_id",
+ CONF_MAX_POWER_SENSOR: "sensor.max_power_entity_id",
+ CONF_USE_POWER_FEATURE: True,
+ CONF_PRESET_POWER: 13,
+ }
+ )
+
+ assert central_power_manager.is_configured is True
+ assert central_power_manager.current_max_power is None
+ assert central_power_manager.current_power is None
+ assert central_power_manager.power_temperature == 13
+
+ # 3. start listening (not really useful but don't eat bread)
+ await central_power_manager.start_listening()
+ assert len(central_power_manager._active_listener) == 2
+
+ now: datetime = NowClass.get_now(hass)
+ # vtherm_api._set_now(now) vtherm_api is a MagicMock
+ vtherm_api.now = now
+
+ # 4. Call the _power_sensor_changed
+ side_effects = SideEffects(
+ {
+ "sensor.power_entity_id": State("sensor.power_entity_id", max_power),
+ "sensor.max_power_entity_id": State(
+ "sensor.max_power_entity_id", max_power
+ ),
+ },
+ State("unknown.entity_id", "unknown"),
+ )
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.calculate_shedding", new_callable=AsyncMock) as mock_calculate_shedding:
+ # fmt:on
+ # set a default value to see if it has been replaced
+ central_power_manager._current_max_power = -999
+ await central_power_manager._power_sensor_changed(event=Event(
+ event_type=EVENT_STATE_CHANGED,
+ data={
+ "entity_id": "sensor.max_power_entity_id",
+ "new_state": State("sensor.max_power_entity_id", max_power),
+ "old_state": State("sensor.max_power_entity_id", STATE_UNAVAILABLE),
+ }))
+
+ if nb_call > 0:
+ await central_power_manager._do_immediate_shedding()
+
+ expected_power = max_power if isinstance(max_power, (int, float)) else -999
+ assert central_power_manager.current_max_power == expected_power
+ assert mock_calculate_shedding.call_count == nb_call
+
+ # Do another call x seconds later
+ now = now + timedelta(seconds=dsecs)
+ vtherm_api.now = now
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.calculate_shedding", new_callable=AsyncMock) as mock_calculate_shedding:
+ # fmt:on
+ central_power_manager._current_max_power = -999
+
+ await central_power_manager._power_sensor_changed(event=Event(
+ event_type=EVENT_STATE_CHANGED,
+ data={
+ "entity_id": "sensor.max_power_entity_id",
+ "new_state": State("sensor.max_power_entity_id", max_power),
+ "old_state": State("sensor.max_power_entity_id", STATE_UNAVAILABLE),
+ }))
+
+ if nb_call > 0:
+ await central_power_manager._do_immediate_shedding()
+
+ assert central_power_manager.current_max_power == expected_power
+ assert mock_calculate_shedding.call_count == nb_call
diff --git a/tests/test_motion.py b/tests/test_motion.py
index a89a54d..ae7bf00 100644
--- a/tests/test_motion.py
+++ b/tests/test_motion.py
@@ -90,7 +90,7 @@ async def test_motion_feature_manager_refresh(
assert custom_attributes["motion_off_delay_sec"] == 30
# 3. start listening
- motion_manager.start_listening()
+ await motion_manager.start_listening()
assert motion_manager.is_configured is True
assert motion_manager.motion_state == STATE_UNKNOWN
assert motion_manager.is_motion_detected is False
@@ -198,7 +198,7 @@ async def test_motion_feature_manager_event(
CONF_NO_MOTION_PRESET: PRESET_ECO,
}
)
- motion_manager.start_listening()
+ await motion_manager.start_listening()
# 2. test _motion_sensor_changed with the parametrized
# fmt: off
diff --git a/tests/test_multiple_switch.py b/tests/test_multiple_switch.py
index e0e8583..38be50b 100644
--- a/tests/test_multiple_switch.py
+++ b/tests/test_multiple_switch.py
@@ -721,10 +721,14 @@ async def test_multiple_climates_underlying_changes_not_aligned(
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_multiple_switch_power_management(
- hass: HomeAssistant, skip_hass_states_is_state
+ hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
):
"""Test the Power management"""
-
+ temps = {
+ "eco": 17,
+ "comfort": 18,
+ "boost": 19,
+ }
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
@@ -737,17 +741,16 @@ async def test_multiple_switch_power_management(
CONF_CYCLE_MIN: 8,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: False,
- CONF_HEATER: "switch.mock_switch1",
- CONF_HEATER_2: "switch.mock_switch2",
- CONF_HEATER_3: "switch.mock_switch3",
- CONF_HEATER_4: "switch.mock_switch4",
+ CONF_UNDERLYING_LIST: [
+ "switch.mock_switch1",
+ "switch.mock_switch2",
+ "switch.mock_switch3",
+ "switch.mock_switch4",
+ ],
CONF_HEATER_KEEP_ALIVE: 0,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
@@ -755,15 +758,13 @@ async def test_multiple_switch_power_management(
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: BaseThermostat = await create_thermostat(
- hass, entry, "climate.theover4switchmockname"
+ hass, entry, "climate.theover4switchmockname", temps
)
assert entity
assert entity.is_over_climate is False
@@ -772,6 +773,9 @@ async def test_multiple_switch_power_management(
tpi_algo = entity._prop_algorithm
assert tpi_algo
+ now: datetime = NowClass.get_now(hass)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
await entity.async_set_hvac_mode(HVACMode.HEAT)
await entity.async_set_preset_mode(PRESET_BOOST)
assert entity.hvac_mode is HVACMode.HEAT
@@ -779,77 +783,109 @@ async def test_multiple_switch_power_management(
assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert entity.target_temperature == 19
+ # make the heater heats
+ await send_temperature_change_event(entity, 15, now)
+ await send_ext_temperature_change_event(entity, 1, now)
+ await hass.async_block_till_done()
+
# 1. Send power mesurement
- await send_power_change_event(entity, 50, datetime.now())
+ side_effects = SideEffects(
+ {
+ "sensor.the_power_sensor": State("sensor.the_power_sensor", 50),
+ "sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 300),
+ },
+ State("unknown.entity_id", "unknown"),
+ )
+
# Send power max mesurement
- await send_max_power_change_event(entity, 300, datetime.now())
- assert await entity.power_manager.check_overpowering() is False
- # All configuration is complete and power is < power_max
- assert entity.preset_mode is PRESET_BOOST
- assert entity.power_manager.overpowering_state is STATE_OFF
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
+ # fmt: on
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
+ await send_power_change_event(entity, 50, datetime.now())
+ await send_max_power_change_event(entity, 300, datetime.now())
+ assert entity.power_manager.is_overpowering_detected is False
+ # All configuration is complete and power is < power_max
+ assert entity.preset_mode is PRESET_BOOST
+ assert entity.power_manager.overpowering_state is STATE_OFF
# 2. Send power max mesurement too low and HVACMode is on
- with patch(
- "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
- ) as mock_send_event, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
- ) as mock_heater_on, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
- ) as mock_heater_off:
- # 100 of the device / 4 -> 25, current power 50 so max is 75
- await send_max_power_change_event(entity, 74, datetime.now())
- assert await entity.power_manager.check_overpowering() is True
- # All configuration is complete and power is > power_max we switch to POWER preset
- assert entity.preset_mode is PRESET_POWER
- assert entity.power_manager.overpowering_state is STATE_ON
- assert entity.target_temperature == 12
+ side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 49))
- assert mock_send_event.call_count == 2
- mock_send_event.assert_has_calls(
- [
- call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_POWER}),
- call.send_event(
- EventType.POWER_EVENT,
- {
- "type": "start",
- "current_power": 50,
- "device_power": 100,
- "current_max_power": 74,
- "current_power_consumption": 25.0,
- },
- ),
- ],
- any_order=True,
- )
- assert mock_heater_on.call_count == 0
- assert mock_heater_off.call_count == 4 # The fourth are shutdown
+ #fmt: off
+ with patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on") as mock_heater_on, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off") as mock_heater_off, \
+ patch("custom_components.versatile_thermostat.thermostat_switch.ThermostatOverSwitch.is_device_active", return_value="True"):
+ #fmt: on
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
+ assert entity.power_percent > 0
+ # 100 of the device / 4 -> 25, current power 50 so max is 75
+ await send_max_power_change_event(entity, 49, datetime.now())
+ assert entity.power_manager.is_overpowering_detected is True
+ # All configuration is complete and power is > power_max we switch to POWER preset
+ assert entity.preset_mode is PRESET_POWER
+ assert entity.power_manager.overpowering_state is STATE_ON
+ assert entity.target_temperature == 12
+
+ assert mock_send_event.call_count == 2
+ mock_send_event.assert_has_calls(
+ [
+ call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_POWER}),
+ call.send_event(
+ EventType.POWER_EVENT,
+ {
+ "type": "start",
+ "current_power": 50,
+ "device_power": 100,
+ "current_max_power": 49,
+ "current_power_consumption": 100,
+ },
+ ),
+ ],
+ any_order=True,
+ )
+ assert mock_heater_on.call_count == 0
+ assert mock_heater_off.call_count == 4 # The fourth are shutdown
# 3. change PRESET
- with patch(
- "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
- ) as mock_send_event:
- await entity.async_set_preset_mode(PRESET_ECO)
- assert entity.preset_mode is PRESET_ECO
- # No change
- assert entity.power_manager.overpowering_state is STATE_ON
+ with patch(
+ "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
+ ) as mock_send_event:
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
+ await entity.async_set_preset_mode(PRESET_ECO)
+ assert entity.preset_mode is PRESET_ECO
+ # No change cause temperature is very low
+ assert entity.power_manager.overpowering_state is STATE_ON
# 4. Send hugh power max mesurement to release overpowering
- with patch(
- "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
- ) as mock_send_event, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
- ) as mock_heater_on, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
- ) as mock_heater_off:
- # 100 of the device / 4 -> 25, current power 50 so max is 75. With 150 no overheating
- await send_max_power_change_event(entity, 150, datetime.now())
- assert await entity.power_manager.check_overpowering() is False
- # All configuration is complete and power is > power_max we switch to POWER preset
- assert entity.preset_mode is PRESET_ECO
- assert entity.power_manager.overpowering_state is STATE_OFF
- assert entity.target_temperature == 17
+ side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 150))
- assert (
- mock_heater_on.call_count == 0
- ) # The fourth are not restarted because temperature is enought
- assert mock_heater_off.call_count == 0
+ with patch(
+ "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
+ ) as mock_send_event, patch(
+ "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
+ ) as mock_heater_on, patch(
+ "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
+ ) as mock_heater_off:
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
+ # 100 of the device / 4 -> 25, current power 50 so max is 75. With 150 no overheating
+ await send_max_power_change_event(entity, 150, datetime.now())
+ assert entity.power_manager.is_overpowering_detected is False
+ # All configuration is complete and power is > power_max we switch to POWER preset
+ assert entity.preset_mode is PRESET_ECO
+ assert entity.power_manager.overpowering_state is STATE_OFF
+ assert entity.target_temperature == 17
+
+ assert (
+ mock_heater_on.call_count == 0
+ ) # The fourth are not restarted because temperature is enought
+ assert mock_heater_off.call_count == 0
diff --git a/tests/test_overclimate.py b/tests/test_overclimate.py
index 1c5efca..53da490 100644
--- a/tests/test_overclimate.py
+++ b/tests/test_overclimate.py
@@ -212,6 +212,13 @@ async def test_underlying_change_follow(
tz = get_tz(hass) # pylint: disable=invalid-name
now: datetime = datetime.now(tz=tz)
+ temps = {
+ PRESET_FROST_PROTECTION: 7,
+ PRESET_ECO: 16,
+ PRESET_COMFORT: 17,
+ PRESET_BOOST: 18,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
@@ -232,7 +239,7 @@ async def test_underlying_change_follow(
) as mock_find_climate, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
) as mock_underlying_set_hvac_mode:
- entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname")
+ entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname", temps)
assert entity
assert entity.name == "TheOverClimateMockName"
@@ -354,6 +361,13 @@ async def test_underlying_change_not_follow(
tz = get_tz(hass) # pylint: disable=invalid-name
now: datetime = datetime.now(tz=tz)
+ temps = {
+ PRESET_FROST_PROTECTION: 7,
+ PRESET_ECO: 16,
+ PRESET_COMFORT: 17,
+ PRESET_BOOST: 18,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
@@ -374,7 +388,7 @@ async def test_underlying_change_not_follow(
) as mock_find_climate, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
) as mock_underlying_set_hvac_mode:
- entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname")
+ entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname", temps)
assert entity
@@ -726,6 +740,13 @@ async def test_ignore_temp_outside_minmax_range(
tz = get_tz(hass) # pylint: disable=invalid-name
now: datetime = datetime.now(tz=tz)
+ temps = {
+ PRESET_FROST_PROTECTION: 7,
+ PRESET_ECO: 16,
+ PRESET_COMFORT: 17,
+ PRESET_BOOST: 18,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
@@ -746,7 +767,7 @@ async def test_ignore_temp_outside_minmax_range(
) as mock_find_climate, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
) as mock_underlying_set_hvac_mode:
- entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname")
+ entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname", temps)
assert entity
diff --git a/tests/test_power.py b/tests/test_power.py
index c2b1828..8f5ee28 100644
--- a/tests/test_power.py
+++ b/tests/test_power.py
@@ -10,6 +10,7 @@ from custom_components.versatile_thermostat.thermostat_switch import (
from custom_components.versatile_thermostat.feature_power_manager import (
FeaturePowerManager,
)
+
from custom_components.versatile_thermostat.prop_algorithm import PropAlgorithm
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@@ -17,28 +18,28 @@ logging.getLogger().setLevel(logging.DEBUG)
@pytest.mark.parametrize(
- "is_over_climate, is_device_active, power, max_power, current_overpowering_state, overpowering_state, nb_call, changed, check_overpowering_ret",
+ "is_over_climate, is_device_active, power, max_power, check_power_available",
[
# don't switch to overpower (power is enough)
- (False, False, 1000, 3000, STATE_OFF, STATE_OFF, 0, True, False),
+ (False, False, 1000, 3000, True),
# switch to overpower (power is not enough)
- (False, False, 2000, 3000, STATE_OFF, STATE_ON, 1, True, True),
+ (False, False, 2000, 3000, False),
# don't switch to overpower (power is not enough but device is already on)
- (False, True, 2000, 3000, STATE_OFF, STATE_OFF, 0, True, False),
+ (False, True, 2000, 3000, True),
# Same with a over_climate
# don't switch to overpower (power is enough)
- (True, False, 1000, 3000, STATE_OFF, STATE_OFF, 0, True, False),
+ (True, False, 1000, 3000, True),
# switch to overpower (power is not enough)
- (True, False, 2000, 3000, STATE_OFF, STATE_ON, 1, True, True),
+ (True, False, 2000, 3000, False),
# don't switch to overpower (power is not enough but device is already on)
- (True, True, 2000, 3000, STATE_OFF, STATE_OFF, 0, True, False),
+ (True, True, 2000, 3000, True),
# Leave overpowering state
# switch to not overpower (power is enough)
- (False, False, 1000, 3000, STATE_ON, STATE_OFF, 1, True, False),
+ (False, False, 1000, 3000, True),
# don't switch to overpower (power is still not enough)
- (False, False, 2000, 3000, STATE_ON, STATE_ON, 0, True, True),
+ (False, False, 2000, 3000, False),
# keep overpower (power is not enough but device is already on)
- (False, True, 3000, 3000, STATE_ON, STATE_ON, 0, True, True),
+ (False, True, 3000, 3000, False),
],
)
async def test_power_feature_manager(
@@ -47,17 +48,15 @@ async def test_power_feature_manager(
is_device_active,
power,
max_power,
- current_overpowering_state,
- overpowering_state,
- nb_call,
- changed,
- check_overpowering_ret,
+ check_power_available,
):
"""Test the FeaturePresenceManager class direclty"""
fake_vtherm = MagicMock(spec=BaseThermostat)
type(fake_vtherm).name = PropertyMock(return_value="the name")
+ vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
+
# 1. creation
power_manager = FeaturePowerManager(fake_vtherm, hass)
@@ -80,16 +79,27 @@ async def test_power_feature_manager(
assert custom_attributes["current_max_power"] is None
# 2. post_init
- power_manager.post_init(
+ vtherm_api.find_central_configuration = MagicMock()
+ vtherm_api.central_power_manager.post_init(
{
CONF_POWER_SENSOR: "sensor.the_power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.the_max_power_sensor",
+ CONF_USE_POWER_FEATURE: True,
+ CONF_PRESET_POWER: 13,
+ }
+ )
+ assert vtherm_api.central_power_manager.is_configured
+
+ power_manager.post_init(
+ {
CONF_USE_POWER_FEATURE: True,
CONF_PRESET_POWER: 10,
CONF_DEVICE_POWER: 1234,
}
)
+ await power_manager.start_listening()
+
assert power_manager.is_configured is True
assert power_manager.overpowering_state == STATE_UNKNOWN
@@ -107,25 +117,18 @@ async def test_power_feature_manager(
assert custom_attributes["current_max_power"] is None
# 3. start listening
- power_manager.start_listening()
+ await power_manager.start_listening()
assert power_manager.is_configured is True
assert power_manager.overpowering_state == STATE_UNKNOWN
- assert len(power_manager._active_listener) == 2
+ assert len(power_manager._active_listener) == 0 # no more listening
# 4. test refresh and check_overpowering with the parametrized
- side_effects = SideEffects(
- {
- "sensor.the_power_sensor": State("sensor.the_power_sensor", power),
- "sensor.the_max_power_sensor": State(
- "sensor.the_max_power_sensor", max_power
- ),
- },
- State("unknown.entity_id", "unknown"),
- )
# fmt:off
- with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()) as mock_get_state:
+ with patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.current_max_power", new_callable=PropertyMock, return_value=max_power), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.current_power", new_callable=PropertyMock, return_value=power):
# fmt:on
+
# Finish the mock configuration
tpi_algo = PropAlgorithm(PROPORTIONAL_FUNCTION_TPI, 0.6, 0.01, 5, 0, "climate.vtherm")
tpi_algo._on_percent = 1 # pylint: disable="protected-access"
@@ -134,8 +137,84 @@ async def test_power_feature_manager(
type(fake_vtherm).is_over_climate = PropertyMock(return_value=is_over_climate)
type(fake_vtherm).proportional_algorithm = PropertyMock(return_value=tpi_algo)
type(fake_vtherm).nb_underlying_entities = PropertyMock(return_value=1)
- type(fake_vtherm).preset_mode = PropertyMock(return_value=PRESET_COMFORT if current_overpowering_state == STATE_OFF else PRESET_POWER)
- type(fake_vtherm)._saved_preset_mode = PropertyMock(return_value=PRESET_ECO)
+
+ ret = await power_manager.check_power_available()
+ assert ret == check_power_available
+
+
+@pytest.mark.parametrize(
+ "is_over_climate, current_overpowering_state, is_overpowering, new_overpowering_state, msg_sent",
+ [
+ # false -> false
+ (False, STATE_OFF, False, STATE_OFF, False),
+ # false -> true
+ (False, STATE_OFF, True, STATE_ON, True),
+ # true -> true
+ (False, STATE_ON, True, STATE_ON, False),
+ # true -> False
+ (False, STATE_ON, False, STATE_OFF, True),
+ # Same with over_climate
+ # false -> false
+ (True, STATE_OFF, False, STATE_OFF, False),
+ # false -> true
+ (True, STATE_OFF, True, STATE_ON, True),
+ # true -> true
+ (True, STATE_ON, True, STATE_ON, False),
+ # true -> False
+ (True, STATE_ON, False, STATE_OFF, True),
+ ],
+)
+async def test_power_feature_manager_set_overpowering(
+ hass,
+ is_over_climate,
+ current_overpowering_state,
+ is_overpowering,
+ new_overpowering_state,
+ msg_sent,
+):
+ """Test the set_overpowering method of FeaturePowerManager"""
+ fake_vtherm = MagicMock(spec=BaseThermostat)
+ type(fake_vtherm).name = PropertyMock(return_value="the name")
+
+ vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
+
+ # 1. creation / init
+ power_manager = FeaturePowerManager(fake_vtherm, hass)
+ vtherm_api.find_central_configuration = MagicMock()
+ vtherm_api.central_power_manager.post_init(
+ {
+ CONF_POWER_SENSOR: "sensor.the_power_sensor",
+ CONF_MAX_POWER_SENSOR: "sensor.the_max_power_sensor",
+ CONF_USE_POWER_FEATURE: True,
+ CONF_PRESET_POWER: 13,
+ }
+ )
+ assert vtherm_api.central_power_manager.is_configured
+
+ power_manager.post_init(
+ {
+ CONF_USE_POWER_FEATURE: True,
+ CONF_PRESET_POWER: 10,
+ CONF_DEVICE_POWER: 1234,
+ }
+ )
+
+ await power_manager.start_listening()
+
+ assert power_manager.is_configured is True
+ assert power_manager.overpowering_state == STATE_UNKNOWN
+
+ # check overpowering
+ power_manager._overpowering_state = current_overpowering_state
+
+ # fmt:off
+ with patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.current_max_power", new_callable=PropertyMock, return_value=2000), \
+ patch("custom_components.versatile_thermostat.central_feature_power_manager.CentralFeaturePowerManager.current_power", new_callable=PropertyMock, return_value=1000):
+ # fmt:on
+ # Finish mocking
+ fake_vtherm.is_over_climate = is_over_climate
+ fake_vtherm.preset_mode = MagicMock(return_value=PRESET_COMFORT if current_overpowering_state == STATE_OFF else PRESET_POWER)
+ fake_vtherm._saved_preset_mode = PRESET_ECO
fake_vtherm.save_hvac_mode = MagicMock()
fake_vtherm.restore_hvac_mode = AsyncMock()
@@ -147,26 +226,17 @@ async def test_power_feature_manager(
fake_vtherm.update_custom_attributes = MagicMock()
- ret = await power_manager.refresh_state()
- assert ret == changed
- assert power_manager.is_configured is True
- assert power_manager.overpowering_state == STATE_UNKNOWN
- assert power_manager.current_power == power
- assert power_manager.current_max_power == max_power
+ # Call set_overpowering
+ await power_manager.set_overpowering(is_overpowering, 1234)
- # check overpowering
- power_manager._overpowering_state = current_overpowering_state
- ret2 = await power_manager.check_overpowering()
- assert ret2 == check_overpowering_ret
- assert power_manager.overpowering_state == overpowering_state
- assert mock_get_state.call_count == 2
+ assert power_manager.overpowering_state == new_overpowering_state
- if power_manager.overpowering_state == STATE_OFF:
+ if not is_overpowering:
+ assert power_manager.overpowering_state == STATE_OFF
assert fake_vtherm.save_hvac_mode.call_count == 0
assert fake_vtherm.save_preset_mode.call_count == 0
assert fake_vtherm.async_underlying_entity_turn_off.call_count == 0
assert fake_vtherm.async_set_preset_mode_internal.call_count == 0
- assert fake_vtherm.send_event.call_count == nb_call
if current_overpowering_state == STATE_ON:
assert fake_vtherm.update_custom_attributes.call_count == 1
@@ -178,18 +248,24 @@ async def test_power_feature_manager(
else:
assert fake_vtherm.update_custom_attributes.call_count == 0
- if nb_call == 1:
+ if msg_sent:
fake_vtherm.send_event.assert_has_calls(
[
call.fake_vtherm.send_event(
EventType.POWER_EVENT,
- {'type': 'end', 'current_power': power, 'device_power': 1234, 'current_max_power': max_power}),
+ {
+ "type": "end",
+ "current_power": 1000,
+ "device_power": 1234,
+ "current_max_power": 2000,
+ },
+ ),
]
)
-
-
- elif power_manager.overpowering_state == STATE_ON:
- if is_over_climate:
+ # is_overpowering is True
+ else:
+ assert power_manager.overpowering_state == STATE_ON
+ if is_over_climate and current_overpowering_state == STATE_OFF:
assert fake_vtherm.save_hvac_mode.call_count == 1
else:
assert fake_vtherm.save_hvac_mode.call_count == 0
@@ -209,30 +285,37 @@ async def test_power_feature_manager(
assert fake_vtherm.restore_hvac_mode.call_count == 0
assert fake_vtherm.restore_preset_mode.call_count == 0
- if nb_call == 1:
+ if msg_sent:
fake_vtherm.send_event.assert_has_calls(
[
call.fake_vtherm.send_event(
EventType.POWER_EVENT,
- {'type': 'start', 'current_power': power, 'device_power': 1234, 'current_max_power': max_power, 'current_power_consumption': 1234.0}),
+ {
+ "type": "start",
+ "current_power": 1000,
+ "device_power": 1234,
+ "current_max_power": 2000,
+ "current_power_consumption": 1234.0,
+ },
+ ),
]
)
fake_vtherm.reset_mock()
- # 5. Check custom_attributes
+ # 5. Check custom_attributes
custom_attributes = {}
power_manager.add_custom_attributes(custom_attributes)
assert custom_attributes["power_sensor_entity_id"] == "sensor.the_power_sensor"
assert (
custom_attributes["max_power_sensor_entity_id"] == "sensor.the_max_power_sensor"
)
- assert custom_attributes["overpowering_state"] == overpowering_state
+ assert custom_attributes["overpowering_state"] == new_overpowering_state
assert custom_attributes["is_power_configured"] is True
assert custom_attributes["device_power"] == 1234
assert custom_attributes["power_temp"] == 10
- assert custom_attributes["current_power"] == power
- assert custom_attributes["current_max_power"] == max_power
+ assert custom_attributes["current_power"] == 1000
+ assert custom_attributes["current_max_power"] == 2000
power_manager.stop_listening()
await hass.async_block_till_done()
@@ -241,10 +324,15 @@ async def test_power_feature_manager(
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_power_management_hvac_off(
- hass: HomeAssistant, skip_hass_states_is_state
+ hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
):
"""Test the Power management"""
+ temps = {
+ "eco": 17,
+ "comfort": 18,
+ "boost": 19,
+ }
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
@@ -257,29 +345,24 @@ async def test_power_management_hvac_off(
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: False,
- CONF_HEATER: "switch.mock_switch",
+ CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: ThermostatOverSwitch = await create_thermostat(
- hass, entry, "climate.theoverswitchmockname"
+ hass, entry, "climate.theoverswitchmockname", temps
)
assert entity
@@ -292,34 +375,53 @@ async def test_power_management_hvac_off(
assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert entity.hvac_mode == HVACMode.OFF
+ now: datetime = NowClass.get_now(hass)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
# Send power mesurement
- await send_power_change_event(entity, 50, datetime.now())
- assert await entity.power_manager.check_overpowering() is False
+ # fmt:off
+ side_effects = SideEffects(
+ {
+ "sensor.the_power_sensor": State("sensor.the_power_sensor", 50),
+ "sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 300),
+ },
+ State("unknown.entity_id", "unknown"),
+ )
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
+ # fmt: on
+ await send_power_change_event(entity, 50, now)
+ assert entity.power_manager.is_overpowering_detected is False
- # All configuration is not complete
- assert entity.preset_mode is PRESET_BOOST
- assert entity.power_manager.overpowering_state is STATE_UNKNOWN
+ # All configuration is not complete
+ assert entity.preset_mode is PRESET_BOOST
+ assert entity.power_manager.overpowering_state is STATE_UNKNOWN # due to hvac_off
- # Send power max mesurement
- await send_max_power_change_event(entity, 300, datetime.now())
- assert await entity.power_manager.check_overpowering() is False
- # All configuration is complete and power is < power_max
- assert entity.preset_mode is PRESET_BOOST
- assert entity.power_manager.overpowering_state is STATE_OFF
+ # Send power max mesurement
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+ await send_max_power_change_event(entity, 300, now)
+ assert entity.power_manager.is_overpowering_detected is False
+ # All configuration is complete and power is < power_max
+ assert entity.preset_mode is PRESET_BOOST
+ assert entity.power_manager.overpowering_state is STATE_UNKNOWN # # due to hvac_off
# Send power max mesurement too low but HVACMode is off
- with patch(
- "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
- ) as mock_send_event, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
- ) as mock_heater_on, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
- ) as mock_heater_off:
+ side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 149))
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on") as mock_heater_on, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off") as mock_heater_off:
+ # fmt: on
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
await send_max_power_change_event(entity, 149, datetime.now())
- assert await entity.power_manager.check_overpowering() is True
+ assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is > power_max but we stay in Boost cause thermostat if Off
assert entity.preset_mode is PRESET_BOOST
- assert entity.power_manager.overpowering_state is STATE_ON
+ assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert mock_send_event.call_count == 0
assert mock_heater_on.call_count == 0
@@ -328,9 +430,17 @@ async def test_power_management_hvac_off(
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
-async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is_state):
+async def test_power_management_hvac_on(
+ hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
+):
"""Test the Power management"""
+ temps = {
+ "eco": 17,
+ "comfort": 18,
+ "boost": 19,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
@@ -343,32 +453,30 @@ async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: False,
- CONF_HEATER: "switch.mock_switch",
+ CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: ThermostatOverSwitch = await create_thermostat(
- hass, entry, "climate.theoverswitchmockname"
+ hass, entry, "climate.theoverswitchmockname", temps
)
assert entity
+ now: datetime = NowClass.get_now(hass)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
tpi_algo = entity._prop_algorithm
assert tpi_algo
@@ -379,25 +487,49 @@ async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is
assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert entity.target_temperature == 19
+ # make the heater heats
+ await send_temperature_change_event(entity, 15, now)
+ await send_ext_temperature_change_event(entity, 1, now)
+ await hass.async_block_till_done()
+
+ assert entity.power_percent > 0
+
# Send power mesurement
- await send_power_change_event(entity, 50, datetime.now())
- # Send power max mesurement
- await send_max_power_change_event(entity, 300, datetime.now())
- assert await entity.power_manager.check_overpowering() is False
- # All configuration is complete and power is < power_max
- assert entity.preset_mode is PRESET_BOOST
- assert entity.power_manager.overpowering_state is STATE_OFF
+ side_effects = SideEffects(
+ {
+ "sensor.the_power_sensor": State("sensor.the_power_sensor", 50),
+ "sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 300),
+ },
+ State("unknown.entity_id", "unknown"),
+ )
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
+ # fmt: on
+ await send_power_change_event(entity, 50, datetime.now())
+ # Send power max mesurement
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+ await send_max_power_change_event(entity, 300, datetime.now())
+
+ assert entity.power_manager.is_overpowering_detected is False
+ # All configuration is complete and power is < power_max
+ assert entity.preset_mode is PRESET_BOOST
+ assert entity.power_manager.overpowering_state is STATE_OFF
# Send power max mesurement too low and HVACMode is on
- with patch(
- "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
- ) as mock_send_event, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
- ) as mock_heater_on, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
- ) as mock_heater_off:
- await send_max_power_change_event(entity, 149, datetime.now())
- assert await entity.power_manager.check_overpowering() is True
+ side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 49))
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on") as mock_heater_on, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off") as mock_heater_off, \
+ patch("custom_components.versatile_thermostat.thermostat_switch.ThermostatOverSwitch.is_device_active", return_value="True"):
+ # fmt: on
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
+ await send_max_power_change_event(entity, 49, now)
+ assert entity.power_manager.is_overpowering_detected is True
# All configuration is complete and power is > power_max we switch to POWER preset
assert entity.preset_mode is PRESET_POWER
assert entity.power_manager.overpowering_state is STATE_ON
@@ -413,7 +545,7 @@ async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is
"type": "start",
"current_power": 50,
"device_power": 100,
- "current_max_power": 149,
+ "current_max_power": 49,
"current_power_consumption": 100.0,
},
),
@@ -423,16 +555,20 @@ async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is
assert mock_heater_on.call_count == 0
assert mock_heater_off.call_count == 1
- # Send power mesurement low to unseet power preset
- with patch(
- "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
- ) as mock_send_event, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
- ) as mock_heater_on, patch(
- "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
- ) as mock_heater_off:
- await send_power_change_event(entity, 48, datetime.now())
- assert await entity.power_manager.check_overpowering() is False
+ # Send power mesurement low to unset power preset
+ side_effects.add_or_update_side_effect("sensor.the_power_sensor", State("sensor.the_power_sensor", 48))
+ side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 149))
+ # fmt:off
+ with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
+ patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on") as mock_heater_on, \
+ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off") as mock_heater_off:
+ # fmt: on
+ now = now + timedelta(seconds=30)
+ VersatileThermostatAPI.get_vtherm_api()._set_now(now)
+
+ await send_power_change_event(entity, 48, now)
+ assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is < power_max, we restore previous preset
assert entity.preset_mode is PRESET_BOOST
assert entity.power_manager.overpowering_state is STATE_OFF
@@ -462,10 +598,16 @@ async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_power_management_energy_over_switch(
- hass: HomeAssistant, skip_hass_states_is_state
+ hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
):
"""Test the Power management energy mesurement"""
+ temps = {
+ "eco": 17,
+ "comfort": 18,
+ "boost": 19,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
@@ -478,30 +620,24 @@ async def test_power_management_energy_over_switch(
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: False,
- CONF_HEATER: "switch.mock_switch",
- CONF_HEATER_2: "switch.mock_switch2",
+ CONF_UNDERLYING_LIST: ["switch.mock_switch", "switch.mock_switch2"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: ThermostatOverSwitch = await create_thermostat(
- hass, entry, "climate.theoverswitchmockname"
+ hass, entry, "climate.theoverswitchmockname", temps
)
assert entity
@@ -523,6 +659,8 @@ async def test_power_management_energy_over_switch(
await entity.async_set_preset_mode(PRESET_BOOST)
await send_temperature_change_event(entity, 15, datetime.now())
+ await hass.async_block_till_done()
+
assert entity.hvac_mode is HVACMode.HEAT
assert entity.preset_mode is PRESET_BOOST
assert entity.target_temperature == 19
@@ -594,6 +732,12 @@ async def test_power_management_energy_over_climate(
):
"""Test the Power management for a over_climate thermostat"""
+ temps = {
+ "eco": 17,
+ "comfort": 18,
+ "boost": 19,
+ }
+
the_mock_underlying = MagicMockClimate()
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
@@ -611,26 +755,21 @@ async def test_power_management_energy_over_climate(
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
- "eco_temp": 17,
- "comfort_temp": 18,
- "boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: False,
- CONF_CLIMATE: "climate.mock_climate",
+ CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: ThermostatOverSwitch = await create_thermostat(
- hass, entry, "climate.theoverclimatemockname"
+ hass, entry, "climate.theoverclimatemockname", temps
)
assert entity
assert entity.is_over_climate
diff --git a/tests/test_presence.py b/tests/test_presence.py
index c376c0d..d5ed362 100644
--- a/tests/test_presence.py
+++ b/tests/test_presence.py
@@ -75,7 +75,7 @@ async def test_presence_feature_manager(
assert custom_attributes["is_presence_configured"] is True
# 3. start listening
- presence_manager.start_listening()
+ await presence_manager.start_listening()
assert presence_manager.is_configured is True
assert presence_manager.presence_state == STATE_UNKNOWN
assert presence_manager.is_absence_detected is False
diff --git a/tests/test_sensors.py b/tests/test_sensors.py
index e9a482f..344f205 100644
--- a/tests/test_sensors.py
+++ b/tests/test_sensors.py
@@ -224,8 +224,6 @@ async def test_sensors_over_climate(
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
- CONF_POWER_SENSOR: "sensor.mock_power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 1.5,
CONF_PRESET_POWER: 12,
},
diff --git a/tests/test_switch_ac.py b/tests/test_switch_ac.py
index 55f17ce..a6f321a 100644
--- a/tests/test_switch_ac.py
+++ b/tests/test_switch_ac.py
@@ -26,6 +26,23 @@ async def test_over_switch_ac_full_start(
): # pylint: disable=unused-argument
"""Test the normal full start of a thermostat in thermostat_over_switch type"""
+ temps = {
+ PRESET_FROST_PROTECTION: 7,
+ PRESET_ECO: 17,
+ PRESET_COMFORT: 19,
+ PRESET_BOOST: 20,
+ PRESET_ECO + PRESET_AC_SUFFIX: 25,
+ PRESET_COMFORT + PRESET_AC_SUFFIX: 23,
+ PRESET_BOOST + PRESET_AC_SUFFIX: 21,
+ PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX: 7,
+ PRESET_ECO + PRESET_AWAY_SUFFIX: 16,
+ PRESET_COMFORT + PRESET_AWAY_SUFFIX: 17,
+ PRESET_BOOST + PRESET_AWAY_SUFFIX: 18,
+ PRESET_ECO + PRESET_AC_SUFFIX + PRESET_AWAY_SUFFIX: 27,
+ PRESET_COMFORT + PRESET_AC_SUFFIX + PRESET_AWAY_SUFFIX: 26,
+ PRESET_BOOST + PRESET_AC_SUFFIX + PRESET_AWAY_SUFFIX: 25,
+ }
+
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchACMockName",
@@ -57,21 +74,7 @@ async def test_over_switch_ac_full_start(
assert isinstance(entity, ThermostatOverSwitch)
# Initialise the preset temp
- await set_climate_preset_temp(
- entity, PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX, 7
- )
- await set_climate_preset_temp(entity, PRESET_ECO + PRESET_AWAY_SUFFIX, 16)
- await set_climate_preset_temp(entity, PRESET_COMFORT + PRESET_AWAY_SUFFIX, 17)
- await set_climate_preset_temp(entity, PRESET_BOOST + PRESET_AWAY_SUFFIX, 18)
- await set_climate_preset_temp(
- entity, PRESET_ECO + PRESET_AC_SUFFIX + PRESET_AWAY_SUFFIX, 27
- )
- await set_climate_preset_temp(
- entity, PRESET_COMFORT + PRESET_AC_SUFFIX + PRESET_AWAY_SUFFIX, 26
- )
- await set_climate_preset_temp(
- entity, PRESET_BOOST + PRESET_AC_SUFFIX + PRESET_AWAY_SUFFIX, 25
- )
+ await set_all_climate_preset_temp(hass, entity, temps, "theoverswitchmockname")
assert entity.name == "TheOverSwitchMockName"
assert entity.is_over_climate is False # pylint: disable=protected-access
diff --git a/tests/test_valve.py b/tests/test_valve.py
index 0042d0b..d754e18 100644
--- a/tests/test_valve.py
+++ b/tests/test_valve.py
@@ -51,8 +51,6 @@ async def test_over_valve_full_start(
CONF_MOTION_OFF_DELAY: 30,
CONF_MOTION_PRESET: PRESET_COMFORT,
CONF_NO_MOTION_PRESET: PRESET_ECO,
- CONF_POWER_SENSOR: "sensor.power_sensor",
- CONF_MAX_POWER_SENSOR: "sensor.power_max_sensor",
CONF_PRESENCE_SENSOR: "person.presence_sensor",
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: 7,
PRESET_ECO + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: 17.1,
diff --git a/tests/test_window_feature_manager.py b/tests/test_window_feature_manager.py
index e6f26ff..62f3110 100644
--- a/tests/test_window_feature_manager.py
+++ b/tests/test_window_feature_manager.py
@@ -170,7 +170,7 @@ async def test_window_feature_manager_refresh_sensor_action_turn_off(
)
# 3. start listening
- window_manager.start_listening()
+ await window_manager.start_listening()
assert window_manager.is_configured is True
assert window_manager.window_state == STATE_UNKNOWN
assert window_manager.window_auto_state == STATE_UNAVAILABLE
@@ -288,7 +288,7 @@ async def test_window_feature_manager_refresh_sensor_action_frost_only(
)
# 3. start listening
- window_manager.start_listening()
+ await window_manager.start_listening()
assert window_manager.is_configured is True
assert window_manager.window_state == STATE_UNKNOWN
assert window_manager.window_auto_state == STATE_UNAVAILABLE
@@ -408,7 +408,7 @@ async def test_window_feature_manager_sensor_event_action_turn_off(
)
# 3. start listening
- window_manager.start_listening()
+ await window_manager.start_listening()
assert len(window_manager._active_listener) == 1
# 4. test refresh with the parametrized
@@ -535,7 +535,7 @@ async def test_window_feature_manager_event_sensor_action_frost_only(
)
# 3. start listening
- window_manager.start_listening()
+ await window_manager.start_listening()
# 4. test refresh with the parametrized
# fmt:off
@@ -660,7 +660,7 @@ async def test_window_feature_manager_window_auto(
}
)
assert window_manager.is_window_auto_configured is True
- window_manager.start_listening()
+ await window_manager.start_listening()
# 2. Call manage window auto
tz = get_tz(hass) # pylint: disable=invalid-name