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
2 changed files with 87 additions and 16 deletions
|
@ -232,11 +232,11 @@ class GenericThermostat(ClimateDevice):
|
|||
if operation_mode == STATE_HEAT:
|
||||
self._current_operation = STATE_HEAT
|
||||
self._enabled = True
|
||||
await self._async_control_heating()
|
||||
await self._async_control_heating(force=True)
|
||||
elif operation_mode == STATE_COOL:
|
||||
self._current_operation = STATE_COOL
|
||||
self._enabled = True
|
||||
await self._async_control_heating()
|
||||
await self._async_control_heating(force=True)
|
||||
elif operation_mode == STATE_OFF:
|
||||
self._current_operation = STATE_OFF
|
||||
self._enabled = False
|
||||
|
@ -262,7 +262,7 @@ class GenericThermostat(ClimateDevice):
|
|||
if temperature is None:
|
||||
return
|
||||
self._target_temp = temperature
|
||||
await self._async_control_heating()
|
||||
await self._async_control_heating(force=True)
|
||||
await self.async_update_ha_state()
|
||||
|
||||
@property
|
||||
|
@ -307,7 +307,7 @@ class GenericThermostat(ClimateDevice):
|
|||
except ValueError as 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."""
|
||||
async with self._temp_lock:
|
||||
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:
|
||||
return
|
||||
|
||||
if self.min_cycle_duration:
|
||||
if self._is_device_active:
|
||||
current_state = STATE_ON
|
||||
else:
|
||||
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
|
||||
if not force and time is None:
|
||||
# If the `force` argument is True, we
|
||||
# ignore `min_cycle_duration`.
|
||||
# If the `time` argument is not none, we were invoked for
|
||||
# keep-alive purposes, and `min_cycle_duration` is irrelevant.
|
||||
if self.min_cycle_duration:
|
||||
if self._is_device_active:
|
||||
current_state = STATE_ON
|
||||
else:
|
||||
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 = \
|
||||
self._target_temp - self._cur_temp >= self._cold_tolerance
|
||||
|
@ -385,7 +390,7 @@ class GenericThermostat(ClimateDevice):
|
|||
self._is_away = True
|
||||
self._saved_target_temp = self._target_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()
|
||||
|
||||
async def async_turn_away_mode_off(self):
|
||||
|
@ -394,5 +399,5 @@ class GenericThermostat(ClimateDevice):
|
|||
return
|
||||
self._is_away = False
|
||||
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()
|
||||
|
|
|
@ -623,6 +623,38 @@ class TestClimateGenericThermostatACModeMinCycle(unittest.TestCase):
|
|||
assert SERVICE_TURN_OFF == call.service
|
||||
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):
|
||||
"""Set up the test sensor."""
|
||||
self.hass.states.set(ENT_SENSOR, temp)
|
||||
|
@ -714,6 +746,38 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase):
|
|||
assert SERVICE_TURN_OFF == call.service
|
||||
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):
|
||||
"""Set up the test sensor."""
|
||||
self.hass.states.set(ENT_SENSOR, temp)
|
||||
|
@ -748,6 +812,7 @@ class TestClimateGenericThermostatACKeepAlive(unittest.TestCase):
|
|||
'target_temp': 25,
|
||||
'target_sensor': ENT_SENSOR,
|
||||
'ac_mode': True,
|
||||
'min_cycle_duration': datetime.timedelta(minutes=15),
|
||||
'keep_alive': datetime.timedelta(minutes=10)
|
||||
}})
|
||||
|
||||
|
@ -838,6 +903,7 @@ class TestClimateGenericThermostatKeepAlive(unittest.TestCase):
|
|||
'target_temp': 25,
|
||||
'heater': ENT_SWITCH,
|
||||
'target_sensor': ENT_SENSOR,
|
||||
'min_cycle_duration': datetime.timedelta(minutes=15),
|
||||
'keep_alive': datetime.timedelta(minutes=10)
|
||||
}})
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue