From 74931071de0ba6288f00e920be55344720d8e165 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Mon, 30 Sep 2024 16:29:39 +0200 Subject: [PATCH] Use scheduled current preset (if set), when setting HVAC mode in AVM Fritz!Smarthome (#126044) * Use temperature of current preset when set fritz HVAC mode to HEAT If the HVAC mode of the Fritzbox thermostats changes from `HVACMode.OFF` to `HVAMode.HEAT`, the current preset (COMFORT / ECO) should be observed. Depending on the status of the current preset, the set temperature of comfort / eco is set as the new temperature. * fixup do not use value_scheduled_preset Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com> * Add current_preset value to test_set_hvac_mode The current_preset parameter allows the mock to be set to an active preset. When setting HVACMode.HEAT, the respective temperature of the ECO/COMFORT preset should be set. * fixup Use the updated value_scheduled_preset function To distinguish which temperature should be used when setting the `HVAMode.HEAT`, `value_schedules_preset` is now used again, which has been updated since the first commit. If no schedule is active, the comfort_temperature is used. Otherwise, the respective temperature of the current preset. Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com> --------- Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com> --- homeassistant/components/fritzbox/climate.py | 7 ++++- tests/components/fritzbox/test_climate.py | 31 ++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index 7b0bec6fc09..924d92d6c5b 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -33,6 +33,7 @@ from .const import ( from .coordinator import FritzboxConfigEntry, FritzboxDataUpdateCoordinator from .entity import FritzBoxDeviceEntity from .model import ClimateExtraAttributes +from .sensor import value_scheduled_preset HVAC_MODES = [HVACMode.HEAT, HVACMode.OFF] PRESET_HOLIDAY = "holiday" @@ -177,7 +178,11 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): if hvac_mode == HVACMode.OFF: await self.async_set_temperature(temperature=OFF_REPORT_SET_TEMPERATURE) else: - await self.async_set_temperature(temperature=self.data.comfort_temperature) + if value_scheduled_preset(self.data) == PRESET_ECO: + target_temp = self.data.eco_temperature + else: + target_temp = self.data.comfort_temperature + await self.async_set_temperature(temperature=target_temp) @property def preset_mode(self) -> str | None: diff --git a/tests/components/fritzbox/test_climate.py b/tests/components/fritzbox/test_climate.py index 61fe6b48a7a..29f5742216f 100644 --- a/tests/components/fritzbox/test_climate.py +++ b/tests/components/fritzbox/test_climate.py @@ -313,12 +313,24 @@ async def test_set_temperature( @pytest.mark.parametrize( - ("service_data", "target_temperature", "expected_call_args"), + ("service_data", "target_temperature", "current_preset", "expected_call_args"), [ - ({ATTR_HVAC_MODE: HVACMode.OFF}, 22, [call(0)]), - ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, [call(22)]), - ({ATTR_HVAC_MODE: HVACMode.HEAT}, 18, []), - ({ATTR_HVAC_MODE: HVACMode.HEAT}, 22, []), + # mode off always sets target temperature to 0 + ({ATTR_HVAC_MODE: HVACMode.OFF}, 22, PRESET_COMFORT, [call(0)]), + ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, PRESET_ECO, [call(0)]), + ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, None, [call(0)]), + # mode heat sets target temperature based on current scheduled preset, + # when not already in mode heat + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, PRESET_COMFORT, [call(22)]), + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, PRESET_ECO, [call(16)]), + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, None, [call(22)]), + # mode heat does not set target temperature, when already in mode heat + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 16, PRESET_COMFORT, []), + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 16, PRESET_ECO, []), + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 16, None, []), + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 22, PRESET_COMFORT, []), + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 22, PRESET_ECO, []), + ({ATTR_HVAC_MODE: HVACMode.HEAT}, 22, None, []), ], ) async def test_set_hvac_mode( @@ -326,11 +338,20 @@ async def test_set_hvac_mode( fritz: Mock, service_data: dict, target_temperature: float, + current_preset: str, expected_call_args: list[_Call], ) -> None: """Test setting hvac mode.""" device = FritzDeviceClimateMock() device.target_temperature = target_temperature + + if current_preset is PRESET_COMFORT: + device.nextchange_temperature = device.eco_temperature + elif current_preset is PRESET_ECO: + device.nextchange_temperature = device.comfort_temperature + else: + device.nextchange_endperiod = 0 + assert await setup_config_entry( hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz )