I’ve already explained it. A set does not execute code. It’s already set. When you import it, it does not run it. It just gets the value that it has. You have to use a macro to run anything. And because you have to use a macro, you have to account for the output always being a string when you use the macro.
You can also doe | to_json
at the end of the macro, and then | from_json
when using it in a template
eg:
{%- macro alerts -%}
{{- expand(state_attr('binary_sensor.alerts','entity_id'))
|selectattr('state','eq','on')|map(attribute='name')|list | to_json -}}
{%- endmacro -%}
{% from 'alerts.jinja' import alerts %}
{{ alerts | from_json }}
I would also reccomend to add hyphens everywhere you can in the macro, to avoid whitespace issues.
Yes but that’s running potentially expensive operations when a split on a comma separated list isn’t too expensive.
Personally, I use to/from json as a last resort. I make all my macros perform the operations with simple returned values.
wasn’t aware that there is such a difference in these operations
To_json has to touch every object inside what your turning into json and from_json does as well.
so I changed that to:
{% macro alerts_macro() %}
{{ expand(state_attr('binary_sensor.alerts','entity_id'))
|selectattr('state','eq','on')|map(attribute='name')|list }}
{% endmacro %}
but that makes
{% from 'alerts.jinja' import alerts_macro %}
{{ alerts_macro }}
return
<Macro ‘alerts_macro’>
maybe I am just better off using the this variable in these type of templates.
at least this works:
- unique_id: alerts_notifying_this
state: >
{{this.attributes.alerts|count}}
name: >
{% set count = this.state|int(default=-1) %}
{% set phrase = 'Alert' if count == 1 else 'Alerts' %}
{{count}} {{phrase}} actief
icon: >
{% set count = this.state|int(default=-1) %}
mdi:numeric-{{count}}-box
attributes:
alerts: >
{{ expand(state_attr('binary_sensor.alerts','entity_id'))
|selectattr('state','eq','on')|map(attribute='name')|list }}
message: >-
{% set count = this.state|int(default=-1) %}
{% set alerts = this.attributes.get('alerts') %}
{%- if count == 0 -%} Ok, geen alerts, alles is in orde
{%- elif count == 1 -%}{{count}} Alert: {{alerts[0]}}
{%- elif count == 2 -%}
{{count}} Alerts: {{alerts[0]}} en {{alerts[1]}}
{%- else -%}
{{count}} Alerts:{{alerts[:-1]|join(', ')}}, en {{alerts[-1]}}
{%- endif %}
… I am not sure though if this is just as efficient as using the custom_templates. as in, whether the attribute is evaluated only once, and not in every single config option of the template sensor
You’re missing () in your macro call
a yes that helps
but nw I have no way to count or iterate…
what should I change in the macro:
{% macro alerts_macro() %}
{{ expand(state_attr('binary_sensor.alerts','entity_id'))
|selectattr('state','eq','on')|map(attribute='name')|list }}
{% endmacro %}
to be able to do that?
Yes that will work but then you have to split the output…
arghh, getting better, not quit there yet…
{% from 'alerts.jinja' import alerts_macro %}
{{ alerts_macro().split(',') }}
{% set count = alerts_macro().split(',')|count %}
{% set alerts = alerts_macro().split(',') %}
{%- if count == 0 -%} Ok, geen alerts, alles is in orde
{%- elif count == 1 -%}{{count}} Alert: {{alerts[0]}}
{%- elif count == 2 -%}
{{count}} Alerts: {{alerts[0]}} en {{alerts[1]}}
{%- else -%}
{{count}} Alerts:{{alerts[:-1]|join(', ')}}, en {{alerts[-1]}}
{%- endif %}
native the odd new lines, and the listing notation in the bottom part
yes I did that now, and still producing:
["['Hall'", " 'Humidity Living low'", " 'Climate living alert']"]
3 Alerts:['Hall', 'Humidity Living low', en 'Climate living alert']
Ensure your macro template ends with join(',')
{%- macro alerts_macro() -%}
{{ expand(state_attr('binary_sensor.alerts','entity_id'))
|selectattr('state','eq','on')|map(attribute='name')|list|join(',') }}
{%- endmacro -%}
that is getting better again:
{% from 'alerts.jinja' import alerts_macro %}
{{ alerts_macro().split(',')}}
{% set count = alerts_macro().split(',')|count %}
{% set alerts = alerts_macro().split(',') %}
{%- if count == 0 -%} Ok, geen alerts, alles is in orde
{%- elif count == 1 -%}{{count}} Alert: {{alerts[0]}}
{%- elif count == 2 -%}
{{count}} Alerts: {{alerts[0]}} en {{alerts[1]}}
{%- else -%}
{{count}} Alerts: {{alerts[:-1]}}, en {{alerts[-1]}}
{%- endif %}
adding
{{count}} Alerts: {{alerts[:-1]|join(', ')}}, en {{alerts[-1]}}
finally resulting in:
Please, can you go back to my original response and reread it? I already explained everything you need to do in the first post. End your macro with a join, and split the macros result.
This post here
yes, thanks Petro and 123, sometimes one has to go over things oneself to truly grasp.
for the sake of completeness/reference:
wat is your opinion on this final template sensor using the macro in the custom_templates, compared to the version with the this
variable, from an efficiency perspective?
I started this whole migration with that in mind after all…
Tip:
If you use join(', ')
at the end of your macro’s template (that’s a comma and a space) then you can use this:
{% set alerts = alerts_macro().split(', ') %}
{% set count = alerts|count %}
{%- if count == 0 -%} Ok, geen alerts, alles is in orde
{%- else -%}
{{count}} Alert{{iif(count > 1, 's','')}}: {{' en '.join(alerts_macro().rsplit(', ', 1))}}
{%- endif %}
It takes advantage of the template in this post:
not yet there just yet…
consider this macro:
{%- macro critical_off() -%}
{{- expand(state_attr('switch.critical_switches','entity_id'))
|selectattr('state','eq','off')
|map(attribute='name')
|list|join(',') -}}
{%- endmacro -%}
and then no switches are off. this results in:
using:
- unique_id: critical_switches_offline
state: >
{% from 'alerts.jinja' import critical_off %}
{{critical_off().split(',')|count}}
name: >
{% from 'alerts.jinja' import critical_off %}
{% set count = critical_off().split(',')|count %}
{% set phrase = 'switch' if count == 1 else 'switches' %}
{{count}} Critical {{phrase}} offline
icon: >
{% from 'alerts.jinja' import critical_off %}
{% set count = critical_off().split(',')|count %}
mdi:numeric-{{count}}-box
attributes:
message: >-
{% from 'alerts.jinja' import critical_off %}
{% set critical = critical_off().split(',') %}
{% set count = critical_off().split(',')|count %}
{%- if count == 0 -%} Ok, all critical switches are online
{%- elif count == 1 -%}{{count}} Critical switch offline: {{critical[0]}}
{%- elif count == 2 -%}
{{count}} Critical switches offline: {{critical[0]}} and {{critical[1]}}
{% else %}
{{count}} Critical switches are offline: {{critical[:-1]|join(', ')}}, and {{critical[-1]}}
{% endif %}
cant it handle an empty list?, and the split there causing the empty list to be ['']
which returns 1 when |count
that. Note there is NO switch listed after the 1 Critical switch offline:
(and that is actually correct)
It’s ok when I actually turn off 1:
Note the Koelkast keuken as listing of the entity
It seems to be due to the way the split
method works If given an empty string and a delimiter, it returns a list with one item containing the empty string.
The first line of this template sets the variable to an empty list if the macro reports an empty string.
{% set alerts = alerts_macro().split(', ') if alerts_macro() != '' else [] %}
{% set count = alerts|count %}
{%- if count == 0 -%} Ok, geen alerts, alles is in orde
{%- else -%}
{{count}} Alert{{iif(count > 1, 's','')}}: {{' en '.join(alerts_macro().rsplit(', ', 1))}}
{%- endif %}