Suppress domain and all listeners during template rate limit (#42005)
parent
344514601d
commit
3a9b2392f8
|
@ -1,5 +1,6 @@
|
|||
"""Helpers for listening to events."""
|
||||
import asyncio
|
||||
import copy
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
import functools as ft
|
||||
|
@ -820,6 +821,8 @@ class _TrackTemplateResultInfo:
|
|||
if not _event_triggers_rerender(event, info):
|
||||
return False
|
||||
|
||||
had_timer = self._rate_limit.async_has_timer(template)
|
||||
|
||||
if self._rate_limit.async_schedule_action(
|
||||
template,
|
||||
_rate_limit_for_event(event, info, track_template_),
|
||||
|
@ -829,7 +832,7 @@ class _TrackTemplateResultInfo:
|
|||
(track_template_,),
|
||||
True,
|
||||
):
|
||||
return False
|
||||
return not had_timer
|
||||
|
||||
_LOGGER.debug(
|
||||
"Template update %s triggered by event: %s",
|
||||
|
@ -893,7 +896,14 @@ class _TrackTemplateResultInfo:
|
|||
if info_changed:
|
||||
assert self._track_state_changes
|
||||
self._track_state_changes.async_update_listeners(
|
||||
_render_infos_to_track_states(self._info.values()),
|
||||
_render_infos_to_track_states(
|
||||
[
|
||||
_suppress_domain_all_in_render_info(self._info[template])
|
||||
if self._rate_limit.async_has_timer(template)
|
||||
else self._info[template]
|
||||
for template in self._info
|
||||
]
|
||||
)
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"Template group %s listens for %s",
|
||||
|
@ -1458,3 +1468,13 @@ def _rate_limit_for_event(
|
|||
|
||||
rate_limit: Optional[timedelta] = info.rate_limit
|
||||
return rate_limit
|
||||
|
||||
|
||||
def _suppress_domain_all_in_render_info(render_info: RenderInfo) -> RenderInfo:
|
||||
"""Remove the domains and all_states from render info during a ratelimit."""
|
||||
rate_limited_render_info = copy.copy(render_info)
|
||||
rate_limited_render_info.all_states = False
|
||||
rate_limited_render_info.all_states_lifecycle = False
|
||||
rate_limited_render_info.domains = set()
|
||||
rate_limited_render_info.domains_lifecycle = set()
|
||||
return rate_limited_render_info
|
||||
|
|
|
@ -1494,6 +1494,71 @@ async def test_track_template_rate_limit(hass):
|
|||
assert refresh_runs == [0, 1, 2, 4]
|
||||
|
||||
|
||||
async def test_track_template_rate_limit_suppress_listener(hass):
|
||||
"""Test template rate limit will suppress the listener during the rate limit."""
|
||||
template_refresh = Template("{{ states | count }}", hass)
|
||||
|
||||
refresh_runs = []
|
||||
|
||||
@ha.callback
|
||||
def refresh_listener(event, updates):
|
||||
refresh_runs.append(updates.pop().result)
|
||||
|
||||
info = async_track_template_result(
|
||||
hass,
|
||||
[TrackTemplate(template_refresh, None, timedelta(seconds=0.1))],
|
||||
refresh_listener,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
info.async_refresh()
|
||||
|
||||
assert info.listeners == {"all": True, "domains": set(), "entities": set()}
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert refresh_runs == [0]
|
||||
hass.states.async_set("sensor.one", "any")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == [0]
|
||||
info.async_refresh()
|
||||
assert refresh_runs == [0, 1]
|
||||
hass.states.async_set("sensor.two", "any")
|
||||
await hass.async_block_till_done()
|
||||
# Should be suppressed during the rate limit
|
||||
assert info.listeners == {"all": False, "domains": set(), "entities": set()}
|
||||
assert refresh_runs == [0, 1]
|
||||
next_time = dt_util.utcnow() + timedelta(seconds=0.125)
|
||||
with patch(
|
||||
"homeassistant.helpers.ratelimit.dt_util.utcnow", return_value=next_time
|
||||
):
|
||||
async_fire_time_changed(hass, next_time)
|
||||
await hass.async_block_till_done()
|
||||
# Rate limit released and the all listener returns
|
||||
assert info.listeners == {"all": True, "domains": set(), "entities": set()}
|
||||
assert refresh_runs == [0, 1, 2]
|
||||
hass.states.async_set("sensor.three", "any")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == [0, 1, 2]
|
||||
hass.states.async_set("sensor.four", "any")
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_runs == [0, 1, 2]
|
||||
# Rate limit hit and the all listener is shut off
|
||||
assert info.listeners == {"all": False, "domains": set(), "entities": set()}
|
||||
next_time = dt_util.utcnow() + timedelta(seconds=0.125 * 2)
|
||||
with patch(
|
||||
"homeassistant.helpers.ratelimit.dt_util.utcnow", return_value=next_time
|
||||
):
|
||||
async_fire_time_changed(hass, next_time)
|
||||
await hass.async_block_till_done()
|
||||
# Rate limit released and the all listener returns
|
||||
assert info.listeners == {"all": True, "domains": set(), "entities": set()}
|
||||
assert refresh_runs == [0, 1, 2, 4]
|
||||
hass.states.async_set("sensor.five", "any")
|
||||
await hass.async_block_till_done()
|
||||
# Rate limit hit and the all listener is shut off
|
||||
assert info.listeners == {"all": False, "domains": set(), "entities": set()}
|
||||
assert refresh_runs == [0, 1, 2, 4]
|
||||
|
||||
|
||||
async def test_track_template_rate_limit_five(hass):
|
||||
"""Test template rate limit of 5 seconds."""
|
||||
template_refresh = Template("{{ states | count }}", hass)
|
||||
|
|
Loading…
Reference in New Issue