Fix generic thermostat switch state initialization (#56073)
This commit is contained in:
parent
c869b78ac1
commit
8d87f4148b
2 changed files with 83 additions and 12 deletions
|
@ -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(
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue