From f614a6bac1a4434c65686c6e413bc80de5132ce5 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 7 May 2024 08:32:45 +0000 Subject: [PATCH] fix: fan hot tolerance issue Fixes #181 --- .../managers/temperature_manager.py | 2 +- tests/test_dual_mode.py | 94 +++++++++++++++++++ tests/test_fan_mode.py | 83 +++++++++++++++- 3 files changed, 177 insertions(+), 2 deletions(-) diff --git a/custom_components/dual_smart_thermostat/managers/temperature_manager.py b/custom_components/dual_smart_thermostat/managers/temperature_manager.py index 1df647f..26ebebc 100644 --- a/custom_components/dual_smart_thermostat/managers/temperature_manager.py +++ b/custom_components/dual_smart_thermostat/managers/temperature_manager.py @@ -251,7 +251,7 @@ def is_within_fan_tolerance(self, target_attr="_target_temp") -> bool: ) return ( - self._cur_temp > too_hot_for_ac_temp + self._cur_temp >= too_hot_for_ac_temp and self._cur_temp <= too_hot_for_fan_temp ) diff --git a/tests/test_dual_mode.py b/tests/test_dual_mode.py index 01c5b53..7fead79 100644 --- a/tests/test_dual_mode.py +++ b/tests/test_dual_mode.py @@ -1256,6 +1256,100 @@ async def test_hvac_mode_mode_heat_cool( assert state.attributes["supported_features"] == 386 +@pytest.mark.parametrize( + "hvac_mode", + [ + HVACMode.HEAT_COOL, + HVACMode.COOL, + ], +) +async def test_hvac_mode_mode_heat_cool_fan_tolerance( + hass: HomeAssistant, hvac_mode, setup_comp_1 # noqa: F811 +): + """Test thermostat heater and cooler switch in heat/cool mode.""" + + heater_switch = "input_boolean.heater" + cooler_switch = "input_boolean.cooler" + fan_switch = "input_boolean.fan" + + assert await async_setup_component( + hass, + input_boolean.DOMAIN, + {"input_boolean": {"heater": None, "cooler": None, "fan": None}}, + ) + + assert await async_setup_component( + hass, + input_number.DOMAIN, + { + "input_number": { + "temp": {"name": "test", "initial": 10, "min": 0, "max": 40, "step": 1} + } + }, + ) + + assert await async_setup_component( + hass, + CLIMATE, + { + "climate": { + "platform": DOMAIN, + "name": "test", + "cooler": cooler_switch, + "heater": heater_switch, + "fan": fan_switch, + "hot_tolerance": 0.2, + "cold_tolerance": 0.2, + "fan_hot_tolerance": 0.5, + "heat_cool_mode": True, + "target_sensor": common.ENT_SENSOR, + } + }, + ) + await hass.async_block_till_done() + + # switch to COOL mode and test the fan hot tolerance + # after the hot tolerance first the fan should turn on + # and outside the fan_hot_tolerance the AC + + await common.async_set_hvac_mode(hass, hvac_mode) + await common.async_set_temperature(hass, 20, ENTITY_MATCH_ALL, 20, 18) + setup_sensor(hass, 20) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(heater_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_OFF + + setup_sensor(hass, 20.2) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(heater_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_ON + + setup_sensor(hass, 20.5) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(heater_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_ON + + setup_sensor(hass, 20.7) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(heater_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_ON + + setup_sensor(hass, 20.8) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_ON + assert hass.states.get(heater_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_OFF + + async def test_hvac_mode_mode_heat_cool_hvac_modes_temps( hass: HomeAssistant, setup_comp_1 # noqa: F811 ): diff --git a/tests/test_fan_mode.py b/tests/test_fan_mode.py index e60356f..d10ac64 100644 --- a/tests/test_fan_mode.py +++ b/tests/test_fan_mode.py @@ -2375,7 +2375,88 @@ async def test_set_target_temp_ac_on_after_fan_tolerance( call = calls[1] assert call.domain == HASS_DOMAIN assert call.service == SERVICE_TURN_ON - assert call.data["entity_id"] == common.ENT_SWITCH + assert call.data["entity_id"] == common.ENT_FAN + + +async def test_set_target_temp_ac_on_after_fan_tolerance_2( + hass: HomeAssistant, setup_comp_1 # noqa: F811 +) -> None: + cooler_switch = "input_boolean.test" + fan_switch = "input_boolean.fan" + + assert await async_setup_component( + hass, + input_boolean.DOMAIN, + {"input_boolean": {"test": None, "fan": None}}, + ) + + assert await async_setup_component( + hass, + input_number.DOMAIN, + { + "input_number": { + "temp": {"name": "test", "initial": 10, "min": 0, "max": 40, "step": 1} + } + }, + ) + + assert await async_setup_component( + hass, + CLIMATE, + { + "climate": { + "platform": DOMAIN, + "name": "test", + "cold_tolerance": 0.2, + "hot_tolerance": 0.2, + "ac_mode": True, + "heater": cooler_switch, + "target_sensor": common.ENT_SENSOR, + "fan": fan_switch, + "fan_hot_tolerance": 0.5, + "initial_hvac_mode": HVACMode.OFF, + } + }, + ) + await hass.async_block_till_done() + + await common.async_set_hvac_mode(hass, HVACMode.COOL) + await common.async_set_temperature(hass, 20) + + # below hot_tolerance + setup_sensor(hass, 20) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_OFF + + # within hot_tolerance and fan_hot_tolerance + setup_sensor(hass, 20.2) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_ON + + # within hot_tolerance and fan_hot_tolerance + setup_sensor(hass, 20.5) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_ON + + # within hot_tolerance and fan_hot_tolerance + setup_sensor(hass, 20.7) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_OFF + assert hass.states.get(fan_switch).state == STATE_ON + + # outside fan_hot_tolerance, within hot_tolerance + setup_sensor(hass, 20.8) + await hass.async_block_till_done() + + assert hass.states.get(cooler_switch).state == STATE_ON + assert hass.states.get(fan_switch).state == STATE_OFF ######################