From bb4d3a59a91c52d86c6d52358446470d397afcd7 Mon Sep 17 00:00:00 2001 From: Tomasz Madycki Date: Wed, 12 Feb 2025 14:41:13 +0100 Subject: [PATCH] Add minimal_deactivation_delay (#905) * Add minimal_deactivation_delay In some cases users may want to keep the equipment on if it will deactivate for a short time. This could help reduce wear on gas boilers. * fix(prop_algorithm): correct the log message for minimal_deactivation_delay * fix(translations): correct the translations for minimal_deactivation_delay --- .../versatile_thermostat/base_thermostat.py | 3 + .../versatile_thermostat/config_flow.py | 6 + .../versatile_thermostat/config_schema.py | 1 + .../versatile_thermostat/const.py | 2 + .../versatile_thermostat/prop_algorithm.py | 21 +++- .../versatile_thermostat/strings.json | 4 + .../thermostat_climate_valve.py | 1 + .../versatile_thermostat/thermostat_switch.py | 1 + .../versatile_thermostat/thermostat_valve.py | 1 + .../versatile_thermostat/translations/en.json | 2 + .../versatile_thermostat/translations/fr.json | 4 + documentation/en/feature-advanced.md | 4 + documentation/en/reference.md | 2 + tests/commons.py | 2 + tests/const.py | 1 + tests/test_auto_fan_mode.py | 3 + tests/test_auto_start_stop.py | 8 ++ tests/test_binary_sensors.py | 7 ++ tests/test_bugs.py | 4 + tests/test_central_boiler.py | 6 + tests/test_central_config.py | 5 + tests/test_central_mode.py | 9 ++ tests/test_central_power_manager.py | 2 + tests/test_config_flow.py | 8 ++ tests/test_inverted_switch.py | 1 + tests/test_last_seen.py | 1 + tests/test_motion.py | 5 + tests/test_multiple_switch.py | 6 + tests/test_overclimate.py | 5 + tests/test_power.py | 7 +- tests/test_safety.py | 3 + tests/test_sensors.py | 3 + tests/test_start.py | 1 + tests/test_switch_keep_alive.py | 1 + tests/test_temp_number.py | 4 + tests/test_tpi.py | 112 ++++++++++++++++++ tests/test_valve.py | 3 + tests/test_window.py | 14 +++ 38 files changed, 270 insertions(+), 3 deletions(-) diff --git a/custom_components/versatile_thermostat/base_thermostat.py b/custom_components/versatile_thermostat/base_thermostat.py index 5bb3d25..9f69759 100644 --- a/custom_components/versatile_thermostat/base_thermostat.py +++ b/custom_components/versatile_thermostat/base_thermostat.py @@ -104,6 +104,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): "last_temperature_datetime", "last_ext_temperature_datetime", "minimal_activation_delay_sec", + "minimal_deactivation_delay_sec", "last_update_datetime", "timezone", "temperature_unit", @@ -370,6 +371,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): self._tpi_coef_ext = 0 self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY) + self._minimal_deactivation_delay = entry_infos.get(CONF_MINIMAL_DEACTIVATION_DELAY) self._last_temperature_measure = self.now self._last_ext_temperature_measure = self.now @@ -1684,6 +1686,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): self._current_tz ).isoformat(), "minimal_activation_delay_sec": self._minimal_activation_delay, + "minimal_deactivation_delay_sec": self._minimal_deactivation_delay, ATTR_TOTAL_ENERGY: self.total_energy, "last_update_datetime": self.now.isoformat(), "timezone": str(self._current_tz), diff --git a/custom_components/versatile_thermostat/config_flow.py b/custom_components/versatile_thermostat/config_flow.py index 40b5153..65ba48e 100644 --- a/custom_components/versatile_thermostat/config_flow.py +++ b/custom_components/versatile_thermostat/config_flow.py @@ -371,6 +371,12 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): ): return False + if ( + infos.get(CONF_USE_ADVANCED_CENTRAL_CONFIG, False) is False + and infos.get(CONF_MINIMAL_DEACTIVATION_DELAY, -1) == -1 + ): + return False + if ( infos.get(CONF_PROP_FUNCTION, None) == PROPORTIONAL_FUNCTION_TPI and infos.get(CONF_USE_TPI_CENTRAL_CONFIG, False) is False diff --git a/custom_components/versatile_thermostat/config_schema.py b/custom_components/versatile_thermostat/config_schema.py index 8f12b98..51b6fe1 100644 --- a/custom_components/versatile_thermostat/config_schema.py +++ b/custom_components/versatile_thermostat/config_schema.py @@ -374,6 +374,7 @@ STEP_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name STEP_CENTRAL_ADVANCED_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name { vol.Required(CONF_MINIMAL_ACTIVATION_DELAY, default=10): cv.positive_int, + vol.Required(CONF_MINIMAL_DEACTIVATION_DELAY, default=0): cv.positive_int, vol.Required(CONF_SAFETY_DELAY_MIN, default=60): cv.positive_int, vol.Required( CONF_SAFETY_MIN_ON_PERCENT, diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py index 501f4d3..b5eb122 100644 --- a/custom_components/versatile_thermostat/const.py +++ b/custom_components/versatile_thermostat/const.py @@ -83,6 +83,7 @@ CONF_TPI_COEF_EXT = "tpi_coef_ext" CONF_PRESENCE_SENSOR = "presence_sensor_entity_id" CONF_PRESET_POWER = "power_temp" CONF_MINIMAL_ACTIVATION_DELAY = "minimal_activation_delay" +CONF_MINIMAL_DEACTIVATION_DELAY = "minimal_deactivation_delay" CONF_TEMP_MIN = "temp_min" CONF_TEMP_MAX = "temp_max" CONF_SAFETY_DELAY_MIN = "safety_delay_min" @@ -289,6 +290,7 @@ ALL_CONF = ( CONF_TPI_COEF_EXT, CONF_PRESENCE_SENSOR, CONF_MINIMAL_ACTIVATION_DELAY, + CONF_MINIMAL_DEACTIVATION_DELAY, CONF_TEMP_MIN, CONF_TEMP_MAX, CONF_SAFETY_DELAY_MIN, diff --git a/custom_components/versatile_thermostat/prop_algorithm.py b/custom_components/versatile_thermostat/prop_algorithm.py index f37747f..027e86a 100644 --- a/custom_components/versatile_thermostat/prop_algorithm.py +++ b/custom_components/versatile_thermostat/prop_algorithm.py @@ -30,18 +30,20 @@ class PropAlgorithm: tpi_coef_ext, cycle_min: int, minimal_activation_delay: int, + minimal_deactivation_delay: int, vtherm_entity_id: str = None, max_on_percent: float = None, ) -> None: """Initialisation of the Proportional Algorithm""" _LOGGER.debug( - "%s - Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d, minimal_activation_delay:%d", # pylint: disable=line-too-long + "%s - Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d, minimal_activation_delay:%d, minimal_deactivation_delay:%d", # pylint: disable=line-too-long vtherm_entity_id, function_type, tpi_coef_int, tpi_coef_ext, cycle_min, minimal_activation_delay, + minimal_deactivation_delay, ) # Issue 506 - check parameters @@ -51,10 +53,11 @@ class PropAlgorithm: or not is_number(tpi_coef_ext) or not is_number(cycle_min) or not is_number(minimal_activation_delay) + or not is_number(minimal_deactivation_delay) or function_type != PROPORTIONAL_FUNCTION_TPI ): _LOGGER.error( - "%s - configuration is wrong. function_type=%s, entity_id is %s, tpi_coef_int is %s, tpi_coef_ext is %s, cycle_min is %s, minimal_activation_delay is %s", + "%s - configuration is wrong. function_type=%s, entity_id is %s, tpi_coef_int is %s, tpi_coef_ext is %s, cycle_min is %s, minimal_activation_delay is %s, minimal_deactivation_delay is %s", vtherm_entity_id, function_type, vtherm_entity_id, @@ -62,6 +65,7 @@ class PropAlgorithm: tpi_coef_ext, cycle_min, minimal_activation_delay, + minimal_deactivation_delay, ) raise TypeError( "TPI parameters are not set correctly. VTherm will not work as expected. Please reconfigure it correctly. See previous log for values" @@ -73,6 +77,7 @@ class PropAlgorithm: self._tpi_coef_ext = tpi_coef_ext self._cycle_min = cycle_min self._minimal_activation_delay = minimal_activation_delay + self._minimal_deactivation_delay = minimal_deactivation_delay self._on_percent = 0 self._calculated_on_percent = 0 self._on_time_sec = 0 @@ -187,6 +192,18 @@ class PropAlgorithm: self._off_time_sec = self._cycle_min * 60 - self._on_time_sec + # Do not stop heating when off time less than xx sec + if self._off_time_sec < self._minimal_deactivation_delay: + if self._off_time_sec > 0: + _LOGGER.info( + "%s - Force 100% heating cycle since the off duration (%f) is shorter than minimal_deactivation_delay (%f)", + self._vtherm_entity_id, + self._off_time_sec, + self._minimal_deactivation_delay, + ) + self._on_time_sec = self._cycle_min * 60 + self._off_time_sec = 0 + def set_safety(self, default_on_percent: float): """Set a default value for on_percent (used for safety mode)""" _LOGGER.info( diff --git a/custom_components/versatile_thermostat/strings.json b/custom_components/versatile_thermostat/strings.json index 989d76c..0e1a178 100644 --- a/custom_components/versatile_thermostat/strings.json +++ b/custom_components/versatile_thermostat/strings.json @@ -199,6 +199,7 @@ "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-advanced.md)", "data": { "minimal_activation_delay": "Minimum activation delay", + "minimal_deactivation_delay": "Minimum deactivation delay", "safety_delay_min": "Safety delay (in minutes)", "safety_min_on_percent": "Minimum power percent to enable safety mode", "safety_default_on_percent": "Power percent to use in safety mode", @@ -206,6 +207,7 @@ }, "data_description": { "minimal_activation_delay": "Delay in seconds under which the equipment will not be activated", + "minimal_deactivation_delay": "Delay in seconds under which the equipment will be kept active", "safety_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a safety off state", "safety_min_on_percent": "Minimum heating percent value for safety preset activation. Below this amount of power percent the thermostat won't go into safety preset", "safety_default_on_percent": "The default heating power percent value in safety preset. Set to 0 to switch off heater in safety preset", @@ -451,6 +453,7 @@ "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-advanced.md)", "data": { "minimal_activation_delay": "Minimum activation delay", + "minimal_deactivation_delay": "Minimum deactivation delay", "safety_delay_min": "Safety delay (in minutes)", "safety_min_on_percent": "Minimum power percent to enable safety mode", "safety_default_on_percent": "Power percent to use in safety mode", @@ -458,6 +461,7 @@ }, "data_description": { "minimal_activation_delay": "Delay in seconds under which the equipment will not be activated", + "minimal_deactivation_delay": "Delay in seconds under which the equipment will be kept active", "safety_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a safety off state", "safety_min_on_percent": "Minimum heating percent value for safety preset activation. Below this amount of power percent the thermostat won't go into safety preset", "safety_default_on_percent": "The default heating power percent value in safety preset. Set to 0 to switch off heater in safety preset", diff --git a/custom_components/versatile_thermostat/thermostat_climate_valve.py b/custom_components/versatile_thermostat/thermostat_climate_valve.py index 6e219cc..5b5d9c7 100644 --- a/custom_components/versatile_thermostat/thermostat_climate_valve.py +++ b/custom_components/versatile_thermostat/thermostat_climate_valve.py @@ -82,6 +82,7 @@ class ThermostatOverClimateValve(ThermostatOverClimate): self._tpi_coef_ext, self._cycle_min, self._minimal_activation_delay, + self._minimal_deactivation_delay, self.name, ) diff --git a/custom_components/versatile_thermostat/thermostat_switch.py b/custom_components/versatile_thermostat/thermostat_switch.py index 1c819b4..d32506e 100644 --- a/custom_components/versatile_thermostat/thermostat_switch.py +++ b/custom_components/versatile_thermostat/thermostat_switch.py @@ -77,6 +77,7 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): self._tpi_coef_ext, self._cycle_min, self._minimal_activation_delay, + self._minimal_deactivation_delay, self.name, max_on_percent=self._max_on_percent, ) diff --git a/custom_components/versatile_thermostat/thermostat_valve.py b/custom_components/versatile_thermostat/thermostat_valve.py index 207a2da..cf4a906 100644 --- a/custom_components/versatile_thermostat/thermostat_valve.py +++ b/custom_components/versatile_thermostat/thermostat_valve.py @@ -96,6 +96,7 @@ class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=a self._tpi_coef_ext, self._cycle_min, self._minimal_activation_delay, + self._minimal_deactivation_delay, self.name, max_on_percent=self._max_on_percent, ) diff --git a/custom_components/versatile_thermostat/translations/en.json b/custom_components/versatile_thermostat/translations/en.json index 989d76c..3b91a00 100644 --- a/custom_components/versatile_thermostat/translations/en.json +++ b/custom_components/versatile_thermostat/translations/en.json @@ -199,6 +199,7 @@ "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-advanced.md)", "data": { "minimal_activation_delay": "Minimum activation delay", + "minimal_deactivation_delay": "Minimal deactivation delay", "safety_delay_min": "Safety delay (in minutes)", "safety_min_on_percent": "Minimum power percent to enable safety mode", "safety_default_on_percent": "Power percent to use in safety mode", @@ -206,6 +207,7 @@ }, "data_description": { "minimal_activation_delay": "Delay in seconds under which the equipment will not be activated", + "minimal_deactivation_delay": "Delay in seconds under which the equipment will be kept active", "safety_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a safety off state", "safety_min_on_percent": "Minimum heating percent value for safety preset activation. Below this amount of power percent the thermostat won't go into safety preset", "safety_default_on_percent": "The default heating power percent value in safety preset. Set to 0 to switch off heater in safety preset", diff --git a/custom_components/versatile_thermostat/translations/fr.json b/custom_components/versatile_thermostat/translations/fr.json index 9b5fa85..c16776f 100644 --- a/custom_components/versatile_thermostat/translations/fr.json +++ b/custom_components/versatile_thermostat/translations/fr.json @@ -200,6 +200,7 @@ "description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-advanced.md)", "data": { "minimal_activation_delay": "Délai minimal d'activation", + "minimal_deactivation_delay": "Minimal deactivation delay", "safety_delay_min": "Délai maximal entre 2 mesures de températures", "safety_min_on_percent": "Pourcentage minimal de puissance", "safety_default_on_percent": "Pourcentage de puissance a utiliser en mode securité", @@ -207,6 +208,7 @@ }, "data_description": { "minimal_activation_delay": "Délai en secondes en-dessous duquel l'équipement ne sera pas activé", + "minimal_deactivation_delay": "Delay in seconds under which the equipment will be kept active", "safety_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position de sécurité", "safety_min_on_percent": "Seuil minimal de pourcentage de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé", "safety_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité", @@ -448,6 +450,7 @@ "description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-advanced.md)", "data": { "minimal_activation_delay": "Délai minimal d'activation", + "minimal_deactivation_delay": "Minimal deactivation delay", "safety_delay_min": "Délai maximal entre 2 mesures de températures", "safety_min_on_percent": "Pourcentage minimal de puissance", "safety_default_on_percent": "Pourcentage de puissance a utiliser en mode securité", @@ -455,6 +458,7 @@ }, "data_description": { "minimal_activation_delay": "Délai en seondes en-dessous duquel l'équipement ne sera pas activé", + "minimal_deactivation_delay": "Delay in seconds under which the equipment will be kept active", "safety_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position de sécurité", "safety_min_on_percent": "Seuil minimal de pourcentage de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé", "safety_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité", diff --git a/documentation/en/feature-advanced.md b/documentation/en/feature-advanced.md index 9bb0371..381713f 100644 --- a/documentation/en/feature-advanced.md +++ b/documentation/en/feature-advanced.md @@ -25,6 +25,10 @@ The advanced configuration form looks like this: The first delay (`minimal_activation_delay_sec`) in seconds is the minimum acceptable delay to turn on the heating. If the calculated activation time is shorter than this value, the heating remains off. This parameter only applies to _VTherm_ with cyclic triggering `over_switch`. If the activation time is too short, rapid switching will not allow the device to heat up properly. +### Minimum Deactivation Delay + +The delay (`minimal_deactivation_delay_sec`) in seconds is the minimum acceptable delay to turn off the heating. If the calculated deactivation time is shorter than this value, the heating remains on. + ### Safety Mode The second delay (`safety_delay_min`) is the maximum time between two temperature measurements before the _VTherm_ switches to Safety Mode. diff --git a/documentation/en/reference.md b/documentation/en/reference.md index 71fbd50..b879be3 100644 --- a/documentation/en/reference.md +++ b/documentation/en/reference.md @@ -61,6 +61,7 @@ | ``power_temp`` | Temperature during load shedding | X | X | X | X | | ``presence_sensor_entity_id`` | Presence sensor entity id (true if someone is present) | X | X | X | - | | ``minimal_activation_delay`` | Minimum activation delay | X | - | - | X | +| ``minimal_deactivation_delay`` | Minimum deactivation delay | X | - | - | X | | ``safety_delay_min`` | Maximum delay between two temperature measurements | X | - | X | X | | ``safety_min_on_percent`` | Minimum power percentage to enter security mode | X | - | X | X | | ``auto_regulation_mode`` | Auto-regulation mode | - | X | - | - | @@ -259,6 +260,7 @@ The custom attributes are as follows: | ``last_ext_temperature_datetime`` | The date and time in ISO8866 format of the last external temperature reception | | ``security_state`` | The security state. True or false | | ``minimal_activation_delay_sec`` | The minimal activation delay in seconds | +| ``minimal_deactivation_delay_sec``| The minimal deactivation delay in seconds | | ``last_update_datetime`` | The date and time in ISO8866 format of this state | | ``friendly_name`` | The name of the thermostat | | ``supported_features`` | A combination of all features supported by this thermostat. See the official climate integration documentation for more information | diff --git a/tests/commons.py b/tests/commons.py index 757080c..e4c7996 100644 --- a/tests/commons.py +++ b/tests/commons.py @@ -181,6 +181,7 @@ FULL_CENTRAL_CONFIG = { CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, @@ -222,6 +223,7 @@ FULL_CENTRAL_CONFIG_WITH_BOILER = { CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, diff --git a/tests/const.py b/tests/const.py index 56a10dc..d2d0563 100644 --- a/tests/const.py +++ b/tests/const.py @@ -185,6 +185,7 @@ MOCK_PRESENCE_AC_CONFIG = { MOCK_ADVANCED_CONFIG = { CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, diff --git a/tests/test_auto_fan_mode.py b/tests/test_auto_fan_mode.py index a7271b5..9d9a2c7 100644 --- a/tests/test_auto_fan_mode.py +++ b/tests/test_auto_fan_mode.py @@ -53,6 +53,7 @@ async def test_over_climate_auto_fan_mode_turbo( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -119,6 +120,7 @@ async def test_over_climate_auto_fan_mode_not_turbo( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -189,6 +191,7 @@ async def test_over_climate_auto_fan_mode_turbo_activation( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, diff --git a/tests/test_auto_start_stop.py b/tests/test_auto_start_stop.py index 62ede37..fd2539c 100644 --- a/tests/test_auto_start_stop.py +++ b/tests/test_auto_start_stop.py @@ -335,6 +335,7 @@ async def test_auto_start_stop_none_vtherm( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -426,6 +427,7 @@ async def test_auto_start_stop_medium_heat_vtherm( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -691,6 +693,7 @@ async def test_auto_start_stop_fast_ac_vtherm( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_UNDERLYING_LIST: ["climate.mock_climate"], CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -932,6 +935,7 @@ async def test_auto_start_stop_medium_heat_vtherm_preset_change( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -1145,6 +1149,7 @@ async def test_auto_start_stop_medium_heat_vtherm_preset_change_enable_false( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -1284,6 +1289,7 @@ async def test_auto_start_stop_fast_heat_window( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -1462,6 +1468,7 @@ async def test_auto_start_stop_fast_heat_window_mixed( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -1644,6 +1651,7 @@ async def test_auto_start_stop_disable_vtherm_off( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, diff --git a/tests/test_binary_sensors.py b/tests/test_binary_sensors.py index da3807e..77f316b 100644 --- a/tests/test_binary_sensors.py +++ b/tests/test_binary_sensors.py @@ -57,6 +57,7 @@ async def test_security_binary_sensors( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 30, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, @@ -138,6 +139,7 @@ async def test_overpowering_binary_sensors( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, @@ -240,6 +242,7 @@ async def test_window_binary_sensors( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", @@ -328,6 +331,7 @@ async def test_motion_binary_sensors( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", @@ -421,6 +425,7 @@ async def test_presence_binary_sensors( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor", @@ -504,6 +509,7 @@ async def test_binary_sensors_over_climate_minimal( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, @@ -539,3 +545,4 @@ async def test_binary_sensors_over_climate_minimal( hass, "binary_sensor.theoverclimatemockname_presence_state", "binary_sensor" ) assert presence_binary_sensor is None + diff --git a/tests/test_bugs.py b/tests/test_bugs.py index 68afe9d..56e937a 100644 --- a/tests/test_bugs.py +++ b/tests/test_bugs.py @@ -63,6 +63,7 @@ async def test_bug_63( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.0, # !! here CONF_SAFETY_DEFAULT_ON_PERCENT: 0.0, # !! here @@ -115,6 +116,7 @@ async def test_bug_64( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, # !! here @@ -304,6 +306,7 @@ async def test_bug_407( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, @@ -545,6 +548,7 @@ async def test_bug_465(hass: HomeAssistant, skip_hass_states_is_state): CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, diff --git a/tests/test_central_boiler.py b/tests/test_central_boiler.py index 4ccb0a5..477adf2 100644 --- a/tests/test_central_boiler.py +++ b/tests/test_central_boiler.py @@ -111,6 +111,7 @@ async def test_update_central_boiler_state_simple( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -298,6 +299,7 @@ async def test_update_central_boiler_state_multiple( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -626,6 +628,7 @@ async def test_update_central_boiler_state_simple_valve( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -800,6 +803,7 @@ async def test_update_central_boiler_state_simple_climate( CONF_USE_PRESENCE_FEATURE: False, CONF_UNDERLYING_LIST: [climate1.entity_id], CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -990,6 +994,7 @@ async def test_update_central_boiler_state_simple_climate_valve_regulation( CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_HIGH, CONF_AUTO_REGULATION_USE_DEVICE_TEMP: False, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -1255,6 +1260,7 @@ async def test_bug_339( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: climate1.entity_id, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, diff --git a/tests/test_central_config.py b/tests/test_central_config.py index e805dbe..357ed39 100644 --- a/tests/test_central_config.py +++ b/tests/test_central_config.py @@ -67,6 +67,7 @@ async def test_add_a_central_config(hass: HomeAssistant, skip_hass_states_is_sta CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, @@ -135,6 +136,7 @@ async def test_minimal_over_switch_wo_central_config( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -224,6 +226,7 @@ async def test_full_over_switch_wo_central_config( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -349,6 +352,7 @@ async def test_full_over_switch_with_central_config( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -511,6 +515,7 @@ async def test_migration_of_central_config( CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_EXT: 0.02, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, diff --git a/tests/test_central_mode.py b/tests/test_central_mode.py index 742aaef..f89c385 100644 --- a/tests/test_central_mode.py +++ b/tests/test_central_mode.py @@ -56,6 +56,7 @@ async def test_config_with_central_mode_true( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -103,6 +104,7 @@ async def test_config_with_central_mode_false( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -153,6 +155,7 @@ async def test_config_with_central_mode_none( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -205,6 +208,7 @@ async def test_switch_change_central_mode_true( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -347,6 +351,7 @@ async def test_switch_ac_change_central_mode_true( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -482,6 +487,7 @@ async def test_climate_ac_change_central_mode_false( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -624,6 +630,7 @@ async def test_climate_ac_only_change_central_mode_true( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -778,6 +785,7 @@ async def test_switch_change_central_mode_true_with_window( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, @@ -935,6 +943,7 @@ async def test_switch_change_central_mode_true_with_cool_only_and_window( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, diff --git a/tests/test_central_power_manager.py b/tests/test_central_power_manager.py index 3f83095..30681ae 100644 --- a/tests/test_central_power_manager.py +++ b/tests/test_central_power_manager.py @@ -741,6 +741,7 @@ async def test_central_power_manager_start_vtherm_power(hass: HomeAssistant, ski CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 1000, @@ -811,6 +812,7 @@ async def test_central_power_manager_start_vtherm_power(hass: HomeAssistant, ski CONF_USE_PRESENCE_FEATURE: False, CONF_UNDERLYING_LIST: ["switch.mock_climate"], CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 150, diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 780ed1f..0e02be1 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -471,6 +471,7 @@ async def test_user_config_flow_over_climate( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, @@ -495,6 +496,7 @@ async def test_user_config_flow_over_climate( assert result.get("errors") is None assert result["data"] == MOCK_TH_OVER_CLIMATE_USER_CONFIG | MOCK_TH_OVER_CLIMATE_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_TYPE_CONFIG | { CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, @@ -1077,6 +1079,7 @@ async def test_user_config_flow_over_climate_auto_start_stop( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, @@ -1104,6 +1107,7 @@ async def test_user_config_flow_over_climate_auto_start_stop( "data" ] == MOCK_TH_OVER_CLIMATE_USER_CONFIG | MOCK_TH_OVER_CLIMATE_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_TYPE_CONFIG | { CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, @@ -1274,6 +1278,7 @@ async def test_user_config_flow_over_switch_bug_552_tpi( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, @@ -1359,6 +1364,7 @@ async def test_user_config_flow_over_switch_bug_552_tpi( CONF_TEMP_MAX: 30, CONF_STEP_TEMPERATURE: 0.5, CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, @@ -1658,6 +1664,7 @@ async def test_user_config_flow_over_climate_valve( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, @@ -1684,6 +1691,7 @@ async def test_user_config_flow_over_climate_valve( assert result.get("errors") is None assert result["data"] == MOCK_TH_OVER_CLIMATE_USER_CONFIG | MOCK_TH_OVER_CLIMATE_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_TYPE_CONFIG | { CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.4, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, diff --git a/tests/test_inverted_switch.py b/tests/test_inverted_switch.py index f0749d1..fc166cc 100644 --- a/tests/test_inverted_switch.py +++ b/tests/test_inverted_switch.py @@ -45,6 +45,7 @@ async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state): CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, diff --git a/tests/test_last_seen.py b/tests/test_last_seen.py index 4d35b6d..66807e7 100644 --- a/tests/test_last_seen.py +++ b/tests/test_last_seen.py @@ -54,6 +54,7 @@ async def test_last_seen_feature(hass: HomeAssistant, skip_hass_states_is_state) "tpi_coef_int": 0.3, "tpi_coef_ext": 0.01, "minimal_activation_delay": 30, + "minimal_deactivation_delay": 0, CONF_SAFETY_DELAY_MIN: 5, # 5 minutes CONF_SAFETY_MIN_ON_PERCENT: 0.2, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, diff --git a/tests/test_motion.py b/tests/test_motion.py index fb345c9..0d480b0 100644 --- a/tests/test_motion.py +++ b/tests/test_motion.py @@ -305,6 +305,7 @@ async def test_motion_management_time_not_enough( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 10, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", @@ -506,6 +507,7 @@ async def test_motion_management_time_enough_and_presence( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", @@ -631,6 +633,7 @@ async def test_motion_management_time_enough_and_not_presence( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", @@ -757,6 +760,7 @@ async def test_motion_management_with_stop_during_condition( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", @@ -890,6 +894,7 @@ async def test_motion_management_with_stop_during_condition_last_state_on( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", diff --git a/tests/test_multiple_switch.py b/tests/test_multiple_switch.py index 0c85e91..55b9c02 100644 --- a/tests/test_multiple_switch.py +++ b/tests/test_multiple_switch.py @@ -45,6 +45,7 @@ async def test_one_switch_cycle( CONF_USE_PRESENCE_FEATURE: False, CONF_HEATER: "switch.mock_switch1", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, @@ -248,6 +249,7 @@ async def test_multiple_switchs( CONF_HEATER_4: "switch.mock_switch4", CONF_HEATER_KEEP_ALIVE: 0, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, @@ -383,6 +385,7 @@ async def test_multiple_climates( CONF_CLIMATE_3: "switch.mock_climate3", CONF_CLIMATE_4: "switch.mock_climate4", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, @@ -484,6 +487,7 @@ async def test_multiple_climates_underlying_changes( CONF_CLIMATE_3: "switch.mock_climate3", CONF_CLIMATE_4: "switch.mock_climate4", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, @@ -629,6 +633,7 @@ async def test_multiple_climates_underlying_changes_not_aligned( CONF_CLIMATE_3: "switch.mock_climate3", CONF_CLIMATE_4: "switch.mock_climate4", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, @@ -734,6 +739,7 @@ async def test_multiple_switch_power_management( ], CONF_HEATER_KEEP_ALIVE: 0, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, diff --git a/tests/test_overclimate.py b/tests/test_overclimate.py index ad317a5..69bad90 100644 --- a/tests/test_overclimate.py +++ b/tests/test_overclimate.py @@ -62,6 +62,7 @@ async def test_bug_56( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, @@ -653,6 +654,7 @@ async def test_bug_524(hass: HomeAssistant, skip_hass_states_is_state): CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -923,6 +925,7 @@ async def test_manual_hvac_off_should_take_the_lead_over_window( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -1100,6 +1103,7 @@ async def test_manual_hvac_off_should_take_the_lead_over_auto_start_stop( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, @@ -1267,6 +1271,7 @@ async def test_underlying_from_comes_back_to_life( CONF_USE_PRESENCE_FEATURE: False, CONF_UNDERLYING_LIST: ["climate.mock_climate"], CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_NONE, diff --git a/tests/test_power.py b/tests/test_power.py index 948bd51..6c95e29 100644 --- a/tests/test_power.py +++ b/tests/test_power.py @@ -130,7 +130,7 @@ async def test_power_feature_manager( # fmt:on # Finish the mock configuration - tpi_algo = PropAlgorithm(PROPORTIONAL_FUNCTION_TPI, 0.6, 0.01, 5, 0, "climate.vtherm") + tpi_algo = PropAlgorithm(PROPORTIONAL_FUNCTION_TPI, 0.6, 0.01, 5, 0, 0, "climate.vtherm") tpi_algo._on_percent = 1 # pylint: disable="protected-access" type(fake_vtherm).hvac_mode = PropertyMock(return_value=HVACMode.HEAT) type(fake_vtherm).is_device_active = PropertyMock(return_value=is_device_active) @@ -354,6 +354,7 @@ async def test_power_management_hvac_off( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, @@ -462,6 +463,7 @@ async def test_power_management_hvac_on( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, @@ -629,6 +631,7 @@ async def test_power_management_energy_over_switch( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, @@ -761,6 +764,7 @@ async def test_power_management_energy_over_climate( CONF_USE_PRESENCE_FEATURE: False, CONF_UNDERLYING_LIST: ["climate.mock_climate"], CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, @@ -860,6 +864,7 @@ async def test_power_management_turn_off_while_shedding(hass: HomeAssistant, ski CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, diff --git a/tests/test_safety.py b/tests/test_safety.py index a85173c..a2f1ea5 100644 --- a/tests/test_safety.py +++ b/tests/test_safety.py @@ -152,6 +152,7 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): "tpi_coef_int": 0.3, "tpi_coef_ext": 0.01, "minimal_activation_delay": 30, + "minimal_deactivation_delay": 0, "security_delay_min": 5, # 5 minutes "security_min_on_percent": 0.2, "security_default_on_percent": 0.1, @@ -346,6 +347,7 @@ async def test_security_feature_back_on_percent( "tpi_coef_int": 0.3, "tpi_coef_ext": 0.01, "minimal_activation_delay": 30, + "minimal_deactivation_delay": 0, "safety_delay_min": 5, # 5 minutes "safety_min_on_percent": 0.2, "safety_default_on_percent": 0.1, @@ -657,6 +659,7 @@ async def test_migration_security_safety( CONF_USE_POWER_FEATURE: False, CONF_USE_PRESENCE_FEATURE: False, CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, }, ) diff --git a/tests/test_sensors.py b/tests/test_sensors.py index 344f205..67ea590 100644 --- a/tests/test_sensors.py +++ b/tests/test_sensors.py @@ -62,6 +62,7 @@ async def test_sensors_over_switch( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 200, @@ -222,6 +223,7 @@ async def test_sensors_over_climate( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 1.5, @@ -358,6 +360,7 @@ async def test_sensors_over_climate_minimal( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, diff --git a/tests/test_start.py b/tests/test_start.py index 792c339..8546771 100644 --- a/tests/test_start.py +++ b/tests/test_start.py @@ -232,6 +232,7 @@ async def test_over_switch_deactivate_preset( CONF_HEATER_KEEP_ALIVE: 0, CONF_SAFETY_DELAY_MIN: 10, CONF_MINIMAL_ACTIVATION_DELAY: 10, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_TPI_COEF_INT: 0.6, CONF_TPI_COEF_EXT: 0.01, diff --git a/tests/test_switch_keep_alive.py b/tests/test_switch_keep_alive.py index 3642742..22b6e6e 100644 --- a/tests/test_switch_keep_alive.py +++ b/tests/test_switch_keep_alive.py @@ -39,6 +39,7 @@ def config_entry() -> MockConfigEntry: CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.1, }, diff --git a/tests/test_temp_number.py b/tests/test_temp_number.py index f796f2d..f9c26d3 100644 --- a/tests/test_temp_number.py +++ b/tests/test_temp_number.py @@ -75,6 +75,7 @@ async def test_add_number_for_central_config( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, @@ -170,6 +171,7 @@ async def test_add_number_for_central_config_without_temp( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, @@ -265,6 +267,7 @@ async def test_add_number_for_central_config_without_temp_ac_mode( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, @@ -359,6 +362,7 @@ async def test_add_number_for_central_config_without_temp_restore( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 61, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, diff --git a/tests/test_tpi.py b/tests/test_tpi.py index a033ec5..11e06b5 100644 --- a/tests/test_tpi.py +++ b/tests/test_tpi.py @@ -38,6 +38,7 @@ async def test_tpi_calculation( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, # CONF_DEVICE_POWER: 100, @@ -159,6 +160,94 @@ async def test_tpi_calculation( assert tpi_algo.off_time_sec == 60 +@pytest.mark.parametrize("expected_lingering_tasks", [True]) +@pytest.mark.parametrize("expected_lingering_timers", [True]) +async def test_minimal_deactivation_delay( + hass: HomeAssistant, skip_hass_states_is_state: None +): # pylint: disable=unused-argument + """Test the minimal deactivation delay""" + + entry = MockConfigEntry( + domain=DOMAIN, + title="TheOverSwitchMockName", + unique_id="uniqueId", + data={ + CONF_NAME: "TheOverSwitchMockName", + CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH, + CONF_TEMP_SENSOR: "sensor.mock_temp_sensor", + CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor", + CONF_CYCLE_MIN: 5, + CONF_TEMP_MIN: 15, + CONF_TEMP_MAX: 30, + CONF_USE_WINDOW_FEATURE: False, + CONF_USE_MOTION_FEATURE: False, + CONF_USE_POWER_FEATURE: False, + CONF_USE_PRESENCE_FEATURE: False, + CONF_HEATER: "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_MINIMAL_DEACTIVATION_DELAY: 60, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + }, + ) + + entity: BaseThermostat = await create_thermostat( + hass, entry, "climate.theoverswitchmockname" + ) + assert entity + assert entity._prop_algorithm # pylint: disable=protected-access + + tpi_algo: PropAlgorithm = entity._prop_algorithm # pylint: disable=protected-access + assert tpi_algo + + # off_time is less than minimal_deactivation_delay + tpi_algo.calculate(9, 6, 10, HVACMode.HEAT) + assert tpi_algo.on_percent == 0.89 + assert tpi_algo.calculated_on_percent == 0.89 + assert tpi_algo.on_time_sec == 300 + assert tpi_algo.off_time_sec == 0 + + # off_time is less than minimal_deactivation_delay + tpi_algo.calculate(6, 0, 104, HVACMode.HEAT) + assert tpi_algo.on_percent == 0.82 + assert tpi_algo.calculated_on_percent == 0.82 + assert tpi_algo.on_time_sec == 300 + assert tpi_algo.off_time_sec == 0 + + # off_time is exactly minimal_deactivation_delay + tpi_algo.calculate(10, 8, -10, HVACMode.HEAT) + assert tpi_algo.on_percent == 0.8 + assert tpi_algo.calculated_on_percent == 0.8 + assert tpi_algo.on_time_sec == 240 + assert tpi_algo.off_time_sec == 60 # Equal to minimal_deactivation_delay + + # off_time is greater than minimal_deactivation_delay + tpi_algo.calculate(10, 9, 0, HVACMode.HEAT) + assert tpi_algo.on_percent == 0.4 + assert tpi_algo.calculated_on_percent == 0.4 + assert tpi_algo.on_time_sec == 120 + assert tpi_algo.off_time_sec == 180 + + # with safety mode + tpi_algo.set_safety(0.2) + tpi_algo.calculate(10, 8, -10, HVACMode.HEAT) + assert tpi_algo.on_percent == 0.2 + assert tpi_algo.calculated_on_percent == 0.8 + assert tpi_algo.on_time_sec == 60 + assert tpi_algo.off_time_sec == 240 + tpi_algo.unset_safety() + + # with cooling mode + tpi_algo.calculate(10, 10, 90, HVACMode.COOL) + assert tpi_algo.on_percent == 0.8 + assert tpi_algo.calculated_on_percent == 0.8 + assert tpi_algo.on_time_sec == 240 + assert tpi_algo.off_time_sec == 60 + + @pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True]) async def test_wrong_tpi_parameters( @@ -174,6 +263,7 @@ async def test_wrong_tpi_parameters( 0.01, 5, 1, + 1, "entity_id", ) # We should not be there @@ -190,6 +280,7 @@ async def test_wrong_tpi_parameters( 0, 2, 3, + 3, "entity_id", ) # We should not be there @@ -206,6 +297,7 @@ async def test_wrong_tpi_parameters( 0, 2, 3, + 3, "entity_id", ) # We should not be there @@ -222,6 +314,7 @@ async def test_wrong_tpi_parameters( None, 2, 3, + 3, "entity_id", ) # We should not be there @@ -238,6 +331,7 @@ async def test_wrong_tpi_parameters( 0.00001, None, 3, + 3, "entity_id", ) # We should not be there @@ -262,6 +356,23 @@ async def test_wrong_tpi_parameters( # the normal case pass + # Test minimal_activation_delay + try: + algo = PropAlgorithm( + PROPORTIONAL_FUNCTION_TPI, + 0.6, + 0.00001, + 0, + 3, + None, + "entity_id", + ) + # We should not be there + assert False + except TypeError as e: + # the normal case + pass + # Test vtherm_entity_id try: algo = PropAlgorithm( @@ -270,6 +381,7 @@ async def test_wrong_tpi_parameters( 0.00001, 0, 12, + 12, None, ) # We should not be there diff --git a/tests/test_valve.py b/tests/test_valve.py index d754e18..d0d40dc 100644 --- a/tests/test_valve.py +++ b/tests/test_valve.py @@ -58,6 +58,7 @@ async def test_over_valve_full_start( PRESET_BOOST + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: 17.3, CONF_PRESET_POWER: 10, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, @@ -350,6 +351,7 @@ async def test_over_valve_regulation( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 60, CONF_SAFETY_MIN_ON_PERCENT: 0.3, # only send new valve open percent if dtemp is > 30% @@ -589,6 +591,7 @@ async def test_bug_533( CONF_VALVE: "number.mock_valve", CONF_AUTO_REGULATION_DTEMP: 10, # This parameter makes the bug CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 60, }, # | temps, diff --git a/tests/test_window.py b/tests/test_window.py index e8f7002..5d52db0 100644 --- a/tests/test_window.py +++ b/tests/test_window.py @@ -45,6 +45,7 @@ async def test_window_management_time_not_enough( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", @@ -134,6 +135,7 @@ async def test_window_management_time_enough( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", @@ -281,6 +283,7 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state): CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, @@ -490,6 +493,7 @@ async def test_window_auto_fast_and_sensor( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.fake_window_sensor", @@ -615,6 +619,7 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "switch.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6, @@ -783,6 +788,7 @@ async def test_window_auto_no_on_percent( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6, @@ -910,6 +916,7 @@ async def test_window_bypass(hass: HomeAssistant, skip_hass_states_is_state): CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", @@ -1050,6 +1057,7 @@ async def test_window_auto_bypass(hass: HomeAssistant, skip_hass_states_is_state CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6, @@ -1178,6 +1186,7 @@ async def test_window_bypass_reactivate(hass: HomeAssistant, skip_hass_states_is CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", @@ -1615,6 +1624,7 @@ async def test_window_action_eco_temp(hass: HomeAssistant, skip_hass_states_is_s CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, @@ -1812,6 +1822,7 @@ async def test_window_action_frost_temp(hass: HomeAssistant, skip_hass_states_is CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, @@ -2016,6 +2027,7 @@ async def test_bug_66( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.5, CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, # !! here @@ -2155,6 +2167,7 @@ async def test_window_action_frost_temp_preset_change( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_ACTION: CONF_WINDOW_FROST_TEMP, @@ -2265,6 +2278,7 @@ async def test_window_action_frost_temp_temp_change( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_MINIMAL_DEACTIVATION_DELAY: 0, CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_ACTION: CONF_WINDOW_FROST_TEMP,