Wait for switch startup in generic_thermostat (#45253)
* Better status control on restore * Better status control on restore * fix code coverage * Rollback hvac_mode initialization I think I have better understood the handling of the `hvac_mode`. I change the approach. Now the thermostat doesn't initialize until the switch is available. * fix pyupgrade * fix black * Delete test_turn_on_while_restarting HVAC mode should not be modified by the switch. IMHO, this test does not make sense because if the switch is turned on the thermostat is not turning on (and not changing HVAC_MODE) * Re add turn off if HVAC is off If HVAC_MODE is off thermostat will not control heater switch. This can be because `initial_hvac_mode`, because state defaults to or because old_state. IMHO it is preferable to be excessively cautious. * Update climate.py * Change warning message * Fix black * Fix black
This commit is contained in:
parent
c820dd4cb5
commit
e798f415a4
2 changed files with 102 additions and 1 deletions
|
@ -266,6 +266,14 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||
if not self._hvac_mode:
|
||||
self._hvac_mode = HVAC_MODE_OFF
|
||||
|
||||
# Prevent the device from keep running if HVAC_MODE_OFF
|
||||
if self._hvac_mode == HVAC_MODE_OFF and self._is_device_active:
|
||||
await self._async_heater_turn_off()
|
||||
_LOGGER.warning(
|
||||
"The climate mode is OFF, but the switch device is ON. Turning off device %s",
|
||||
self.heater_entity_id,
|
||||
)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling state."""
|
||||
|
@ -418,7 +426,11 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||
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, self._target_temp):
|
||||
if not self._active and None not in (
|
||||
self._cur_temp,
|
||||
self._target_temp,
|
||||
self._is_device_active,
|
||||
):
|
||||
self._active = True
|
||||
_LOGGER.info(
|
||||
"Obtained current and target temperature. "
|
||||
|
@ -480,6 +492,9 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||
@property
|
||||
def _is_device_active(self):
|
||||
"""If the toggleable device is currently active."""
|
||||
if not self.hass.states.get(self.heater_entity_id):
|
||||
return None
|
||||
|
||||
return self.hass.states.is_state(self.heater_entity_id, STATE_ON)
|
||||
|
||||
@property
|
||||
|
|
|
@ -1249,6 +1249,92 @@ async def test_no_restore_state(hass):
|
|||
assert state.state == HVAC_MODE_OFF
|
||||
|
||||
|
||||
async def test_initial_hvac_off_force_heater_off(hass):
|
||||
"""Ensure that restored state is coherent with real situation.
|
||||
|
||||
'initial_hvac_mode: off' will force HVAC status, but we must be sure
|
||||
that heater don't keep on.
|
||||
"""
|
||||
# switch is on
|
||||
calls = _setup_switch(hass, True)
|
||||
assert hass.states.get(ENT_SWITCH).state == STATE_ON
|
||||
|
||||
_setup_sensor(hass, 16)
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
"climate": {
|
||||
"platform": "generic_thermostat",
|
||||
"name": "test_thermostat",
|
||||
"heater": ENT_SWITCH,
|
||||
"target_sensor": ENT_SENSOR,
|
||||
"target_temp": 20,
|
||||
"initial_hvac_mode": HVAC_MODE_OFF,
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("climate.test_thermostat")
|
||||
# 'initial_hvac_mode' will force state but must prevent heather keep working
|
||||
assert state.state == HVAC_MODE_OFF
|
||||
# heater must be switched off
|
||||
assert len(calls) == 1
|
||||
call = calls[0]
|
||||
assert call.domain == HASS_DOMAIN
|
||||
assert call.service == SERVICE_TURN_OFF
|
||||
assert call.data["entity_id"] == ENT_SWITCH
|
||||
|
||||
|
||||
async def test_restore_will_turn_off_(hass):
|
||||
"""Ensure that restored state is coherent with real situation.
|
||||
|
||||
Thermostat status must trigger heater event if temp raises the target .
|
||||
"""
|
||||
heater_switch = "input_boolean.test"
|
||||
mock_restore_cache(
|
||||
hass,
|
||||
(
|
||||
State(
|
||||
"climate.test_thermostat",
|
||||
HVAC_MODE_HEAT,
|
||||
{ATTR_TEMPERATURE: "18", ATTR_PRESET_MODE: PRESET_NONE},
|
||||
),
|
||||
State(heater_switch, STATE_ON, {}),
|
||||
),
|
||||
)
|
||||
|
||||
hass.state = CoreState.starting
|
||||
|
||||
assert await async_setup_component(
|
||||
hass, input_boolean.DOMAIN, {"input_boolean": {"test": None}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(heater_switch).state == STATE_ON
|
||||
|
||||
_setup_sensor(hass, 22)
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
"climate": {
|
||||
"platform": "generic_thermostat",
|
||||
"name": "test_thermostat",
|
||||
"heater": heater_switch,
|
||||
"target_sensor": ENT_SENSOR,
|
||||
"target_temp": 20,
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("climate.test_thermostat")
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 20
|
||||
assert state.state == HVAC_MODE_HEAT
|
||||
assert hass.states.get(heater_switch).state == STATE_ON
|
||||
|
||||
|
||||
async def test_restore_state_uncoherence_case(hass):
|
||||
"""
|
||||
Test restore from a strange state.
|
||||
|
|
Loading…
Add table
Reference in a new issue