Set binary sensor on basis of sensor loop

I am trying to set a binary sensor on the basis of a number of sensors of the same family. I can extract the true/false, but can I set the binary sensor on the basis of an AND of the results.

i.e. if any one of the sensors last mean of is less than 5, the sensor should be true.

{% for device in states.sensor if device.attributes.unit_of_measurement == '°C' 
      and device.attributes['sensor type'] == 'LYWSDCGQ' -%}
   {{ device.attributes['last mean of'] < 5 }}
{% endfor %}

I believe you can achieve your goal using this:

{{ states.sensor  
   | selectattr('attributes.unit_of_measurement', '==', '°C')
   | selectattr('attributes.sensor type', '==', 'LYWSDCGQ')
   | selectattr('attributes.last mean of', '<', 5)
   | list | count > 0 }}

That is really neat, brilliant! :grinning:.

1 Like

FWIW, in order to test it, I had to create an appropriate sensor containing attribute names with spaces in them. Normally, attributes use underscores. At first I thought this would work, but it doesn’t:

selectattr('attributes["sensor type"]'), '==', 'LYWSDCGQ')

However, using the test sensor I had created, I discovered this works:

selectattr('attributes.sensor type', '==', 'LYWSDCGQ')

I learned something new today. :slight_smile:

1 Like

@123 is there a way to template the entity_id list entry. From what I have found, there isn’t.

This doesn’t work.

binary_sensor:
  - platform: template
    sensors:
      sensor_fault:
        friendly_name: "Sensor Fault"
        entity_id: >-
          {% for device in states.sensor if device.attributes.unit_of_measurement == '°C' 
                and device.attributes['sensor type'] == 'LYWSDCGQ' -%}
            {{ device.entity_id }}
          {% endfor %}
        value_template: >-
          {{ states.sensor  
            | selectattr('attributes.unit_of_measurement', '==', '°C')
            | selectattr('attributes.sensor type', '==', 'LYWSDCGQ')
            | selectattr('attributes.last mean of', '<', 5)
            | list | count > 0 }}

Welcome to the Template Sensor’s Catch-22. You want it to dynamically select entities but somewhere in there you have to specify entities so Home Assistant can monitored them for changes to their state or attribute value (then the template will be evaluated). If those entities aren’t identifiable within the value_template then they must be listed in entity_id.

I think you’ll have to employ the usual workaround and use sensor.time in entity_id. It changes state every minute so that will cause the template to be evaluated every minute. It’s not the most efficient method but it will get the job done.


EDIT
There might be another way of doing this in the future.

Step one will be to create an automation that runs at startup and creates a group containing all the sensors that meet your criteria.

Step two will be to use expand('group.my_sensors') in the value_template. There’s a PR in the works to make Home Assistant actually expand the group first and then monitor the state of all group members. Currently it simply monitors the state of the group itself (not its members). This feature was implemented in 0.110 but failed to work properly. The new PR is attempting to ensure groups are initialized before Template Sensors.

With these two steps work? Maybe. Perhaps the automation I described in Step One will create the group after your Template Binary Sensor is already initialized (so the sensor may fail because the group doesn’t exist yet). Only way we’ll know for sure is after the PR is implemented and we get to experiment with the new feature.

Yes thanks, I had a feeling that was it.

BTW, because of the new startup in 0.111.x, I noticed the sensor is unavailable until all the sensors are found again. Could I use this template technique to test for availability of the sensors as well? I could not work out how to test for or return the availability status.

mf_social has a topic dedicated to the subject of creating a Template Sensor that reports the number of unavailable sensors (even supports a blacklist). I don’t know if that’s what you want but my small contribution to that project is here.

1 Like

@123, with the changes to entity_id in templates in mind, I currently list the entities to scan as in

      sensor_fault:
        friendly_name: "Sensor Fault"
        entity_id:
          - sensor.mi_t_4c65a8d8de98
          - sensor.mi_t_4c65a8d8e00c
          - sensor.mi_t_4c65a8d8e0d8
          - sensor.mi_t_4c65a8d9591a
          - sensor.mi_t_4c65a8dc8a9b
        value_template: >-
          {{ states.sensor  
            | selectattr('attributes.unit_of_measurement', '==', '°C')
            | selectattr('attributes.sensor type', '==', 'LYWSDCGQ')
            | selectattr('attributes.last mean of', '<', 5)
            | list | count > 0 }}

Will the template sensor just work without the entity IDs or do I need to include them with the set trick?

entity_id is deprecated in 0.115 so it’s already being ignored in that Template Sensor (and reports a warning in the log to remove all instances of entity_id).

As opposed to all versions prior to 0.115, Home Assistant now knows that states.sensor means every sensor in your system and will assign a listener to each and every sensor. That means if any one of those sensors changes state, it will cause the template to be evaluated.

So it will work without entity_id but, compared to previous versions, the template is evaluated far more often. Some people have reported decreased performance notably when their system has many entities and they use states in their template (listeners are assigned to all entities). In previous versions, only 5 listeners were assigned to your Template Sensor, one for each of the 5 entities listed in entity_id. In 0.115 it will assign listeners to however many sensors you have defined in your system.

The way your template is designed, it dynamically selects sensors based on certain criteria. If you don’t need this capability and can live with hard-coding the desired sensor entities in the template, then everything is simplified and listeners will be assigned exclusively to the listed entities.

On the other hand, if you want them to be dynamically selected and to minimize the number of listeners, here’s one possible approach. Create an automation with two triggers (when Home Assistant starts and when groups are reloaded), that dynamically creates a group containing sensors (the service call is group.set) with the following attributes:

'attributes.unit_of_measurement', '==', '°C'
'attributes.sensor type', '==', 'LYWSDCGQ'

Let’s say this group is called group.my_sensors. The associated Template Sensor can be reduced to this:

      sensor_fault:
        friendly_name: "Sensor Fault"
        value_template: >-
          {{ expand('group.my_sensors')
            | selectattr('attributes.last mean of', '<', 5)
            | list | count > 0 }}

This approach:

  • Preserves the dynamic nature of selecting only certain types of sensors (i.e you don’t have to 'hard-do. The group is updated every time Home Assistant is restarted or when you execute Reload Groups.
  • Minimizes the number of listeners to only the entities contained within group.my_sensors.

Thanks, this definitely seems like a move forward (not) :slight_smile:. I sort of picked this up from the main thread on it. The old way was just so elegant!

Could you expand on the automation required to create the group, please?

It would be something like this (untested):

- alias: 'Create My Sensors Group'
  trigger:
    - platform: homeassistant
      event: start
    - platform: event
      event_type: 'call_service'
      event_data:
        domain: 'group'
        service: 'reload'
  action:
    - service: group.set
      data_template:
        name: 'My Sensors'
        entities: >
          {{ states.sensor  
            | selectattr('attributes.unit_of_measurement', '==', '°C')
            | selectattr('attributes.sensor type', '==', 'LYWSDCGQ')
            | list }}

The potential glitch is the old ‘chicken and egg’ problem: if a dynamically created group includes Template Sensors that are based on other groups that are also dynamically created. Depending on the order of how things gets defined, nothing good may come out of it. Clearly it’s possible to create a circular dependency that cannot be resolved at startup.

1 Like

for that exact reason, you can do this:

  - alias: Run at startup
    id: Run at startup
    trigger:
      platform: homeassistant
      event: start
    action:
      - ...
      - ...
      - delay:
          seconds: >
            {{states('input_number.ha_delayed_startup')|int}}
      - event: delayed_homeassistant_start

  - alias: Run at delayed startup
    id: Run at delayed startup
    trigger:
      platform: event
      event_type: delayed_homeassistant_start
    action:
      - service: script.notify_delayed_startup
      - service: input_boolean.turn_off
        entity_id: input_boolean.just_started
      - service: script.run_after_delayed_startup

this is a pre HA 115 setup, because even then, things could get called too early in he startup sequence.

otoh, If adding the creation of the group to the delayed startup event, you’d have to be sure, any other automation based on the groups existence would have to start after that. which is easy, because you can now time that based on the delayed startup sequence :wink: