Template with custom_templates import does not update correctly

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 -%}
1 Like

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

1 Like

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:

1 Like

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 %}

thanks Taras, and yes, that is what is happening, I had been ‘hacking’ this fix, but felt insecure.
It seems to be just that, a hack, and figured I was doing things fundamentally incorrect using/needing that to solve the split method anomaly.

thing is, I now need yet another template evalution in that config. It is becoming increasingly complex using the import method, which started out hoping it would be the easy and efficient way…

I could ofc make it a bit simpler by simply setting a name, and not use the name template (which also uses the count) but really that is besides the issue I am facing here.

just compare these and see the difference, considering the import template still requires the additional custom_template yaml, while the this variant suffices as is. (didn’t yet test whether it updates as trigger template yet, could be another challenge in itself…)

      - unique_id: alerts_notifying
        state: >
          {% from 'alerts.jinja' import alerts %}
          {% set alerts = alerts().split(',') if alerts() != '' else [] %}
          {% set count = alerts|count %}
          {{count}}
        name: >
          {% from 'alerts.jinja' import alerts %}
          {% set alerts = alerts().split(',') if alerts() != '' else [] %}
          {% set count = alerts|count %}
          {% set phrase = 'Alert' if count == 1 else 'Alerts' %}
          {{count}} {{phrase}} actief
        icon: >
          {% from 'alerts.jinja' import alerts %}
          {% set alerts = alerts().split(',') if alerts() != '' else [] %}
          {% set count = alerts|count %}
          mdi:numeric-{{count}}-box
        attributes:
          message: >-
            {% from 'alerts.jinja' import alerts %}
            {% set alerts = alerts().split(',') if alerts() != '' else [] %}
            {% set count = alerts|count %}
            {%- if count == 0 -%} Ok, geen alerts, alles is in orde
            {%- elif count == 1 -%}{{count}} Alert voor: {{alerts[0]}}
            {%- elif count == 2 -%}
              {{count}} Alerts voor: {{alerts[0]}} en {{alerts[1]}}
            {%- else -%}
              {{count}} Alerts voor: {{alerts[:-1]|join(', ')}}, en {{alerts[-1]}}
            {%- endif %}

      - 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 alerts = this.attributes.get('alerts') %}
            {% set count = alerts|count %}
            {/ {% set count = this.state|int(default=-1) %} /}
            {%- if count == 0 -%} Ok, geen alerts, alles is in orde
            {%- elif count == 1 -%}{{count}} Alert voor: {{alerts[0]}}
            {%- elif count == 2 -%}
              {{count}} Alerts voor: {{alerts[0]}} en {{alerts[1]}}
            {%- else -%}
              {{count}} Alerts voor:{{alerts[:-1]|join(', ')}}, en {{alerts[-1]}}
            {%- endif %}

In your second example, your templates use this to reference property values that exist in related attributes. For example, the state template references the value of the alerts attribute. Be advised of the following:

  • this references the entity’s existing property values (i.e. the previously computed values).
  • The values of state and all attributes are computed sequentially (not concurrently). In other words, some get new values before others do.

Aware the this.state seems to translate to the previous state sometimes, I still can not get to the point where I can fully predict when…

sometimes we can use {{this.state}}, and sometimes we need to use {{states(this.entity_id)}}.

in the case of the sensors above I havent been able to get it to fail yet:

          message: >-
            {% set alerts = this.attributes.get('alerts') %}
            {% set count = alerts|count %}

btw, check this:

          {% from 'alerts.jinja' import alerts %}
          {% set alerts = alerts().split(',')|reject('eq', '')|list %}
          {% set count = alerts|count %}
          {{count}}

which Petro suggested I should use in the generator to prevent those empty list joins.

It’s my understanding that both will report the entity’s current state value from the State Machine.

That’s correct. But marius is referring to this.state's use inside attribute templates. Which will be the current state because of the resolution order. I’m not sure where he’s getting “sometimes”. It’s pretty straight forward. this.state inside state or availability will always be the last state. this.state inside attributes will always be the current state.

Would you happen to know if (individual) attributes are evaluated in the order presented in the entity’s configuration or in some other order (sorted perhaps)?

Probably sorted. I don’t have that part memorized.

it’s sorted. The attributes template listeners are stored in a list, but the yaml configuration is a dictionary. When the configuration is converted from yaml to a dictionary, the attribute keys are sorted.

This of course assumes that they all ‘update at the same time’. If the templates themselves reference other entities, then the order is not “expected” because templates update based on what they are listening to.

EDIT: Now that I think about it, there’s no guaranteed order. The only guarantee is that availability resolves first, then state, then the rest in any order.

Given that attributes are evaluated in sorted order, then bat is evaluated before cat in the following example:

        attributes:
          cat: >-
            {{ this.state | int(0) * 5 }}
          bat: >
            {{ this.attributes.cat * 10 }}

That means bat uses the previous value of this.attributes.cat and not the newly computed value. As for cat it will use the newly computed value of this.state.

Is that a correct interpretation of how the system works?

You’d have to test that. HA is async and so are the attributes. I believe that would be sort order at that point but it’s hard to tell without debugging into the system with that specific test in mind. Based on the code I’ve read, I believe that particular case would be sort order.

that describes my sometimes :wink:

I didnt check before, but now I did, and couldnt find any description of this on the templates page at all, I believe it should be in the documentation for the this variable.
In a clearly accented dedicated section on this…

there is only This and linking to This

confusion arises when people mistake last (as in the final) state for current (which is also final). Maybe we should refer to the last state as ‘previous’…