From a440b358151194febeb4e38ee61810a35d581d43 Mon Sep 17 00:00:00 2001 From: Paulo Ferreira de Castro Date: Sun, 4 Feb 2024 19:58:22 +0000 Subject: [PATCH] Prevent disabled heating warning loop while HVACMode is OFF (#374) --- .../versatile_thermostat/base_thermostat.py | 2 +- .../versatile_thermostat/prop_algorithm.py | 15 +++++--- .../versatile_thermostat/thermostat_switch.py | 2 +- .../versatile_thermostat/thermostat_valve.py | 2 +- tests/test_tpi.py | 37 ++++++++++++++----- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/custom_components/versatile_thermostat/base_thermostat.py b/custom_components/versatile_thermostat/base_thermostat.py index 9e70229..d122b26 100644 --- a/custom_components/versatile_thermostat/base_thermostat.py +++ b/custom_components/versatile_thermostat/base_thermostat.py @@ -799,7 +799,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity): self._target_temp, self._cur_temp, self._cur_ext_temp, - self._hvac_mode == HVACMode.COOL, + self._hvac_mode or HVACMode.OFF, ) self.hass.create_task(self._check_initial_state()) diff --git a/custom_components/versatile_thermostat/prop_algorithm.py b/custom_components/versatile_thermostat/prop_algorithm.py index 2a8cc38..8c61dda 100644 --- a/custom_components/versatile_thermostat/prop_algorithm.py +++ b/custom_components/versatile_thermostat/prop_algorithm.py @@ -1,6 +1,8 @@ """ The TPI calculation module """ import logging +from homeassistant.components.climate import HVACMode + _LOGGER = logging.getLogger(__name__) PROPORTIONAL_FUNCTION_ATAN = "atan" @@ -46,19 +48,20 @@ class PropAlgorithm: def calculate( self, - target_temp: float, - current_temp: float, - ext_current_temp: float, - cooling=False, + target_temp: float | None, + current_temp: float | None, + ext_current_temp: float | None, + hvac_mode: HVACMode, ): """Do the calculation of the duration""" if target_temp is None or current_temp is None: - _LOGGER.warning( + log = _LOGGER.debug if hvac_mode == HVACMode.OFF else _LOGGER.warning + log( "Proportional algorithm: calculation is not possible cause target_temp or current_temp is null. Heating/cooling will be disabled" # pylint: disable=line-too-long ) self._calculated_on_percent = 0 else: - if cooling: + if hvac_mode == HVACMode.COOL: delta_temp = current_temp - target_temp delta_ext_temp = ( ext_current_temp diff --git a/custom_components/versatile_thermostat/thermostat_switch.py b/custom_components/versatile_thermostat/thermostat_switch.py index f711c0b..f069161 100644 --- a/custom_components/versatile_thermostat/thermostat_switch.py +++ b/custom_components/versatile_thermostat/thermostat_switch.py @@ -183,7 +183,7 @@ class ThermostatOverSwitch(BaseThermostat): self._target_temp, self._cur_temp, self._cur_ext_temp, - self._hvac_mode == HVACMode.COOL, + self._hvac_mode or HVACMode.OFF, ) self.update_custom_attributes() self.async_write_ha_state() diff --git a/custom_components/versatile_thermostat/thermostat_valve.py b/custom_components/versatile_thermostat/thermostat_valve.py index a42585d..11d67fb 100644 --- a/custom_components/versatile_thermostat/thermostat_valve.py +++ b/custom_components/versatile_thermostat/thermostat_valve.py @@ -234,7 +234,7 @@ class ThermostatOverValve(BaseThermostat): self._target_temp, self._cur_temp, self._cur_ext_temp, - self._hvac_mode == HVACMode.COOL, + self._hvac_mode or HVACMode.OFF, ) new_valve_percent = round( diff --git a/tests/test_tpi.py b/tests/test_tpi.py index ca0d0b6..0ef4016 100644 --- a/tests/test_tpi.py +++ b/tests/test_tpi.py @@ -1,6 +1,9 @@ """ Test the TPI algorithm """ +from homeassistant.components.climate import HVACMode + from custom_components.versatile_thermostat.base_thermostat import BaseThermostat +from custom_components.versatile_thermostat.prop_algorithm import PropAlgorithm from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import @@ -42,53 +45,54 @@ async def test_tpi_calculation( hass, entry, "climate.theoverswitchmockname" ) assert entity + assert entity._prop_algorithm # pylint: disable=protected-access - tpi_algo = entity._prop_algorithm # pylint: disable=protected-access + tpi_algo: PropAlgorithm = entity._prop_algorithm # pylint: disable=protected-access assert tpi_algo - tpi_algo.calculate(15, 10, 7) + tpi_algo.calculate(15, 10, 7, HVACMode.HEAT) assert tpi_algo.on_percent == 1 assert tpi_algo.calculated_on_percent == 1 assert tpi_algo.on_time_sec == 300 assert tpi_algo.off_time_sec == 0 assert entity.mean_cycle_power is None # no device power configured - tpi_algo.calculate(15, 14, 5, False) + tpi_algo.calculate(15, 14, 5, 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 tpi_algo.set_security(0.1) - tpi_algo.calculate(15, 14, 5, False) + tpi_algo.calculate(15, 14, 5, HVACMode.HEAT) assert tpi_algo.on_percent == 0.1 assert tpi_algo.calculated_on_percent == 0.4 assert tpi_algo.on_time_sec == 30 # >= minimal_activation_delay (=30) assert tpi_algo.off_time_sec == 270 tpi_algo.unset_security() - tpi_algo.calculate(15, 14, 5, False) + tpi_algo.calculate(15, 14, 5, 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 # Test minimal activation delay - tpi_algo.calculate(15, 14.7, 15, False) + tpi_algo.calculate(15, 14.7, 15, HVACMode.HEAT) assert tpi_algo.on_percent == 0.09 assert tpi_algo.calculated_on_percent == 0.09 assert tpi_algo.on_time_sec == 0 assert tpi_algo.off_time_sec == 300 tpi_algo.set_security(0.09) - tpi_algo.calculate(15, 14.7, 15, False) + tpi_algo.calculate(15, 14.7, 15, HVACMode.HEAT) assert tpi_algo.on_percent == 0.09 assert tpi_algo.calculated_on_percent == 0.09 assert tpi_algo.on_time_sec == 0 assert tpi_algo.off_time_sec == 300 tpi_algo.unset_security() - tpi_algo.calculate(25, 30, 35, True) + tpi_algo.calculate(25, 30, 35, HVACMode.COOL) assert tpi_algo.on_percent == 1 assert tpi_algo.calculated_on_percent == 1 assert tpi_algo.on_time_sec == 300 @@ -96,9 +100,24 @@ async def test_tpi_calculation( assert entity.mean_cycle_power is None # no device power configured tpi_algo.set_security(0.09) - tpi_algo.calculate(25, 30, 35, True) + tpi_algo.calculate(25, 30, 35, HVACMode.COOL) assert tpi_algo.on_percent == 0.09 assert tpi_algo.calculated_on_percent == 1 assert tpi_algo.on_time_sec == 0 assert tpi_algo.off_time_sec == 300 assert entity.mean_cycle_power is None # no device power configured + + tpi_algo.unset_security() + # The calculated values for HVACMode.OFF are the same as for HVACMode.HEAT. + tpi_algo.calculate(15, 10, 7, HVACMode.OFF) + assert tpi_algo.on_percent == 1 + assert tpi_algo.calculated_on_percent == 1 + assert tpi_algo.on_time_sec == 300 + assert tpi_algo.off_time_sec == 0 + + # If target_temp or current_temp are None, _calculated_on_percent is set to 0. + tpi_algo.calculate(15, None, 7, HVACMode.OFF) + assert tpi_algo.on_percent == 0 + assert tpi_algo.calculated_on_percent == 0 + assert tpi_algo.on_time_sec == 0 + assert tpi_algo.off_time_sec == 300