Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
11 changes: 11 additions & 0 deletions homeassistant/components/automation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,16 @@ async def _async_attach_triggers(
def log_cb(level, msg, **kwargs):
self._logger.log(level, "%s %s", msg, self._name, **kwargs)

variables = None
if self._variables:
try:
variables = self._variables.async_render(
cast(HomeAssistant, self.hass), None
)
except template.TemplateError as err:
self._logger.error("Error rendering variables: %s", err)
return None

return await async_initialize_triggers(
cast(HomeAssistant, self.hass),
self._trigger_config,
Expand All @@ -473,6 +483,7 @@ def log_cb(level, msg, **kwargs):
self._name,
log_cb,
home_assistant_start,
variables,
)

@property
Expand Down
15 changes: 12 additions & 3 deletions homeassistant/components/mqtt/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from homeassistant.const import CONF_PAYLOAD, CONF_PLATFORM
from homeassistant.core import HassJob, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import config_validation as cv, template

from .. import mqtt

Expand All @@ -20,8 +20,8 @@
TRIGGER_SCHEMA = vol.Schema(
{
vol.Required(CONF_PLATFORM): mqtt.DOMAIN,
vol.Required(CONF_TOPIC): mqtt.util.valid_subscribe_topic,
vol.Optional(CONF_PAYLOAD): cv.string,
vol.Required(CONF_TOPIC): cv.template,
vol.Optional(CONF_PAYLOAD): cv.template,
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
vol.Optional(CONF_QOS, default=DEFAULT_QOS): vol.All(
vol.Coerce(int), vol.In([0, 1, 2])
Expand All @@ -37,6 +37,15 @@ async def async_attach_trigger(hass, config, action, automation_info):
encoding = config[CONF_ENCODING] or None
qos = config[CONF_QOS]
job = HassJob(action)
variables = automation_info["variables"]

if payload:
template.attach(hass, payload)
payload = payload.async_render(variables, limited=True)
Comment thread
balloob marked this conversation as resolved.

template.attach(hass, topic)
topic = topic.async_render(variables, limited=True)
topic = mqtt.util.valid_subscribe_topic(topic)

@callback
def mqtt_automation_listener(mqttmsg):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/helpers/config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ def dynamic_template(value: Optional[Any]) -> template_helper.Template:
if isinstance(value, (list, dict, template_helper.Template)):
raise vol.Invalid("template value should be a string")
if not template_helper.is_template_string(str(value)):
raise vol.Invalid("template value does not contain a dynmamic template")
raise vol.Invalid("template value does not contain a dynamic template")

template_value = template_helper.Template(str(value)) # type: ignore
try:
Expand Down
42 changes: 38 additions & 4 deletions homeassistant/helpers/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class Template:
"is_static",
"_compiled_code",
"_compiled",
"_limited",
)

def __init__(self, template, hass=None):
Expand All @@ -291,10 +292,11 @@ def __init__(self, template, hass=None):
self._compiled: Optional[Template] = None
self.hass = hass
self.is_static = not is_template_string(template)
self._limited = None

@property
def _env(self) -> "TemplateEnvironment":
if self.hass is None:
if self.hass is None or self._limited:
return _NO_HASS_ENV
ret: Optional[TemplateEnvironment] = self.hass.data.get(_ENVIRONMENT)
if ret is None:
Expand All @@ -315,6 +317,7 @@ def render(
self,
variables: TemplateVarsType = None,
parse_result: bool = True,
limited: bool = False,
**kwargs: Any,
) -> Any:
"""Render given template."""
Expand All @@ -325,14 +328,15 @@ def render(

return run_callback_threadsafe(
self.hass.loop,
partial(self.async_render, variables, parse_result, **kwargs),
partial(self.async_render, variables, parse_result, limited, **kwargs),
).result()

@callback
def async_render(
self,
variables: TemplateVarsType = None,
parse_result: bool = True,
limited: bool = False,
Comment thread
balloob marked this conversation as resolved.
**kwargs: Any,
) -> Any:
"""Render given template.
Expand All @@ -344,7 +348,7 @@ def async_render(
return self.template
return self._parse_result(self.template)

compiled = self._compiled or self._ensure_compiled()
compiled = self._compiled or self._ensure_compiled(limited)

if variables is not None:
kwargs.update(variables)
Expand Down Expand Up @@ -519,12 +523,16 @@ def async_render_with_possible_json_value(
)
return value if error_value is _SENTINEL else error_value

def _ensure_compiled(self) -> "Template":
def _ensure_compiled(self, limited: bool = False) -> "Template":
"""Bind a template to a specific hass instance."""
self.ensure_valid()

assert self.hass is not None, "hass variable not set on template"
assert (
self._limited is None or self._limited == limited
), "can't change between limited and non limited template"

self._limited = limited
env = self._env

self._compiled = cast(
Expand Down Expand Up @@ -1352,6 +1360,32 @@ def __init__(self, hass):
self.globals["strptime"] = strptime
self.globals["urlencode"] = urlencode
if hass is None:

def unsupported(name):
def warn_unsupported(*args, **kwargs):
_LOGGER.warning(
"Use of '%s' is not supported in limited templates", name
)
return ""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it raise?

@emontnemery emontnemery Jan 27, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, maybe better to raise to entirely block the template from rendering?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to break it and not have it attach the trigger.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because triggers are a pain to debug.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to raise, it gets really noisy though:

2021-01-27 20:52:58 ERROR (MainThread) [homeassistant.components.automation.hasp_control_slider_object] Error setting up trigger HASP Control Slider Object
Traceback (most recent call last):
  File "/mnt/d/development/github/home-assistant_fork/homeassistant/helpers/template.py", line 357, in async_render
    render_result = compiled.render(kwargs)
  File "/mnt/d/development/github/home-assistant_fork/venv/lib/python3.8/site-packages/jinja2/environment.py", line 1090, in render
    self.environment.handle_exception()
  File "/mnt/d/development/github/home-assistant_fork/venv/lib/python3.8/site-packages/jinja2/environment.py", line 832, in handle_exception
    reraise(*rewrite_traceback_stack(source=source))
  File "/mnt/d/development/github/home-assistant_fork/venv/lib/python3.8/site-packages/jinja2/_compat.py", line 28, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
  File "/mnt/d/development/github/home-assistant_fork/venv/lib/python3.8/site-packages/jinja2/sandbox.py", line 462, in call
    return __context.call(__obj, *args, **kwargs)
  File "/mnt/d/development/github/home-assistant_fork/homeassistant/helpers/template.py", line 1366, in warn_unsupported
    raise TemplateError(f"Use of '{name}' is not supported in limited templates")
homeassistant.exceptions.TemplateError: str: Use of 'states' is not supported in limited templates

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/mnt/d/development/github/home-assistant_fork/homeassistant/components/mqtt/trigger.py", line 47, in async_attach_trigger
    topic = topic.async_render(variables, limited=True)
  File "/mnt/d/development/github/home-assistant_fork/homeassistant/helpers/template.py", line 359, in async_render
    raise TemplateError(err) from err
homeassistant.exceptions.TemplateError: TemplateError: str: Use of 'states' is not supported in limited templates

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should catch HomeAssistantError and then not print the stack trace. That will help :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, fixed in eeac975


return warn_unsupported

hass_globals = [
"closest",
"distance",
"expand",
"is_state",
"is_state_attr",
"state_attr",
"states",
"utcnow",
"now",
]
hass_filters = ["closest", "expand"]
for g in hass_globals:
self.globals[g] = unsupported(g)
for f in hass_filters:
self.filters[f] = unsupported(f)
return

# We mark these as a context functions to ensure they get
Expand Down