From 9defd2bcb45f4485652c6823a8128e910aa04023 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 8 May 2024 16:03:37 +0000 Subject: [PATCH] fix: set temps don't restore on HA boot if a preset is set when HA restarts Fixes #184 --- config/configuration.yaml | 3 +- .../dual_smart_thermostat/climate.py | 1 + .../managers/temperature_manager.py | 16 +- tests/test_dual_mode.py | 152 +++++++++++++++++- 4 files changed, 166 insertions(+), 6 deletions(-) diff --git a/config/configuration.yaml b/config/configuration.yaml index ee6e291..504e1f9 100644 --- a/config/configuration.yaml +++ b/config/configuration.yaml @@ -303,7 +303,8 @@ climate: - platform: dual_smart_thermostat name: FAN Only Room unique_id: fan_only_room - fan: switch.fan + heater: switch.fan + fan_mode: true fan_hot_tolerance: 2 fan_on_with_ac: true target_sensor: sensor.room_temp diff --git a/custom_components/dual_smart_thermostat/climate.py b/custom_components/dual_smart_thermostat/climate.py index c101445..0663d0c 100644 --- a/custom_components/dual_smart_thermostat/climate.py +++ b/custom_components/dual_smart_thermostat/climate.py @@ -528,6 +528,7 @@ async def _async_startup(*_) -> None: else: # No previous state, try and restore defaults + _LOGGER.debug("No previous state found, setting defaults") if not self.hvac_device.hvac_mode: self.hvac_device.hvac_mode = HVACMode.OFF if self.hvac_device.hvac_mode == HVACMode.OFF: diff --git a/custom_components/dual_smart_thermostat/managers/temperature_manager.py b/custom_components/dual_smart_thermostat/managers/temperature_manager.py index 855b5e8..1df647f 100644 --- a/custom_components/dual_smart_thermostat/managers/temperature_manager.py +++ b/custom_components/dual_smart_thermostat/managers/temperature_manager.py @@ -2,7 +2,12 @@ import math from typing import Any -from homeassistant.components.climate import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP +from homeassistant.components.climate import ( + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + DEFAULT_MAX_TEMP, + DEFAULT_MIN_TEMP, +) from homeassistant.components.climate.const import HVACMode from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.core import HomeAssistant, State, callback @@ -387,16 +392,21 @@ def _set_default_temps_range_mode(self) -> None: self._target_temp_high += PRECISION_WHOLE def apply_old_state(self, old_state: State) -> None: + _LOGGER.debug("Applying old state: %s", old_state) if old_state is None: return # If we have no initial temperature, restore if self._target_temp_low is None: - old_target_min = old_state.attributes.get(ATTR_PREV_TARGET_LOW) + old_target_min = old_state.attributes.get( + ATTR_PREV_TARGET_LOW + ) or old_state.attributes.get(ATTR_TARGET_TEMP_LOW) if old_target_min is not None: self._target_temp_low = float(old_target_min) if self._target_temp_high is None: - old_target_max = old_state.attributes.get(ATTR_PREV_TARGET_HIGH) + old_target_max = old_state.attributes.get( + ATTR_PREV_TARGET_HIGH + ) or old_state.attributes.get(ATTR_TARGET_TEMP_HIGH) if old_target_max is not None: self._target_temp_high = float(old_target_max) if self._target_temp is None: diff --git a/tests/test_dual_mode.py b/tests/test_dual_mode.py index 6e9c449..01c5b53 100644 --- a/tests/test_dual_mode.py +++ b/tests/test_dual_mode.py @@ -16,15 +16,21 @@ HVACAction, HVACMode, ) -from homeassistant.components.climate.const import ATTR_PRESET_MODE, DOMAIN as CLIMATE +from homeassistant.components.climate.const import ( + ATTR_PRESET_MODE, + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + DOMAIN as CLIMATE, +) from homeassistant.const import ( + ATTR_TEMPERATURE, ENTITY_MATCH_ALL, STATE_CLOSED, STATE_OFF, STATE_ON, STATE_OPEN, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import CoreState, HomeAssistant, State from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component @@ -554,6 +560,148 @@ async def test_set_heat_cool_preset_mode_and_restore_prev_temp( assert state.attributes.get("target_temp_high") == 22 +@pytest.mark.parametrize( + ("preset", "temp_low", "temp_high"), + [ + (PRESET_NONE, 18, 22), + (PRESET_AWAY, 16, 30), + (PRESET_COMFORT, 20, 27), + (PRESET_ECO, 18, 29), + (PRESET_HOME, 19, 23), + (PRESET_SLEEP, 17, 24), + (PRESET_ACTIVITY, 21, 28), + (PRESET_ANTI_FREEZE, 5, 32), + ], +) +async def test_set_heat_cool_fan_preset_mode_and_restore_prev_temp( + hass: HomeAssistant, + setup_comp_heat_cool_fan_presets, # noqa: F811 + preset, + temp_low, + temp_high, +) -> None: + """Test the setting preset mode. + + Verify original temperature is restored. + """ + await common.async_set_temperature(hass, 23, common.ENTITY, 22, 18) + await common.async_set_preset_mode(hass, preset) + state = hass.states.get(common.ENTITY) + assert state.attributes.get("target_temp_low") == temp_low + assert state.attributes.get("target_temp_high") == temp_high + await common.async_set_preset_mode(hass, PRESET_NONE) + state = hass.states.get(common.ENTITY) + assert state.attributes.get("target_temp_low") == 18 + assert state.attributes.get("target_temp_high") == 22 + + +@pytest.mark.parametrize( + "preset", + [PRESET_NONE, PRESET_AWAY], +) +async def test_set_heat_cool_fan_restore_state( + hass: HomeAssistant, preset # noqa: F811 +) -> None: + common.mock_restore_cache( + hass, + ( + State( + "climate.test_thermostat", + HVACMode.HEAT_COOL, + { + ATTR_TARGET_TEMP_HIGH: "20", + ATTR_TARGET_TEMP_LOW: "18", + ATTR_PRESET_MODE: preset, + }, + ), + ), + ) + + hass.set_state(CoreState.starting) + + await async_setup_component( + hass, + CLIMATE, + { + "climate": { + "platform": DOMAIN, + "name": "test_thermostat", + "heater": common.ENT_SWITCH, + "cooler": common.ENT_COOLER, + "fan": common.ENT_FAN, + "heat_cool_mode": True, + "target_sensor": common.ENT_SENSOR, + PRESET_AWAY: { + "temperature": 14, + "target_temp_high": 20, + "target_temp_low": 18, + }, + } + }, + ) + await hass.async_block_till_done() + state = hass.states.get("climate.test_thermostat") + assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 20 + assert state.attributes[ATTR_TARGET_TEMP_LOW] == 18 + assert state.attributes[ATTR_PRESET_MODE] == preset + assert state.state == HVACMode.HEAT_COOL + + +@pytest.mark.parametrize( + ["preset", "hvac_mode"], + [ + [PRESET_NONE, HVACMode.HEAT], + [PRESET_AWAY, HVACMode.HEAT], + [PRESET_NONE, HVACMode.COOL], + [PRESET_AWAY, HVACMode.COOL], + ], +) +async def test_set_heat_cool_fan_restore_state_2( + hass: HomeAssistant, preset, hvac_mode # noqa: F811 +) -> None: + common.mock_restore_cache( + hass, + ( + State( + "climate.test_thermostat", + hvac_mode, + { + ATTR_TEMPERATURE: "20", + ATTR_PRESET_MODE: preset, + }, + ), + ), + ) + + hass.set_state(CoreState.starting) + + await async_setup_component( + hass, + CLIMATE, + { + "climate": { + "platform": DOMAIN, + "name": "test_thermostat", + "heater": common.ENT_SWITCH, + "cooler": common.ENT_COOLER, + "fan": common.ENT_FAN, + "heat_cool_mode": True, + "target_sensor": common.ENT_SENSOR, + PRESET_AWAY: { + "temperature": 14, + "target_temp_high": 20, + "target_temp_low": 18, + }, + } + }, + ) + await hass.async_block_till_done() + state = hass.states.get("climate.test_thermostat") + assert state.attributes[ATTR_TEMPERATURE] == 20 + assert state.attributes[ATTR_PRESET_MODE] == preset + assert state.state == hvac_mode + + @pytest.mark.parametrize( ("preset", "temperature"), [