Adjust generic hygrostat to detect reported events for stale tracking (#124109)

* Listen to reported events for stale check

* Always enable stale sensor tracking

There is no reason not to have this enabled
now that we track reported events for sensors.

* Remove default stale code

* Adjust for ruff change
pull/125006/head^2
Joakim Plate 2024-09-08 12:51:08 +02:00 committed by GitHub
parent e7cb646a58
commit 3139a7e431
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 29 deletions

View File

@ -36,6 +36,7 @@ from homeassistant.core import (
DOMAIN as HOMEASSISTANT_DOMAIN,
Event,
EventStateChangedData,
EventStateReportedData,
HomeAssistant,
State,
callback,
@ -45,6 +46,7 @@ from homeassistant.helpers.device import async_device_info_to_link_from_entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import (
async_track_state_change_event,
async_track_state_report_event,
async_track_time_interval,
)
from homeassistant.helpers.restore_state import RestoreEntity
@ -72,7 +74,6 @@ _LOGGER = logging.getLogger(__name__)
ATTR_SAVED_HUMIDITY = "saved_humidity"
PLATFORM_SCHEMA = HUMIDIFIER_PLATFORM_SCHEMA.extend(HYGROSTAT_SCHEMA.schema)
@ -222,18 +223,21 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
"""Run when entity about to be added."""
await super().async_added_to_hass()
# Add listener
self.async_on_remove(
async_track_state_change_event(
self.hass, self._sensor_entity_id, self._async_sensor_changed_event
self.hass, self._sensor_entity_id, self._async_sensor_event
)
)
self.async_on_remove(
async_track_state_report_event(
self.hass, self._sensor_entity_id, self._async_sensor_event
)
)
self.async_on_remove(
async_track_state_change_event(
self.hass, self._switch_entity_id, self._async_switch_changed_event
self.hass, self._switch_entity_id, self._async_switch_event
)
)
if self._keep_alive:
self.async_on_remove(
async_track_time_interval(
@ -253,7 +257,8 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
sensor_state.state if sensor_state is not None else "None",
)
return
await self._async_sensor_changed(self._sensor_entity_id, None, sensor_state)
await self._async_sensor_update(sensor_state)
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _async_startup)
@ -391,25 +396,23 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
# Get default humidity from super class
return super().max_humidity
async def _async_sensor_changed_event(
self, event: Event[EventStateChangedData]
) -> None:
"""Handle ambient humidity changes."""
data = event.data
await self._async_sensor_changed(
data["entity_id"], data["old_state"], data["new_state"]
)
async def _async_sensor_changed(
self, entity_id: str, old_state: State | None, new_state: State | None
async def _async_sensor_event(
self, event: Event[EventStateChangedData] | Event[EventStateReportedData]
) -> None:
"""Handle ambient humidity changes."""
new_state = event.data["new_state"]
if new_state is None:
return
await self._async_sensor_update(new_state)
async def _async_sensor_update(self, new_state: State) -> None:
"""Update state based on humidity sensor."""
if self._sensor_stale_duration:
if self._remove_stale_tracking:
self._remove_stale_tracking()
self._remove_stale_tracking = async_track_time_interval(
self.hass,
self._async_sensor_not_responding,
@ -426,23 +429,18 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
state = self.hass.states.get(self._sensor_entity_id)
_LOGGER.debug(
"Sensor has not been updated for %s",
now - state.last_updated if now and state else "---",
now - state.last_reported if now and state else "---",
)
_LOGGER.warning("Sensor is stalled, call the emergency stop")
await self._async_update_humidity("Stalled")
@callback
def _async_switch_changed_event(self, event: Event[EventStateChangedData]) -> None:
def _async_switch_event(self, event: Event[EventStateChangedData]) -> None:
"""Handle humidifier switch state changes."""
data = event.data
self._async_switch_changed(
data["entity_id"], data["old_state"], data["new_state"]
)
self._async_switch_changed(event.data["new_state"])
@callback
def _async_switch_changed(
self, entity_id: str, old_state: State | None, new_state: State | None
) -> None:
def _async_switch_changed(self, new_state: State | None) -> None:
"""Handle humidifier switch state changes."""
if new_state is None:
return

View File

@ -3,6 +3,7 @@
import datetime
from freezegun import freeze_time
from freezegun.api import FrozenDateTimeFactory
import pytest
import voluptuous as vol
@ -520,6 +521,7 @@ async def test_set_target_humidity_humidifier_on(hass: HomeAssistant) -> None:
calls = await _setup_switch(hass, False)
_setup_sensor(hass, 36)
await hass.async_block_till_done()
calls.clear()
await hass.services.async_call(
DOMAIN,
SERVICE_SET_HUMIDITY,
@ -540,6 +542,7 @@ async def test_set_target_humidity_humidifier_off(hass: HomeAssistant) -> None:
calls = await _setup_switch(hass, True)
_setup_sensor(hass, 45)
await hass.async_block_till_done()
calls.clear()
await hass.services.async_call(
DOMAIN,
SERVICE_SET_HUMIDITY,
@ -1733,7 +1736,9 @@ async def test_away_fixed_humidity_mode(hass: HomeAssistant) -> None:
@pytest.mark.usefixtures("setup_comp_1")
async def test_sensor_stale_duration(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test turn off on sensor stale."""
@ -1775,14 +1780,31 @@ async def test_sensor_stale_duration(
assert hass.states.get(humidifier_switch).state == STATE_ON
# Wait 11 minutes
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(minutes=11))
freezer.tick(datetime.timedelta(minutes=11))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# 11 minutes later, no news from the sensor : emergency cut off
assert hass.states.get(humidifier_switch).state == STATE_OFF
assert "emergency" in caplog.text
# Updated value from sensor received
# Updated value from sensor received (same value)
_setup_sensor(hass, 23)
await hass.async_block_till_done()
# A new value has arrived, the humidifier should go ON
assert hass.states.get(humidifier_switch).state == STATE_ON
# Wait 11 minutes
freezer.tick(datetime.timedelta(minutes=11))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# 11 minutes later, no news from the sensor : emergency cut off
assert hass.states.get(humidifier_switch).state == STATE_OFF
assert "emergency" in caplog.text
# Updated value from sensor received (new value)
_setup_sensor(hass, 24)
await hass.async_block_till_done()