From 549423b313f7ca1966f55584bdd743813c2554ce Mon Sep 17 00:00:00 2001 From: jkreiss-coexya <9569564+jdeksup@users.noreply.github.com> Date: Mon, 13 May 2024 08:27:52 +0200 Subject: [PATCH] Enhance temperature regulation when working with internal device temperature (#453) * [feature/autoregulation-send-for-underlyingtemp] Do not forget regulation send when using underlying device temperature for offset * [feature/autoregulation-send-for-underlyingtemp] Add unit test for dtemp = 0 * [feature/autoregulation-send-for-underlyingtemp] Test with dtemp lower than 0.5 * [feature/autoregulation-send-for-underlyingtemp] Comments --- .../thermostat_climate.py | 10 +- tests/test_auto_regulation.py | 113 ++++++++++++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/custom_components/versatile_thermostat/thermostat_climate.py b/custom_components/versatile_thermostat/thermostat_climate.py index de2042f..879dece 100644 --- a/custom_components/versatile_thermostat/thermostat_climate.py +++ b/custom_components/versatile_thermostat/thermostat_climate.py @@ -168,11 +168,17 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): _LOGGER.info("%s - regulation calculation will be done", self) + # use _attr_target_temperature_step to round value if _auto_regulation_dtemp is equal to 0 + regulation_step = self._auto_regulation_dtemp if self._auto_regulation_dtemp else self._attr_target_temperature_step + _LOGGER.debug("%s - usage of regulation_step: %.2f ", + self, + regulation_step) + new_regulated_temp = round_to_nearest( self._regulation_algo.calculate_regulated_temperature( self.current_temperature, self._cur_ext_temp ), - self._auto_regulation_dtemp, + regulation_step, ) dtemp = new_regulated_temp - self._regulated_target_temp @@ -216,7 +222,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): ): offset_temp = device_temp - self.current_temperature - target_temp = round_to_nearest(self.regulated_target_temp + offset_temp, self._auto_regulation_dtemp) + target_temp = round_to_nearest(self.regulated_target_temp + offset_temp, regulation_step) _LOGGER.debug( "%s - The device offset temp for regulation is %.2f - internal temp is %.2f. New target is %.2f", diff --git a/tests/test_auto_regulation.py b/tests/test_auto_regulation.py index 88d545e..76d0f1f 100644 --- a/tests/test_auto_regulation.py +++ b/tests/test_auto_regulation.py @@ -544,3 +544,116 @@ async def test_over_climate_regulation_use_device_temp( ), ] ) + +@pytest.mark.parametrize("expected_lingering_tasks", [True]) +@pytest.mark.parametrize("expected_lingering_timers", [True]) +async def test_over_climate_regulation_dtemp_null( + hass: HomeAssistant, skip_hass_states_is_state, skip_send_event +): + """Test the regulation of an over climate thermostat with no Dtemp limitation""" + + entry = MockConfigEntry( + domain=DOMAIN, + title="TheOverClimateMockName", + unique_id="uniqueId", + # This is include a medium regulation + data=PARTIAL_CLIMATE_AC_CONFIG | {CONF_AUTO_REGULATION_DTEMP: 0, CONF_STEP_TEMPERATURE: 0.1}, + ) + + tz = get_tz(hass) # pylint: disable=invalid-name + now: datetime = datetime.now(tz=tz) + fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {}) + + # Creates the regulated VTherm over climate + # change temperature so that the heating will start + event_timestamp = now - timedelta(minutes=20) + + with patch( + "custom_components.versatile_thermostat.commons.NowClass.get_now", + return_value=event_timestamp, + ), patch( + "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate", + return_value=fake_underlying_climate, + ): + entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname") + + assert entity + assert isinstance(entity, ThermostatOverClimate) + + assert entity.name == "TheOverClimateMockName" + assert entity.is_over_climate is True + assert entity.is_regulated is True + + # Activate the heating by changing HVACMode and temperature + # Select a hvacmode, presence and preset + await entity.async_set_hvac_mode(HVACMode.HEAT) + assert entity.hvac_mode is HVACMode.HEAT + assert entity.hvac_action == HVACAction.OFF + + # change temperature so that the heating will start + await send_temperature_change_event(entity, 15, event_timestamp) + await send_ext_temperature_change_event(entity, 10, event_timestamp) + + # set manual target temp + event_timestamp = now - timedelta(minutes=17) + with patch( + "custom_components.versatile_thermostat.commons.NowClass.get_now", + return_value=event_timestamp, + ): + await entity.async_set_temperature(temperature=20) + + fake_underlying_climate.set_hvac_action( + HVACAction.HEATING + ) # simulate under cooling + assert entity.hvac_action == HVACAction.HEATING + assert entity.preset_mode == PRESET_NONE # Manual mode + + # the regulated temperature should be lower + assert entity.regulated_target_temp > entity.target_temperature + assert ( + entity.regulated_target_temp == 20 + 2.4 + ) # In medium we could go up to +3 degre + assert entity.hvac_action == HVACAction.HEATING + + # change temperature so that the regulated temperature should slow down + event_timestamp = now - timedelta(minutes=15) + with patch( + "custom_components.versatile_thermostat.commons.NowClass.get_now", + return_value=event_timestamp, + ): + await send_temperature_change_event(entity, 19, event_timestamp) + await send_ext_temperature_change_event(entity, 10, event_timestamp) + + # the regulated temperature should be greater + assert entity.regulated_target_temp > entity.target_temperature + assert ( + entity.regulated_target_temp == 20 + 0.9 + ) + + # change temperature so that the regulated temperature should slow down + event_timestamp = now - timedelta(minutes=13) + with patch( + "custom_components.versatile_thermostat.commons.NowClass.get_now", + return_value=event_timestamp, + ): + await send_temperature_change_event(entity, 20, event_timestamp) + await send_ext_temperature_change_event(entity, 10, event_timestamp) + + # the regulated temperature should be greater + assert entity.regulated_target_temp > entity.target_temperature + assert ( + entity.regulated_target_temp == 20 + 0.5 + ) + + old_regulated_temp = entity.regulated_target_temp + # Test if a small temperature change is taken into account : change temperature so that dtemp < 0.5 and time is > period_min (+ 3min) + event_timestamp = now - timedelta(minutes=10) + with patch( + "custom_components.versatile_thermostat.commons.NowClass.get_now", + return_value=event_timestamp, + ): + await send_temperature_change_event(entity, 19.6, event_timestamp) + await send_ext_temperature_change_event(entity, 10, event_timestamp) + + # the regulated temperature should be greater. This does not work if dtemp is not null + assert entity.regulated_target_temp > old_regulated_temp \ No newline at end of file