Fix generic_thermostat so it doesn't turn on when current temp is within target temp range (#138209)

* Don't turn on thermostat if temp is equal to target temp.

* Update strings to reflect logic change.

* Fix logic and add zero tolerance tests.

* Include tests for cool mode

* Removed unnecessary async_block_till_done calls
This commit is contained in:
Eli Sand 2025-03-30 14:43:13 -04:00 committed by GitHub
parent 506d485c0d
commit 5106548f2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 5 deletions

View File

@ -539,10 +539,14 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
return
assert self._cur_temp is not None and self._target_temp is not None
too_cold = self._target_temp >= self._cur_temp + self._cold_tolerance
too_hot = self._cur_temp >= self._target_temp + self._hot_tolerance
min_temp = self._target_temp - self._cold_tolerance
max_temp = self._target_temp + self._hot_tolerance
if self._is_device_active:
if (self.ac_mode and too_cold) or (not self.ac_mode and too_hot):
if (self.ac_mode and self._cur_temp <= min_temp) or (
not self.ac_mode and self._cur_temp >= max_temp
):
_LOGGER.debug("Turning off heater %s", self.heater_entity_id)
await self._async_heater_turn_off()
elif time is not None:
@ -552,7 +556,9 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
self.heater_entity_id,
)
await self._async_heater_turn_on()
elif (self.ac_mode and too_hot) or (not self.ac_mode and too_cold):
elif (self.ac_mode and self._cur_temp > max_temp) or (
not self.ac_mode and self._cur_temp < min_temp
):
_LOGGER.debug("Turning on heater %s", self.heater_entity_id)
await self._async_heater_turn_on()
elif time is not None:

View File

@ -21,7 +21,7 @@
"heater": "Switch entity used to cool or heat depending on A/C mode.",
"target_sensor": "Temperature sensor that reflects the current temperature.",
"min_cycle_duration": "Set a minimum amount of time that the switch specified must be in its current state prior to being switched either off or on.",
"cold_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched on. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will start when the sensor equals or goes below 24.5.",
"cold_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched on. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will start when the sensor goes below 24.5.",
"hot_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched off. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will stop when the sensor equals or goes above 25.5."
}
},

View File

@ -1119,6 +1119,52 @@ async def test_precision(hass: HomeAssistant) -> None:
assert state.attributes.get("target_temp_step") == 0.1
@pytest.fixture(
params=[
HVACMode.HEAT,
HVACMode.COOL,
]
)
async def setup_comp_10(hass: HomeAssistant, request: pytest.FixtureRequest) -> None:
"""Initialize components."""
assert await async_setup_component(
hass,
CLIMATE_DOMAIN,
{
"climate": {
"platform": "generic_thermostat",
"name": "test",
"cold_tolerance": 0,
"hot_tolerance": 0,
"target_temp": 25,
"heater": ENT_SWITCH,
"target_sensor": ENT_SENSOR,
"initial_hvac_mode": request.param,
}
},
)
await hass.async_block_till_done()
@pytest.mark.usefixtures("setup_comp_10")
async def test_zero_tolerances(hass: HomeAssistant) -> None:
"""Test that having a zero tolerance doesn't cause the switch to flip-flop."""
# if the switch is off, it should remain off
calls = _setup_switch(hass, False)
_setup_sensor(hass, 25)
await hass.async_block_till_done()
await common.async_set_temperature(hass, 25)
assert len(calls) == 0
# if the switch is on, it should turn off
calls = _setup_switch(hass, True)
_setup_sensor(hass, 25)
await hass.async_block_till_done()
await common.async_set_temperature(hass, 25)
assert len(calls) == 1
async def test_custom_setup_params(hass: HomeAssistant) -> None:
"""Test the setup with custom parameters."""
result = await async_setup_component(