mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Fix generic thermostat switch state initialization (#56073)
This commit is contained in:
parent
c869b78ac1
commit
8d87f4148b
@ -227,6 +227,12 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
):
|
):
|
||||||
self._async_update_temp(sensor_state)
|
self._async_update_temp(sensor_state)
|
||||||
self.async_write_ha_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:
|
if self.hass.state == CoreState.running:
|
||||||
_async_startup()
|
_async_startup()
|
||||||
@ -270,14 +276,6 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if not self._hvac_mode:
|
if not self._hvac_mode:
|
||||||
self._hvac_mode = HVAC_MODE_OFF
|
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
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
"""Return the polling state."""
|
"""Return the polling state."""
|
||||||
@ -401,12 +399,24 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
await self._async_control_heating()
|
await self._async_control_heating()
|
||||||
self.async_write_ha_state()
|
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
|
@callback
|
||||||
def _async_switch_changed(self, event):
|
def _async_switch_changed(self, event):
|
||||||
"""Handle heater switch state changes."""
|
"""Handle heater switch state changes."""
|
||||||
new_state = event.data.get("new_state")
|
new_state = event.data.get("new_state")
|
||||||
|
old_state = event.data.get("old_state")
|
||||||
if new_state is None:
|
if new_state is None:
|
||||||
return
|
return
|
||||||
|
if old_state is None:
|
||||||
|
self.hass.create_task(self._check_switch_initial_state())
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -426,7 +436,6 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if not self._active and None not in (
|
if not self._active and None not in (
|
||||||
self._cur_temp,
|
self._cur_temp,
|
||||||
self._target_temp,
|
self._target_temp,
|
||||||
self._is_device_active,
|
|
||||||
):
|
):
|
||||||
self._active = True
|
self._active = True
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
|
@ -42,6 +42,7 @@ from homeassistant.util.unit_system import METRIC_SYSTEM
|
|||||||
from tests.common import (
|
from tests.common import (
|
||||||
assert_setup_component,
|
assert_setup_component,
|
||||||
async_fire_time_changed,
|
async_fire_time_changed,
|
||||||
|
async_mock_service,
|
||||||
mock_restore_cache,
|
mock_restore_cache,
|
||||||
)
|
)
|
||||||
from tests.components.climate import common
|
from tests.components.climate import common
|
||||||
@ -1189,14 +1190,15 @@ async def test_custom_setup_params(hass):
|
|||||||
assert state.attributes.get("temperature") == TARGET_TEMP
|
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."""
|
"""Ensure states are restored on startup."""
|
||||||
mock_restore_cache(
|
mock_restore_cache(
|
||||||
hass,
|
hass,
|
||||||
(
|
(
|
||||||
State(
|
State(
|
||||||
"climate.test_thermostat",
|
"climate.test_thermostat",
|
||||||
HVAC_MODE_OFF,
|
hvac_mode,
|
||||||
{ATTR_TEMPERATURE: "20", ATTR_PRESET_MODE: PRESET_AWAY},
|
{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")
|
state = hass.states.get("climate.test_thermostat")
|
||||||
assert state.attributes[ATTR_TEMPERATURE] == 20
|
assert state.attributes[ATTR_TEMPERATURE] == 20
|
||||||
assert state.attributes[ATTR_PRESET_MODE] == PRESET_AWAY
|
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):
|
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
|
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):
|
async def test_restore_state_uncoherence_case(hass):
|
||||||
"""
|
"""
|
||||||
Test restore from a strange state.
|
Test restore from a strange state.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user