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
This commit is contained in:
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, DOMAIN as HOMEASSISTANT_DOMAIN,
Event, Event,
EventStateChangedData, EventStateChangedData,
EventStateReportedData,
HomeAssistant, HomeAssistant,
State, State,
callback, 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.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_state_change_event, async_track_state_change_event,
async_track_state_report_event,
async_track_time_interval, async_track_time_interval,
) )
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
@ -72,7 +74,6 @@ _LOGGER = logging.getLogger(__name__)
ATTR_SAVED_HUMIDITY = "saved_humidity" ATTR_SAVED_HUMIDITY = "saved_humidity"
PLATFORM_SCHEMA = HUMIDIFIER_PLATFORM_SCHEMA.extend(HYGROSTAT_SCHEMA.schema) PLATFORM_SCHEMA = HUMIDIFIER_PLATFORM_SCHEMA.extend(HYGROSTAT_SCHEMA.schema)
@ -222,18 +223,21 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
"""Run when entity about to be added.""" """Run when entity about to be added."""
await super().async_added_to_hass() await super().async_added_to_hass()
# Add listener
self.async_on_remove( self.async_on_remove(
async_track_state_change_event( 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( self.async_on_remove(
async_track_state_change_event( 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: if self._keep_alive:
self.async_on_remove( self.async_on_remove(
async_track_time_interval( async_track_time_interval(
@ -253,7 +257,8 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
sensor_state.state if sensor_state is not None else "None", sensor_state.state if sensor_state is not None else "None",
) )
return 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) 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 # Get default humidity from super class
return super().max_humidity return super().max_humidity
async def _async_sensor_changed_event( async def _async_sensor_event(
self, event: Event[EventStateChangedData] self, event: Event[EventStateChangedData] | Event[EventStateReportedData]
) -> 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
) -> None: ) -> None:
"""Handle ambient humidity changes.""" """Handle ambient humidity changes."""
new_state = event.data["new_state"]
if new_state is None: if new_state is None:
return 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._sensor_stale_duration:
if self._remove_stale_tracking: if self._remove_stale_tracking:
self._remove_stale_tracking() self._remove_stale_tracking()
self._remove_stale_tracking = async_track_time_interval( self._remove_stale_tracking = async_track_time_interval(
self.hass, self.hass,
self._async_sensor_not_responding, self._async_sensor_not_responding,
@ -426,23 +429,18 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity):
state = self.hass.states.get(self._sensor_entity_id) state = self.hass.states.get(self._sensor_entity_id)
_LOGGER.debug( _LOGGER.debug(
"Sensor has not been updated for %s", "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") _LOGGER.warning("Sensor is stalled, call the emergency stop")
await self._async_update_humidity("Stalled") await self._async_update_humidity("Stalled")
@callback @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.""" """Handle humidifier switch state changes."""
data = event.data self._async_switch_changed(event.data["new_state"])
self._async_switch_changed(
data["entity_id"], data["old_state"], data["new_state"]
)
@callback @callback
def _async_switch_changed( def _async_switch_changed(self, new_state: State | None) -> None:
self, entity_id: str, old_state: State | None, new_state: State | None
) -> None:
"""Handle humidifier switch state changes.""" """Handle humidifier switch state changes."""
if new_state is None: if new_state is None:
return return

View File

@ -3,6 +3,7 @@
import datetime import datetime
from freezegun import freeze_time from freezegun import freeze_time
from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
import voluptuous as vol 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) calls = await _setup_switch(hass, False)
_setup_sensor(hass, 36) _setup_sensor(hass, 36)
await hass.async_block_till_done() await hass.async_block_till_done()
calls.clear()
await hass.services.async_call( await hass.services.async_call(
DOMAIN, DOMAIN,
SERVICE_SET_HUMIDITY, SERVICE_SET_HUMIDITY,
@ -540,6 +542,7 @@ async def test_set_target_humidity_humidifier_off(hass: HomeAssistant) -> None:
calls = await _setup_switch(hass, True) calls = await _setup_switch(hass, True)
_setup_sensor(hass, 45) _setup_sensor(hass, 45)
await hass.async_block_till_done() await hass.async_block_till_done()
calls.clear()
await hass.services.async_call( await hass.services.async_call(
DOMAIN, DOMAIN,
SERVICE_SET_HUMIDITY, SERVICE_SET_HUMIDITY,
@ -1733,7 +1736,9 @@ async def test_away_fixed_humidity_mode(hass: HomeAssistant) -> None:
@pytest.mark.usefixtures("setup_comp_1") @pytest.mark.usefixtures("setup_comp_1")
async def test_sensor_stale_duration( async def test_sensor_stale_duration(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
freezer: FrozenDateTimeFactory,
) -> None: ) -> None:
"""Test turn off on sensor stale.""" """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 assert hass.states.get(humidifier_switch).state == STATE_ON
# Wait 11 minutes # 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() await hass.async_block_till_done()
# 11 minutes later, no news from the sensor : emergency cut off # 11 minutes later, no news from the sensor : emergency cut off
assert hass.states.get(humidifier_switch).state == STATE_OFF assert hass.states.get(humidifier_switch).state == STATE_OFF
assert "emergency" in caplog.text 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) _setup_sensor(hass, 24)
await hass.async_block_till_done() await hass.async_block_till_done()