Fix generic thermostat switch state initialization (#56073)

This commit is contained in:
Brian Egge 2021-09-13 16:27:06 -04:00 committed by GitHub
parent c869b78ac1
commit 8d87f4148b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 12 deletions

View file

@ -227,6 +227,12 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
):
self._async_update_temp(sensor_state)
self.async_write_ha_state()
switch_state = self.hass.states.get(self.heater_entity_id)
if switch_state and switch_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self.hass.create_task(self._check_switch_initial_state())
if self.hass.state == CoreState.running:
_async_startup()
@ -270,14 +276,6 @@ 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."""
@ -401,12 +399,24 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
await self._async_control_heating()
self.async_write_ha_state()
async def _check_switch_initial_state(self):
"""Prevent the device from keep running if HVAC_MODE_OFF."""
if self._hvac_mode == HVAC_MODE_OFF and self._is_device_active:
_LOGGER.warning(
"The climate mode is OFF, but the switch device is ON. Turning off device %s",
self.heater_entity_id,
)
await self._async_heater_turn_off()
@callback
def _async_switch_changed(self, event):
"""Handle heater switch state changes."""
new_state = event.data.get("new_state")
old_state = event.data.get("old_state")
if new_state is None:
return
if old_state is None:
self.hass.create_task(self._check_switch_initial_state())
self.async_write_ha_state()
@callback
@ -426,7 +436,6 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
if not self._active and None not in (
self._cur_temp,
self._target_temp,
self._is_device_active,
):
self._active = True
_LOGGER.info(

View file

@ -42,6 +42,7 @@ from homeassistant.util.unit_system import METRIC_SYSTEM
from tests.common import (
assert_setup_component,
async_fire_time_changed,
async_mock_service,
mock_restore_cache,
)
from tests.components.climate import common
@ -1189,14 +1190,15 @@ async def test_custom_setup_params(hass):
assert state.attributes.get("temperature") == TARGET_TEMP
async def test_restore_state(hass):
@pytest.mark.parametrize("hvac_mode", [HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL])
async def test_restore_state(hass, hvac_mode):
"""Ensure states are restored on startup."""
mock_restore_cache(
hass,
(
State(
"climate.test_thermostat",
HVAC_MODE_OFF,
hvac_mode,
{ATTR_TEMPERATURE: "20", ATTR_PRESET_MODE: PRESET_AWAY},
),
),
@ -1221,7 +1223,7 @@ async def test_restore_state(hass):
state = hass.states.get("climate.test_thermostat")
assert state.attributes[ATTR_TEMPERATURE] == 20
assert state.attributes[ATTR_PRESET_MODE] == PRESET_AWAY
assert state.state == HVAC_MODE_OFF
assert state.state == hvac_mode
async def test_no_restore_state(hass):
@ -1347,6 +1349,66 @@ async def test_restore_will_turn_off_(hass):
assert hass.states.get(heater_switch).state == STATE_ON
async def test_restore_will_turn_off_when_loaded_second(hass):
"""Ensure that restored state is coherent with real situation.
Switch is not available until after component is loaded
"""
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
await hass.async_block_till_done()
assert hass.states.get(heater_switch) is None
_setup_sensor(hass, 16)
await async_setup_component(
hass,
DOMAIN,
{
"climate": {
"platform": "generic_thermostat",
"name": "test_thermostat",
"heater": heater_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")
assert state.attributes[ATTR_TEMPERATURE] == 20
assert state.state == HVAC_MODE_OFF
calls_on = async_mock_service(hass, ha.DOMAIN, SERVICE_TURN_ON)
calls_off = async_mock_service(hass, ha.DOMAIN, SERVICE_TURN_OFF)
assert await async_setup_component(
hass, input_boolean.DOMAIN, {"input_boolean": {"test": None}}
)
await hass.async_block_till_done()
# heater must be switched off
assert len(calls_on) == 0
assert len(calls_off) == 1
call = calls_off[0]
assert call.domain == HASS_DOMAIN
assert call.service == SERVICE_TURN_OFF
assert call.data["entity_id"] == "input_boolean.test"
async def test_restore_state_uncoherence_case(hass):
"""
Test restore from a strange state.