Binary_sensor, Template trigger and expand()

Greetings,
I’m trying to create a binary_sensor that listen for entities change that are included in a Group.

- trigger:
    - platform: template
      value_template: >-
        {% set excluded = ['switch.caldaia_on_off'] %}
        {% set entita = expand('group.cucina')
                          | rejectattr("state", "in", ["undefined", "unavailable"])
                          | rejectattr("entity_id", "in", excluded)
        %}
        {% set switch_on = entita | selectattr('state', 'eq', 'on') %}
        {{ (switch_on | list | count > 0) }}
  binary_sensor:
    - name: Cucina attiva
      unique_id: cucina_attiva
      state: >-
        {% set excluded = ['switch.caldaia_on_off'] %}
        {% set entita = expand('group.cucina')
                          | rejectattr("state", "in", ["undefined", "unavailable"])
                          | rejectattr("entity_id", "in", excluded)
        %}
        {% set switch_on = entita | selectattr('state', 'eq', 'on') %}
        {{ (switch_on | list | count > 0) }}
      attributes:
        fake_trigger_upd: >-
          {{ not state_attr(this.entity_id, 'fake_trigger_upd')|bool(false) }}
      availability: "{{ true }}"

It seems to works when at least one entity become Active but refuses to goes Off when all same entitites goes Off.

I tested the “{%…%}” in the Developer Model section and it works well following the behaviour I’m expecting.

I tried just leaving only one Switch inside that Group but nothing has changed.

I also added a “fake” attribute that changes state at every trigger but it doesn’t change its state when all group entities goes Off.

If I replace “platform: template” and “value_template: …” with the relative list of “platform: state” and “entity_id:…” all works fine listening correctly for both ON and OFF events of same entitites.

So I suppose there is something on the trigger that doesn’t work well using Trigger Template and/or “expand()” function.

I read THIS topic but it seems not solved as I demonstrated now.

The only workaround I found is avoid to expand the group in the Template Trigger and just list all group entities in a “platform: state” trigger, but this completly crazy…

UPDATE:
I noticed a strange behaviour while testing “{%…%}” in Development Model page.
If I try to execute this code:

{% set excluded = ['switch.caldaia_on_off'] %}
{% set entita = expand('group.cucina')
                  | rejectattr("state", "in", ["undefined", "unavailable"])
                  | rejectattr("entity_id", "in", excluded)
%}
{% set switch_on = entita | selectattr('state', 'eq', 'on') %}
{{ switch_on | map(attribute="entity_id") | list }}
{{ switch_on | list | count > 0 }}
{{ switch_on | map(attribute="entity_id") | list }}

the output is:

['light.dimmer_6_cucina_virtual', 'light.dimmer_6_cucina_reale']
False
[]

It seems the “switch_on” variable become Empty after a while…

Try this version and let me know how it performs:

        {{ expand('group.cucina')
          | rejectattr('entity_id', 'in', ['switch.caldaia_on_off'])
          | rejectattr('state', 'in', ['unknown', 'unavailable'])
          | selectattr('state', 'eq', 'on')
          | list | count > 0 }}

It doesn’t change anything.

I also tried to create a simple Helper as “Group of switches” and choosing only one switch to insert into it, but the behaviour is the same: the sensor became ON but doesn’t went to OFF even if the same trigger is well working on Developers Model page while testing it.

Using the Template Editor, I tested the suggested template using one of my groups that contains six lights.

{{ expand('group.lights_exterior_house')
  | rejectattr('entity_id', 'in', ['light.shed_lamp'])
  | rejectattr('state', 'in', ['unknown', 'unavailable'])
  | selectattr('state', 'eq', 'on')
  | list | count > 0 }}

It correctly reports true when at least one of the lights is on and reports false when all lights are off. So the suggested template works as it should.

Then I created a simple Template Binary Sensor:

template:
  - binary_sensor:
      - name: Cucina attiva
        unique_id: cucina_attiva
        state: >
          {{ expand('group.lights_exterior_house')
            | rejectattr('entity_id', 'in', ['light.shed_lamp'])
            | rejectattr('state', 'in', ['unknown', 'unavailable'])
            | selectattr('state', 'eq', 'on')
            | list | count > 0 }}

It correctly reports on when at least one light in the group is on and off when all lights in the group are off.


I suggest you try a similar approach:

template:
  - binary_sensor:
      - name: Cucina attiva
        unique_id: cucina_attiva
        state: >
          {{ expand('group.cucina')
            | rejectattr('entity_id', 'in', ['switch.caldaia_on_off'])
            | rejectattr('state', 'in', ['unknown', 'unavailable'])
            | selectattr('state', 'eq', 'on')
            | list | count > 0 }}

Thx for your test.

I noticed that if the whole Group became OFF then the trigger is well working, but my Group has an always-ON switch that denies the whole group to became off and that is why I added the “excluded” variable/filter in the Jinja: to exclude those always-ON switches.
It seems the Template Trigger just listens to the whole Group event and not on their single entities change.
If I remove the “excluded” Switch also from the group the Trigger works as expected.

In a further version/project I would like to use not-homogeneous Groups (via YAML groups) to group different domain entities (switches AND sensors AND numbers) and then create standalone Template Sensors that have specific behaviours (Trigger and State) as I’m trying to do here in this post.
This because I need a sensor that “is ON when a room is active: switches are ON (excluded those that are always-ON) AND plug power resports a value greather than 10 Watts”.
Do this easy/stupid thing using Automation is overkilling and I don’t need another Automation in my already long list if I can achieve same result using a Template Sensor and “expand()” function.

you could do this:

{{ expand('group.cucina')
          | rejectattr('entity_id', 'in', ['switch.caldaia_on_off'])
          | rejectattr('state', 'in', ['unknown', 'unavailable'])
          | selectattr('state', 'eq', 'on')
          | list | count != states('input_number.saved_count') | int}}

this depends on creating an input_number.saved_count
then in the action, do input_number.set_value to

{{ expand('group.cucina')
          | rejectattr('entity_id', 'in', ['switch.caldaia_on_off'])
          | rejectattr('state', 'in', ['unknown', 'unavailable'])
          | selectattr('state', 'eq', 'on')
          | list | count }}

Thx for your suggestion but which “action” do you refer at?
I’m not using Automations or anything else but just a Template Sensor.
There isn’t any “thing” that could call “set_value”.

well, i was thinking you’d have to have an automation to update it,
but perhaps that could be done within the trigger of the triggered sensor, not a separate automation…

which made me start to think i wonder if you could use a trigger based sensor, end up keeping the old state in an attribute (‘count_of_on’), then have the state be:

but now i’m wondering if this would work for the state part:

  - binary_sensor:
      - name: Cucina attiva
        unique_id: cucina_attiva
        state: >
          {{ expand('group.lights_exterior_house')
            | rejectattr('entity_id', 'in', ['light.shed_lamp'])
            | rejectattr('state', 'in', ['unknown', 'unavailable'])
            | selectattr('state', 'eq', 'on')
            | list | count != state_attr('sensor.cucina_attive', 'count_of_on') | int }}

i’m clearly showing my ignorance on how to do this efficiently… sorry i don’t write many template sensors with attributes… but i’m hoping to spark some creative ideas…

1 Like

i see @123 typing and about to school me :slight_smile:

2 Likes

What I suggested is a template for a Template Binary Sensor, not a Template Trigger for use in a Trigger-based Template Binary Sensor.

For the template, the group’s only purpose is to supply information about its members. The template excludes one specific group member and any members whose state value is unknown or unavailable. Whatever remaining members are left, it counts how many are on. If the count is greater than zero, it reports true (on), otherwise false (off).

Because it expands the group, the template listens for state-changes of each group member. You can confirm this in the Template Editor which lists the entities that the template monitors.

Yes, in Developer Template page the Jinja I posted is currently working well and shows which entitly listeners are currently used…but same Jinja used in a Template Tensor trigger won’t work.
It starts to work if I remove the “always-on switch” from that expanded group which is the only switch that remains always on and force the group to stay ON.
When I removed that entity from that group all started to work using that original Jinja trigger, so the only conclusion I can get using that Jinja trigger is:

  • if an expanded group (with “all=false” set) doesn’t change its state (but its entities does) that Template Trigger won’t work

Maybe in Developer Template page the Jinja is resolved in a different way than inside a Template Trigger, so in the Dev page it works well.
Unfortunately there are’nt any other places where to use that Jijnja other than a Trigger YAML section or in Dev page.

Why are you referring to a Template Trigger when the example I provided doesn’t contain one?

There is no trigger: section in the Template Binary Sensor I posted above so, by definition, it has no triggers. All it does is evaluate its state template when one of its monitored entities changes its state.

Although that may sound like what a Template Trigger does, it’s not referred to as a “trigger” when used this way in a Template entity.

I will re-test the example I suggested and let you know if I can reproduce the behavior you have described.

Here are the results of my test.

template:
  - binary_sensor:
      - name: Cucina attiva
        unique_id: cucina_attiva
        state: >
          {{ expand('group.lights_exterior_house')
            | rejectattr('entity_id', 'in', ['light.shed_lamp'])
            | rejectattr('state', 'in', ['unknown', 'unavailable'])
            | selectattr('state', 'eq', 'on')
            | list | count > 0 }}

Initial configuration:

  • group.lights_exterior_house contains six lights.
  • The value of the group’s all option is false (this is the default).
  • The state of light.shed_light is on.
  • The template’s excluded light is light.shed_light.

Test results

  • The state of binary_sensor.cucina_attiva is off even though light.shed_light is on (because the template explicitly excludes light.shed_light).

  • Setting light.shed_light to either off or on doesn’t affect the state of binary_sensor.cucina_attiva (because the template explicitly excludes light.shed_light).

  • Turning on any of the group’s other five lights will turn on binary_sensor.cucina_attiva.

  • When all of the group’s other five lights are off, the state of binary_sensor.cucina_attiva becomes off.

It works exactly like I had described in my previous post. The state of the group and the state of the excluded light are not part of the template’s computation. The template focuses exclusively on the states of the group’s un-excluded members.

Excuse me but before answer you I have a question: where is your Binary_sensor’s trigger section?
You just declared a BinarySensor without any trigger which is the part is not working for me…
The Jinja used for Trigger or State part I’m sure it’s working well because I tested it in Development Template page.
What I’m saying in this thread is that if that Jinja is used as a Template Trigger it seems doesn’t works.

Declaring a BinarySensor without any Trigger section will update the sensor only once per minute (as official Docs for Template Sensor says) and it’s not what I want.

I’m testing right now this Group

cucina_attiva:
  name: Cucina attiva
  all: false
  entities:
    - light.dimmer_6_cucina_reale
    - light.dimmer_6_cucina_virtual
    - switch.caldaia_on_off

The last “switch” sensor is the always-ON entity that denies the grouop to became off and the same I tried to exclude using “rejectattr()” in the Jinja.

This is the Binary Sensor Template with its trigger:

- trigger:
    - platform: template
      value_template: >-
        {{ expand('group.cucina_attiva') 
                | rejectattr("state", "in", ["undefined", "unavailable"]) 
                | rejectattr("entity_id", "in", ['switch.caldaia_on_off'])
                | selectattr('state', 'eq', 'on')
                | list | count > 0 }}
  binary_sensor:
    - name: Cucina attiva
      state: >-
        {{ expand('group.cucina_attiva') 
                | rejectattr("state", "in", ["undefined", "unavailable"]) 
                | rejectattr("entity_id", "in", ['switch.caldaia_on_off']) 
                | selectattr('state', 'eq', 'on') 
                | list | count > 0 }}
      availability: "{{ true }}"

Same Jinjia in both Trigger and State section just to be sure (in other sensors I’m used to create Trigger variablkes and use them to avoid re-done an operation already made few milliseconds ago).

This is the behaviour:

  1. group is ON (due to always-on switch) and Binary is OFF
  2. I switch ON one of Lights in that group
  3. Binary turns ON
  4. I switch OFF the same Light of before
  5. Group stays ON as the Binary too

While doing these steps I originally copied same Jinja in Developers Template page to look how result changes, and all was working well by displaying TRUE or FALSE as I expect.

If I remove the always-on Switch from the group and re-do all those 5 steps the Binary is working well because it seems the Triggers follows the group state that now would became OFF when all Lights are off.

UPDATE: adding a “time_pattern” Trigger to the Binary seems fix this just because it forces the Jinja in State to be executed again, and I’m already sure this Jinja is correct.
So after a “time_pattern” of 30 seconds the Binary returns OFF as expected.
(obliviously this is only a test because I don’t want a time_pattern Trigger in this sensor)

No, that’s false; you have misinterpreted the documentation.

The example I posted will have its template evaluated whenever one of its entities changes state. In other words, only when necessary. Review the documentation.

Rate limiting updates


I have provided you with a functional Template Binary Sensor, proven that it works (twice) and it updates its state only when necessary (i.e. when a monitored entity’s state changes). Yet you dismiss it and insist on using a Trigger-based Template Binary Sensor. Why?

Your application doesn’t need any triggers and its requirements are fulfilled by a simple Template Binary Sensor.

1 Like

Hey there! :slight_smile:

You seem to misunderstand the difference between a template binary sensor and a trigger-based template binary sensor. These are two different things, and sometimes hard to differentiate.

I’ll try to make it a little clearer. What you need for your use case, is a simple template binary sensor. The state of this TBS changes every time, one of the included entities is changed. Eg. in your TBS you work with the state of a sensor, then every time this sensor is changed, the TBS reacts on that.

Simple as that, as there is no need for a trigger, the entities used in a template sensor update fully automatically.

So, the other thing is a trigger-based template binary sensor. This is a very specific case of template binary sensor, as it only reacts, when the trigger really is updated/changed. And only then! This gives you control over when this sensor should trigger.

This is not necessary in your case, as you don’t want to exclude any change of the sensors involved. Let’s say you have a sensor, that updates every minute. Now you want to have a template binary sensor, that should only change once a day, despite how often the original sensor updates. This would be a use case for a trigger-based TBS. That’s not a particular good example, guilty as charged, but just to give you an idea.

A trigger-based template binary sensor is something you won’t use the majority of time. A “normal” template binary sensor is what you’ll use most of the time, as this is more than enough. To set specific triggers is only something you do in some rare cases.

To sum it up: you can trust Taras, especially as he has provided you with some tested examples (you should trust him anyway :laughing:), that are working! Take exactly what he had offered you and try it! You’ll see, that he’s right! :slight_smile:

hint, hint Taras is THE code guru here in this very forum, and I couldn’t say he was wrong in any of his vast answers. I know, sounds crazy, but he’s as well (a little, I think)! :laughing: Really, if you need something code related, like everything Jinja or template related, he’s the guy you’d ask. And in this case, others like me can see, that he’s right here. :slight_smile:

8 Likes

Here’s why your Trigger-based Template Binary Sensor fails to work the way you think it should.

A Template Trigger will trigger when the result produced by its template changes from false to true. It does not trigger when it changes from true to false.

Given that information, when will this template’s result change from false to true?

- trigger:
    - platform: template
      value_template: >-
        {{ expand('group.cucina_attiva') 
                | rejectattr("state", "in", ["undefined", "unavailable"]) 
                | rejectattr("entity_id", "in", ['switch.caldaia_on_off'])
                | selectattr('state', 'eq', 'on')
                | list | count > 0 }}

It will happen when none of the lights are on and then one of them is turned on. When that happens the number of illuminated lights is greater than 0 and the template’s result will change from false to true. That’s the only time this template will trigger the Template Trigger.

When all lights are turned off, the quantity of illuminated lights is no longer greater than 0 so the template’s result changes from true to false. That change does not trigger the Template Trigger. That’s why your Trigger-based Template Binary Sensor’s state changes to on but never changes to off.

The fact is that there is no need to create a Trigger-based Template Binary Sensor for your stated application. It adds additional complexity, provides no advantages over a Template Binary Sensor (in this case), and even introduces a major functional disadvantage.

I highly recommend you use the Template Binary Sensor I posted earlier.

The Time Pattern Trigger is effectively bypassing the Template Trigger and forcing the state template to be evaluated every 30 seconds.

There’s no need for the Time Pattern Trigger or the Template Trigger. A Template Binary Sensor, having no explicit triggers, will report the correct state using its template and nothing else. It works efficiently because it only updates when necessary (when one of its monitored entities changes state) and not unnecessarily according to a time pattern.

1 Like

Thx for your explanation.

Triggers are ALWAYS useful to avoid triggering unwanted-events so I’m usually use them in all my template sensors.
Sincerily I never created a template sensor without any Trigger declared…even the most easy/stupid sensor I created has a Trigger section.
I think this is the only good way to declare a template sensors. Not using any Trigger would have unpredicted behaviours and/or reduce overall performance.

My entities have a lot of Attributes and I don’t want HA wastes CPU cycles to trigger something that I don’t want like at each Attribute changes.
For example I have a smart plug that has its Status On/Off but also Attributes Voltage and Temperature that changes avery 5 seconds…and I don’t want my template sensor will be triggered all the day every 5 seconds even if the status is always Off.
This moreover in an expanded group where a lot of entities could be present into it and triggers the Sensor.

Then a Template Triggered Sensor seems unuseful because ALL triggers should be processed for both Trute-False and vice-versa cases: a light or a switch could became ON from its OFF status and then (sometime in the future) it have to return to the prev one but if the trigger won’t triggers it’s useless.
Other cases could be like mine where the Template have to listen for different kind of “things” but there isn’t any only one event to make it Off again. This because if the ON was a complex “IF” statement, the OFF will need the same complexity and not just an Off state trigger like a simple “platform: state”…

This is not necessary in your case

The case I showed here is a simplified version.
My group has 37 entities:

  • most of them have Attributes that changes every 1-5 seconds (i.e: Tuya presence sensor sends an MQTT message every single second even if the State is always “not_home”)
  • few of them are always ON and I want to read only some of their Attributes inside the Trigger (of course I could create a separated sensor from that Attribute, but I tried to do in a more cleaned way by using Template Triggers first)
  • many of them are from different Domains (I grouped switches, lights, numbers, input_booleans and input_text) so I have to exclude someone of them and/or read different things from each of them

Declaring just a template sensor without any trigger would take too much CPU resources and I don’t want this.

[…] This would be a use case for a trigger-based TBS […]

I don’t figure it out because that sensor you imagine have to return (in the future) to its OFF status, but using a TBS seems not possibile if it the Off status would not be triggered. Each sensor needs two-ways trigger…I really don’t understand why a Trigger would have to work only one-way…
Then if I made a very complex Template Trigger from False-to-True status then have I to make the same complex status (but at opposite logic) in a second Trigger? This implementation seems stupid to me…and I’m a 40years programmer worked with dozen of programming languages in all those years…

UPDATE:
adding a second “platform: template” trigger using same Jinja statement but at inversed logic (“… | count <= 0”) seems works…however this is crazy because now the expand() and all those filters would be executed three times: at Trigger 1, at this new Trigger 2 and in the State…
An in my full-version I will have at least 5 expand() function to call and 3-5 selectattr/rejectattr each one…
Very very bad.

No, that’s false.

That’s at least the second time you have misunderstood how a template in a Template entity is processed. As a consequence, you are making unnecessary and self-defeating design decisions.

That sounds like dogma and not an engineering decision.

No, that’s false. You’re repeating the same falsehood you made earlier and it implies you have not read, or understood, the documentation explaining how a Template entity’s template is evaluated.

It seems stupid because your understanding of its operation is not how it actually works. Respectfully, your extensive experience with other programming languages isn’t serving you well in this situation.

You have designed an entity that does not detect “no lights on” because you used a trigger that cannot detect it. Let that sink in for a minute. Its failure to work the way you want is not due to an internal shortcoming but to your misunderstanding of its operation.

Your intended application isn’t best served by the use of a trigger. Yet you persist to add more triggers in a valiant attempt to force it to work the way you think it should. Then you conclude that the overly complicated entity you designed indicates the “implementation seems stupid” and “very very bad”.

You’re simply using the wrong tool for the job. Your application doesn’t need or benefit from a trigger. Use the Template Binary Sensor I posted above. As previously explained, its template will be evaluated only when one of its monitored entities undergoes a state-change (to its state value, not one of its attributes).

4 Likes