mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 09:47:13 +00:00
Ignore min_cycle_duration when manually controlling the thermostat. (#16128)
* Ignore min_cycle_duration when manually controlling the thermostat. * style * Generic thermostat: add minimum cycle duration to keep-alive tests. There was a bug in previous versions of the code, that would not execute the keep-alive action if the minimum cycle duration hasn't passed. This test verifies that the keep-alive action is executed correctly. * Generic thermostat: added tests to verify that changing the thermostat mode manually triggers the switch, regardless of minimum cycle duration. * Updated tests to use `common` module instead of the deprecated `climate`
This commit is contained in:
parent
e9ae862fca
commit
02b46e2ba3
@ -232,11 +232,11 @@ class GenericThermostat(ClimateDevice):
|
|||||||
if operation_mode == STATE_HEAT:
|
if operation_mode == STATE_HEAT:
|
||||||
self._current_operation = STATE_HEAT
|
self._current_operation = STATE_HEAT
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
await self._async_control_heating()
|
await self._async_control_heating(force=True)
|
||||||
elif operation_mode == STATE_COOL:
|
elif operation_mode == STATE_COOL:
|
||||||
self._current_operation = STATE_COOL
|
self._current_operation = STATE_COOL
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
await self._async_control_heating()
|
await self._async_control_heating(force=True)
|
||||||
elif operation_mode == STATE_OFF:
|
elif operation_mode == STATE_OFF:
|
||||||
self._current_operation = STATE_OFF
|
self._current_operation = STATE_OFF
|
||||||
self._enabled = False
|
self._enabled = False
|
||||||
@ -262,7 +262,7 @@ class GenericThermostat(ClimateDevice):
|
|||||||
if temperature is None:
|
if temperature is None:
|
||||||
return
|
return
|
||||||
self._target_temp = temperature
|
self._target_temp = temperature
|
||||||
await self._async_control_heating()
|
await self._async_control_heating(force=True)
|
||||||
await self.async_update_ha_state()
|
await self.async_update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -307,7 +307,7 @@ class GenericThermostat(ClimateDevice):
|
|||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
_LOGGER.error("Unable to update from sensor: %s", ex)
|
_LOGGER.error("Unable to update from sensor: %s", ex)
|
||||||
|
|
||||||
async def _async_control_heating(self, time=None):
|
async def _async_control_heating(self, time=None, force=False):
|
||||||
"""Check if we need to turn heating on or off."""
|
"""Check if we need to turn heating on or off."""
|
||||||
async with self._temp_lock:
|
async with self._temp_lock:
|
||||||
if not self._active and None not in (self._cur_temp,
|
if not self._active and None not in (self._cur_temp,
|
||||||
@ -320,16 +320,21 @@ class GenericThermostat(ClimateDevice):
|
|||||||
if not self._active or not self._enabled:
|
if not self._active or not self._enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.min_cycle_duration:
|
if not force and time is None:
|
||||||
if self._is_device_active:
|
# If the `force` argument is True, we
|
||||||
current_state = STATE_ON
|
# ignore `min_cycle_duration`.
|
||||||
else:
|
# If the `time` argument is not none, we were invoked for
|
||||||
current_state = STATE_OFF
|
# keep-alive purposes, and `min_cycle_duration` is irrelevant.
|
||||||
long_enough = condition.state(
|
if self.min_cycle_duration:
|
||||||
self.hass, self.heater_entity_id, current_state,
|
if self._is_device_active:
|
||||||
self.min_cycle_duration)
|
current_state = STATE_ON
|
||||||
if not long_enough:
|
else:
|
||||||
return
|
current_state = STATE_OFF
|
||||||
|
long_enough = condition.state(
|
||||||
|
self.hass, self.heater_entity_id, current_state,
|
||||||
|
self.min_cycle_duration)
|
||||||
|
if not long_enough:
|
||||||
|
return
|
||||||
|
|
||||||
too_cold = \
|
too_cold = \
|
||||||
self._target_temp - self._cur_temp >= self._cold_tolerance
|
self._target_temp - self._cur_temp >= self._cold_tolerance
|
||||||
@ -385,7 +390,7 @@ class GenericThermostat(ClimateDevice):
|
|||||||
self._is_away = True
|
self._is_away = True
|
||||||
self._saved_target_temp = self._target_temp
|
self._saved_target_temp = self._target_temp
|
||||||
self._target_temp = self._away_temp
|
self._target_temp = self._away_temp
|
||||||
await self._async_control_heating()
|
await self._async_control_heating(force=True)
|
||||||
await self.async_update_ha_state()
|
await self.async_update_ha_state()
|
||||||
|
|
||||||
async def async_turn_away_mode_off(self):
|
async def async_turn_away_mode_off(self):
|
||||||
@ -394,5 +399,5 @@ class GenericThermostat(ClimateDevice):
|
|||||||
return
|
return
|
||||||
self._is_away = False
|
self._is_away = False
|
||||||
self._target_temp = self._saved_target_temp
|
self._target_temp = self._saved_target_temp
|
||||||
await self._async_control_heating()
|
await self._async_control_heating(force=True)
|
||||||
await self.async_update_ha_state()
|
await self.async_update_ha_state()
|
||||||
|
@ -623,6 +623,38 @@ class TestClimateGenericThermostatACModeMinCycle(unittest.TestCase):
|
|||||||
assert SERVICE_TURN_OFF == call.service
|
assert SERVICE_TURN_OFF == call.service
|
||||||
assert ENT_SWITCH == call.data['entity_id']
|
assert ENT_SWITCH == call.data['entity_id']
|
||||||
|
|
||||||
|
def test_mode_change_ac_trigger_off_not_long_enough(self):
|
||||||
|
"""Test if mode change turns ac off despite minimum cycle."""
|
||||||
|
self._setup_switch(True)
|
||||||
|
common.set_temperature(self.hass, 30)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self._setup_sensor(25)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(0, len(self.calls))
|
||||||
|
common.set_operation_mode(self.hass, climate.STATE_OFF)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(1, len(self.calls))
|
||||||
|
call = self.calls[0]
|
||||||
|
self.assertEqual('homeassistant', call.domain)
|
||||||
|
self.assertEqual(SERVICE_TURN_OFF, call.service)
|
||||||
|
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
|
||||||
|
|
||||||
|
def test_mode_change_ac_trigger_on_not_long_enough(self):
|
||||||
|
"""Test if mode change turns ac on despite minimum cycle."""
|
||||||
|
self._setup_switch(False)
|
||||||
|
common.set_temperature(self.hass, 25)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self._setup_sensor(30)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(0, len(self.calls))
|
||||||
|
common.set_operation_mode(self.hass, climate.STATE_HEAT)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(1, len(self.calls))
|
||||||
|
call = self.calls[0]
|
||||||
|
self.assertEqual('homeassistant', call.domain)
|
||||||
|
self.assertEqual(SERVICE_TURN_ON, call.service)
|
||||||
|
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
|
||||||
|
|
||||||
def _setup_sensor(self, temp):
|
def _setup_sensor(self, temp):
|
||||||
"""Set up the test sensor."""
|
"""Set up the test sensor."""
|
||||||
self.hass.states.set(ENT_SENSOR, temp)
|
self.hass.states.set(ENT_SENSOR, temp)
|
||||||
@ -714,6 +746,38 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase):
|
|||||||
assert SERVICE_TURN_OFF == call.service
|
assert SERVICE_TURN_OFF == call.service
|
||||||
assert ENT_SWITCH == call.data['entity_id']
|
assert ENT_SWITCH == call.data['entity_id']
|
||||||
|
|
||||||
|
def test_mode_change_heater_trigger_off_not_long_enough(self):
|
||||||
|
"""Test if mode change turns heater off despite minimum cycle."""
|
||||||
|
self._setup_switch(True)
|
||||||
|
common.set_temperature(self.hass, 25)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self._setup_sensor(30)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(0, len(self.calls))
|
||||||
|
common.set_operation_mode(self.hass, climate.STATE_OFF)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(1, len(self.calls))
|
||||||
|
call = self.calls[0]
|
||||||
|
self.assertEqual('homeassistant', call.domain)
|
||||||
|
self.assertEqual(SERVICE_TURN_OFF, call.service)
|
||||||
|
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
|
||||||
|
|
||||||
|
def test_mode_change_heater_trigger_on_not_long_enough(self):
|
||||||
|
"""Test if mode change turns heater on despite minimum cycle."""
|
||||||
|
self._setup_switch(False)
|
||||||
|
common.set_temperature(self.hass, 30)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self._setup_sensor(25)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(0, len(self.calls))
|
||||||
|
common.set_operation_mode(self.hass, climate.STATE_HEAT)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
self.assertEqual(1, len(self.calls))
|
||||||
|
call = self.calls[0]
|
||||||
|
self.assertEqual('homeassistant', call.domain)
|
||||||
|
self.assertEqual(SERVICE_TURN_ON, call.service)
|
||||||
|
self.assertEqual(ENT_SWITCH, call.data['entity_id'])
|
||||||
|
|
||||||
def _setup_sensor(self, temp):
|
def _setup_sensor(self, temp):
|
||||||
"""Set up the test sensor."""
|
"""Set up the test sensor."""
|
||||||
self.hass.states.set(ENT_SENSOR, temp)
|
self.hass.states.set(ENT_SENSOR, temp)
|
||||||
@ -748,6 +812,7 @@ class TestClimateGenericThermostatACKeepAlive(unittest.TestCase):
|
|||||||
'target_temp': 25,
|
'target_temp': 25,
|
||||||
'target_sensor': ENT_SENSOR,
|
'target_sensor': ENT_SENSOR,
|
||||||
'ac_mode': True,
|
'ac_mode': True,
|
||||||
|
'min_cycle_duration': datetime.timedelta(minutes=15),
|
||||||
'keep_alive': datetime.timedelta(minutes=10)
|
'keep_alive': datetime.timedelta(minutes=10)
|
||||||
}})
|
}})
|
||||||
|
|
||||||
@ -838,6 +903,7 @@ class TestClimateGenericThermostatKeepAlive(unittest.TestCase):
|
|||||||
'target_temp': 25,
|
'target_temp': 25,
|
||||||
'heater': ENT_SWITCH,
|
'heater': ENT_SWITCH,
|
||||||
'target_sensor': ENT_SENSOR,
|
'target_sensor': ENT_SENSOR,
|
||||||
|
'min_cycle_duration': datetime.timedelta(minutes=15),
|
||||||
'keep_alive': datetime.timedelta(minutes=10)
|
'keep_alive': datetime.timedelta(minutes=10)
|
||||||
}})
|
}})
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user