Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions homeassistant/helpers/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,15 +749,15 @@ def async_setup(self, raise_on_template_error: bool) -> None:
for track_template_ in self._track_templates:
template = track_template_.template
variables = track_template_.variables
self._info[template] = info = template.async_render_to_info(variables)

self._info[template] = template.async_render_to_info(variables)
if self._info[template].exception:
if info.exception:
if raise_on_template_error:
raise self._info[template].exception
raise info.exception
_LOGGER.error(
"Error while processing template: %s",
track_template_.template,
exc_info=self._info[template].exception,
exc_info=info.exception,
)

self._track_state_changes = async_track_state_change_filtered(
Expand Down Expand Up @@ -808,9 +808,7 @@ def _render_template_if_ready(
if event:
info = self._info[template]

if not self._rate_limit.async_has_timer(
template
) and not _event_triggers_rerender(event, info):
if not _event_triggers_rerender(event, info):
return False

if self._rate_limit.async_schedule_action(
Expand All @@ -819,6 +817,8 @@ def _render_template_if_ready(
now,
self._refresh,
event,
(track_template_,),
True,
):
return False

Expand All @@ -829,10 +829,12 @@ def _render_template_if_ready(
)

self._rate_limit.async_triggered(template, now)
self._info[template] = template.async_render_to_info(track_template_.variables)
self._info[template] = info = template.async_render_to_info(
track_template_.variables
)

try:
result: Union[str, TemplateError] = self._info[template].result()
result: Union[str, TemplateError] = info.result()
except TemplateError as ex:
result = ex

Expand All @@ -848,12 +850,29 @@ def _render_template_if_ready(
return TrackTemplateResult(template, last_result, result)

@callback
def _refresh(self, event: Optional[Event]) -> None:
def _refresh(
self,
event: Optional[Event],
track_templates: Optional[Iterable[TrackTemplate]] = None,
replayed: Optional[bool] = False,
) -> None:
"""Refresh the template.

The event is the state_changed event that caused the refresh
to be considered.

track_templates is an optional list of TrackTemplate objects
to refresh. If not provided, all tracked templates will be
considered.

replayed is True if the event is being replayed because the
rate limit was hit.
"""
updates = []
info_changed = False
now = dt_util.utcnow()
now = event.time_fired if not replayed and event else dt_util.utcnow()

for track_template_ in self._track_templates:
for track_template_ in track_templates or self._track_templates:
update = self._render_template_if_ready(track_template_, now, event)
if not update:
continue
Expand Down
12 changes: 9 additions & 3 deletions homeassistant/helpers/ratelimit.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def __init__(
@callback
def async_has_timer(self, key: Hashable) -> bool:
"""Check if a rate limit timer is running."""
if not self._rate_limit_timers:
return False
return key in self._rate_limit_timers

@callback
Expand All @@ -37,7 +39,7 @@ def async_triggered(self, key: Hashable, now: Optional[datetime] = None) -> None
@callback
def async_cancel_timer(self, key: Hashable) -> None:
"""Cancel a rate limit time that will call the action."""
if not self.async_has_timer(key):
if not self._rate_limit_timers or not self.async_has_timer(key):
return

self._rate_limit_timers.pop(key).cancel()
Expand Down Expand Up @@ -71,10 +73,14 @@ def async_schedule_action(

Return None
"""
if rate_limit is None or key not in self._last_triggered:
if rate_limit is None:
return None

next_call_time = self._last_triggered[key] + rate_limit
last_triggered = self._last_triggered.get(key)
if not last_triggered:
return None

next_call_time = last_triggered + rate_limit

if next_call_time <= now:
self.async_cancel_timer(key)
Expand Down
17 changes: 16 additions & 1 deletion homeassistant/helpers/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ def _filter_domains_and_entities(self, entity_id: str) -> bool:
split_entity_id(entity_id)[0] in self.domains or entity_id in self.entities
)

def _filter_entities(self, entity_id: str) -> bool:
"""Template should re-render if the entity state changes when we match specific entities."""
return entity_id in self.entities

def _filter_lifecycle_domains(self, entity_id: str) -> bool:
"""Template should re-render if the entity is added or removed with domains watched."""
return split_entity_id(entity_id)[0] in self.domains_lifecycle
Expand Down Expand Up @@ -255,15 +259,26 @@ def _freeze(self) -> None:
if self.all_states:
return

if self.entities or self.domains:
if self.domains:
self.filter = self._filter_domains_and_entities
elif self.entities:
self.filter = self._filter_entities
else:
self.filter = _false


class Template:
"""Class to hold a template and manage caching and rendering."""

__slots__ = (
"__weakref__",
"template",
"hass",
"is_static",
"_compiled_code",
"_compiled",
)

def __init__(self, template, hass=None):
"""Instantiate a template."""
if not isinstance(template, str):
Expand Down