Templating changes in 2021.10.0



Note

This topic was created prior to the release of 2021.10.0 and was meant to inform the community of important upcoming changes to certain aspects of templating.

After the release of 2021.10.0, petro posted many examples of how to adapt templates to comply with the new requirements.

I recommend reviewing the examples before posting questions.



I skimmed the pre-release notes and selected several notable templating improvements and modifications.


Breaking Change

Several existing functions will handle defaults differently. PR 5643

The following template filters and functions will now log a warning instead of silently returning the input if the input is invalid and no default value is specified:

round, multiply, log, sin, cos, tan, asin, acos, atan, atan2, sqrt, timestamp_custom, timestamp_local, timestamp_utc, as_timestamp, strptime, float

The float filter will now log a warning instead of silently returning 0 if the input is invalid and no default value is specified.

In Home Assistant core 2021.12 the template will fail to render if no default value is specified.

Examples:

{{ "abc" | float }} - Will render as 0, and a warning will be logged
{{ "abc" | float(default=5) }} - Will render as -5, no warning will be logged
{{ float("abc") }} - Will render as ā€œabcā€, and a warning will be logged
{{ float("abc", default=5) }} - Will render as -5, no warning will be logged


New/Enhanced Functions

is_numeric

New function that indicates if the supplied value is numeric. PR 56705

is_numeric will returns True if the input can be parsed by Pythonā€™s float function and its value is not inf or nan, in all other cases returns False. Can be used as a filter.

Examples:

{{ "" | is_numeric}} - will render as False
{{ "1" | is_numeric}} - will render as True
{{ "1.1" | is_numeric}} - will render as True
{{ "nan" | is_numeric}} - will render as False
{{ "inf" | is_numeric}} - will render as False
{{ "Ā½" | is_numeric}} - will render as False
{{ True | is_numeric }} - will render as True
{{ False | is_numeric }} - will render as True
{{ "True" | is_numeric}} - will render as False
{{ "False" | is_numeric}} - will render as False

regex_findall

New function that returns an array of matches. PR 54584

Filter string|regex_findall(find='', ignorecase=False) will find all regex matches of the find expression in string and return the array of matches.

device_id

Existing function that now accepts a device name. PR 55474

device_id(entity_id) returns the device ID for a given entity ID or device name. Can also be used as a filter

9 Likes

I have to believe that the ā€œ-5ā€ in the example - and I know itā€™s quoted directly from the PR - is incorrect and it should be simply the positive number 5.

I agree with you; I quoted the original text without alterations and itā€™s unlikely it returns -5 instead of 5.

What I did modify were the examples for is_numeric. The proposed function was originally called isnumber and thatā€™s how it appears in the PRā€™s examples. Beyond that, I didnā€™t make any other corrections.

I asked the question last Thursday, on the advice of tom_l I changed all my

{{ "abc" | float }}

to

{{ "abc" | float(0) }}

Does that work, or do I need

{{ "abc" | float(default=0) }}

I donā€™t think the default= is required (I believe itā€™s just a documentation convention).

For comparison, hereā€™s the documentation for timestamp_custom.

Filter timestamp_custom(format_string, local_time=True) converts an UNIX timestamp to its string representation based on a custom format, the use of a local timezone is default.

If you include the optionā€™s name, the Jinja2 interpreter complains:

1 Like

The following note makes me wonder what should be supplied as the default for all of the timestamp-related functions like as_timestamp, strptime, timestamp_local, etc?

Perhaps something like this where now() represents the default value?

{{ 'foo' | timestamp_local(now()) }}

I asked exactly that in the beta. Got no response.

Even the code tests donā€™t make sense to me

    # Test handling of default return value
    assert render(hass, "{{ None | timestamp_local(1) }}") == 1
    assert render(hass, "{{ None | timestamp_local(default=1) }}") == 1

What is a datetime of ā€œ1ā€?

In all fairness, before this, the function returned the input in case of errors (i.e. the timestamp) which is quite wrong in the first placeā€¦
This is the kind of functions that should just fail if the conversion cannot be done. I donā€™t think returning a default value makes much sense.

one second after midnight on 1/1/1970? :man_shrugging:

at least thatā€™s what I would think since itā€™s a timestamp.

Fact is timestamp_local is supposed to return a python datetime, not a timestamp :wink:
So a default return value of ā€œ1ā€ doesnā€™t make any senseā€¦

I think it makes sense if you want to capture a failure. I.e. set the default to 1 or none and check against it later on in the code. Or donā€™t bother checking against it and it will be ā€˜unavailableā€™.

{{ None | timestamp_local(none) }}

FWIW, hereā€™s what it currently does (2021.9.7)

youā€™re right. good point.

Both are actually valid, so thatā€™s quite a proper test. This is more fun :wink:

EDIT: Actually now() ... is not valid, my mistake. It just returns now() as-is.

Iā€™m running the dev build and timestamp_local doesnā€™t have default kwarg, so itā€™s possible the docs are wrong. It also doesnā€™t accept arguments.

EDIT: It seems like it will just produce the warning but it does not accept arguments or default kwarg.

EDIT2: My dev build is a week old. That might be it.

I know it currently does ā€˜garbage in, garbage outā€™ (and your eyes are better than mine because magenta text on a dark background is evil).

The examples I posted were meant to confirm finityā€™s speculation, except the result also includes timezone offset (so thatā€™s why it is before the Unix Epoch).

This is how strptime/strftime has always worked and timestamp_local is just an extension of that. Thatā€™s why Iā€™m skeptical default kwarg or argument is valid.

Well, it appears my build is outdated. Itā€™s definitely an option:

Yep. And _SENTINEL is an empty object, FWIW. Just used as a dummy value.

So then using None is preferred IMO if you want to have a valid date or unavailable.