mirror of
https://github.com/home-assistant/core.git
synced 2026-02-15 07:36:16 +00:00
Make template lock code error consistent between state based and trigger based template entities (#162923)
This commit is contained in:
@@ -104,7 +104,7 @@ class AbstractTemplateEntity(Entity):
|
||||
validator: Callable[[Any], Any] | None = None,
|
||||
on_update: Callable[[Any], None] | None = None,
|
||||
render_complex: bool = False,
|
||||
**kwargs,
|
||||
none_on_template_error: bool = True,
|
||||
) -> None:
|
||||
"""Set up a template that manages any property or attribute of the entity.
|
||||
|
||||
@@ -124,6 +124,9 @@ class AbstractTemplateEntity(Entity):
|
||||
This signals trigger based template entities to render the template
|
||||
as a complex result. State based template entities always render
|
||||
complex results.
|
||||
none_on_template_error (default=True)
|
||||
If set to false, template errors will be supplied in the result to
|
||||
on_update.
|
||||
"""
|
||||
|
||||
def add_template(
|
||||
|
||||
@@ -305,7 +305,9 @@ class TemplateEntity(AbstractTemplateEntity):
|
||||
else:
|
||||
setattr(self, attribute, state)
|
||||
|
||||
self.add_template(option, attribute, on_update=_update_state)
|
||||
self.add_template(
|
||||
option, attribute, on_update=_update_state, none_on_template_error=False
|
||||
)
|
||||
|
||||
def setup_template(
|
||||
self,
|
||||
@@ -314,7 +316,7 @@ class TemplateEntity(AbstractTemplateEntity):
|
||||
validator: Callable[[Any], Any] | None = None,
|
||||
on_update: Callable[[Any], None] | None = None,
|
||||
render_complex: bool = False,
|
||||
**kwargs,
|
||||
none_on_template_error: bool = True,
|
||||
):
|
||||
"""Set up a template that manages any property or attribute of the entity.
|
||||
|
||||
@@ -334,8 +336,10 @@ class TemplateEntity(AbstractTemplateEntity):
|
||||
This signals trigger based template entities to render the template
|
||||
as a complex result. State based template entities always render
|
||||
complex results.
|
||||
none_on_template_error (default=True)
|
||||
If set to false, template errors will be supplied in the result to
|
||||
on_update.
|
||||
"""
|
||||
none_on_template_error = kwargs.get("none_on_template_error", True)
|
||||
self.add_template(
|
||||
option, attribute, validator, on_update, none_on_template_error
|
||||
)
|
||||
|
||||
@@ -7,9 +7,16 @@ from typing import Any
|
||||
|
||||
from homeassistant.const import CONF_STATE, CONF_VARIABLES
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers.script_variables import ScriptVariables
|
||||
from homeassistant.helpers.template import _SENTINEL
|
||||
from homeassistant.helpers.trigger_template_entity import TriggerBaseEntity
|
||||
from homeassistant.helpers.template import (
|
||||
_SENTINEL,
|
||||
render_complex as template_render_complex,
|
||||
)
|
||||
from homeassistant.helpers.trigger_template_entity import (
|
||||
TriggerBaseEntity,
|
||||
log_triggered_template_error,
|
||||
)
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import TriggerUpdateCoordinator
|
||||
@@ -59,7 +66,9 @@ class TriggerEntity( # pylint: disable=hass-enforce-class-module
|
||||
on_update: Callable[[Any], None] | None = None,
|
||||
) -> None:
|
||||
"""Set up a template that manages the main state of the entity."""
|
||||
if self.add_template(option, attribute, validator, on_update):
|
||||
if self.add_template(
|
||||
option, attribute, validator, on_update, none_on_template_error=False
|
||||
):
|
||||
self._to_render_simple.append(option)
|
||||
self._parse_result.add(option)
|
||||
|
||||
@@ -70,7 +79,7 @@ class TriggerEntity( # pylint: disable=hass-enforce-class-module
|
||||
validator: Callable[[Any], Any] | None = None,
|
||||
on_update: Callable[[Any], None] | None = None,
|
||||
render_complex: bool = False,
|
||||
**kwargs,
|
||||
none_on_template_error: bool = True,
|
||||
) -> None:
|
||||
"""Set up a template that manages any property or attribute of the entity.
|
||||
|
||||
@@ -90,8 +99,13 @@ class TriggerEntity( # pylint: disable=hass-enforce-class-module
|
||||
This signals trigger based template entities to render the template
|
||||
as a complex result. State based template entities always render
|
||||
complex results.
|
||||
none_on_template_error (default=True)
|
||||
If set to false, template errors will be supplied in the result to
|
||||
on_update.
|
||||
"""
|
||||
if self.add_template(option, attribute, validator, on_update):
|
||||
if self.add_template(
|
||||
option, attribute, validator, on_update, none_on_template_error
|
||||
):
|
||||
if render_complex:
|
||||
self._to_render_complex.append(option)
|
||||
else:
|
||||
@@ -116,6 +130,33 @@ class TriggerEntity( # pylint: disable=hass-enforce-class-module
|
||||
"""Render configured variables."""
|
||||
return self._rendered_entity_variables or {}
|
||||
|
||||
def _render_single_template(
|
||||
self,
|
||||
key: str,
|
||||
variables: dict[str, Any],
|
||||
strict: bool = False,
|
||||
) -> Any:
|
||||
"""Render a single template."""
|
||||
try:
|
||||
if key in self._to_render_complex:
|
||||
return template_render_complex(self._config[key], variables)
|
||||
|
||||
return self._config[key].async_render(
|
||||
variables, parse_result=key in self._parse_result, strict=strict
|
||||
)
|
||||
except TemplateError as err:
|
||||
log_triggered_template_error(self.entity_id, err, key=key)
|
||||
# Filter out state templates because they have unique behavior
|
||||
# with none_on_template_error.
|
||||
if (
|
||||
key != CONF_STATE
|
||||
and key in self._templates
|
||||
and not self._templates[key].none_on_template_error
|
||||
):
|
||||
return err
|
||||
|
||||
return _SENTINEL
|
||||
|
||||
def _render_templates(self, variables: dict[str, Any]) -> None:
|
||||
"""Render templates."""
|
||||
self._state_render_error = False
|
||||
|
||||
@@ -757,16 +757,16 @@ async def test_lock_actions_fail_with_invalid_code(
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("style", "attribute", "expected"),
|
||||
("style", "attribute"),
|
||||
[
|
||||
(ConfigurationStyle.LEGACY, "code_format_template", 0),
|
||||
(ConfigurationStyle.MODERN, "code_format", 0),
|
||||
(ConfigurationStyle.TRIGGER, "code_format", 2),
|
||||
(ConfigurationStyle.LEGACY, "code_format_template"),
|
||||
(ConfigurationStyle.MODERN, "code_format"),
|
||||
(ConfigurationStyle.TRIGGER, "code_format"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("setup_state_lock_with_attribute")
|
||||
async def test_lock_actions_dont_execute_with_code_template_rendering_error(
|
||||
hass: HomeAssistant, calls: list[ServiceCall], expected: int
|
||||
hass: HomeAssistant, calls: list[ServiceCall]
|
||||
) -> None:
|
||||
"""Test lock code format rendering fails block lock/unlock actions."""
|
||||
|
||||
@@ -786,10 +786,7 @@ async def test_lock_actions_dont_execute_with_code_template_rendering_error(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Trigger expects calls here because trigger based entities don't
|
||||
# allow template exception resolutions into code_format property so
|
||||
# the actions will fire using the previous code_format.
|
||||
assert len(calls) == expected
|
||||
assert len(calls) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user