Expand group select attr includes unavailable sensors

Hello guys,
I do have a problem with the following configuration.
I have some temp sensors around the house and set up a fire alert to alert me if the temps get too high.
All the sensors are in a group I created.
I have also created a template binary sensor. However, everytime I restart my router, this alert gets triggered due to some wifi sensors being unavailable.
I have checked and tested but I cannot figure out why the template triggers this alert.
the binary sensor config is:

- binary_sensor:
    - name: "Fire Alarm Sensor"
      unique_id: "Fire Alarm Sensor"
      device_class: heat
      icon: >-
        {% if is_state('binary_sensor.fire_alarm_sensor', 'on')  %}
          mdi:fire
        {% else %}
          mdi:fire-off
        {% endif %}
      state: >-
        {% if (expand('group.fire_sensor_group') | selectattr('state', '>=', states('input_number.fire_alert_trigger_degree')) | list | count) > 0  %}
          True
        {% else %}
          False
        {% endif %}
      delay_on: 00:00:05
      delay_off: 00:00:05
      availability: >-
        {% set sensor_list = expand('group.fire_sensor_group') | map(attribute='state') | list %}
        {{ (not 'unavailable' in sensor_list) and (not 'unknown' in sensor_list ) }}
      attributes:
        sensors: >-
          {% if not is_state('group.fire_sensor_group', 'unavailable') %}
            {% set sensor_list = expand('group.fire_sensor_group') | selectattr('state', '>', states('input_number.fire_alert_trigger_degree')) | map(attribute='name') | list | join(', ') %}
            {{ sensor_list }}
          {% endif %}

I put these availability checks but I need to remove them since it is a possible reason that a sensor may get unavailable but the alert should run fine nevertheless.
So what happens is, when router connection reestablishes it, the binary sensor goes “on” briefly triggering the alert. I know I can increase the delay with delay_on and delay_off but this is a workaround not a solution.

When I tested more putting this statement to template editor.

{{ (expand('group.fire_sensor_group') | selectattr('state', '>=', "50") | list | count) }}

I noticed that when a wifi temp sensor (DHT22, esp32 with esphome) loses connection and becomes unavailable, the template evaluates the statement and it counts towards the count.
So could please anyone explain to me why selectattr(‘state’, ‘>=’, “50”) also counts unavailable sensors?
How can I fix it?
Thanks in advance.

1 Like

An entity’s state value is a string. That means even if the value is numeric, its type is string not integer or float.

That’s important to remember when you attempt to do something like this:

selectattr('state', '>=', states('input_number.fire_alert_trigger_degree'))

If the input_number’s value is 50 the template is effectively doing this:

selectattr('state', '>=', '50')

That’s not a numeric comparison. It’s a string comparison (the state value is a string and it’s being compared to another string).

The string unavailable is definitely greater than the string 50. To drive the point home, the string cat is also greater than the string 50.

If you want to perform numeric comparisons, then you need to convert numeric strings into integers or floating point numbers. You should also reject any entities whose state value is non-numeric like unavailable and unknown.

Copy-paste this into the Template Editor and test it:

{{ expand('group.fire_sensor_group')
    | rejectattr('state', 'in', ['unavailable','unknown'])
    | map(attribute='state') | map('int')
    | select('>=', states('input_number.fire_alert_trigger_degree')|int)
    | list | count  > 0 }}

1 Like

Beautifully explained @123. I totally forgot that this was not like python where everything just worked together. Thanks for the explanations and suggested solution.
One thing I would like to ask if you have any opinion?
Is there a simple way to check for the each item on the list is the same?
I would like to put unavailability template but binary_sensor should be unavailable only if all the items in the group report as ‘unavailable’.
I checked with the group state but it always reports ‘unknown’ even if all the items in the group is ‘unavailable’.
BTW shouldn’t in theory, groups report the same value if all the group element states are the same and all is set to True?

Another question would be how to show only the names of the devices while converting states to the int?

As you have already pointed out the states are all int so the evaluation of this template is not exactly reliable at this point:

{% set sensor_list = expand('group.fire_sensor_group') 
    | selectattr('state', '>', states('input_number.fire_alert_trigger_degree')) 
    | map(attribute='name') | list | join(', ') %}
{{ sensor_list }}

You can’t because in order to use map('int') everything except the state value is discarded.

Here’s an alternative way but it uses a string comparison. To minimize errors, it rejects obviously non-numeric values.

{{ expand('group.fire_sensor_group')
    | rejectattr('state', 'in', ['unavailable','unknown'])
    | selectattr('state', '>', states('input_number.fire_alert_trigger_degree')) 
    | map(attribute='name') | list | join(', ') }}

Hmm, got it thanks.
I guess I will use a for loop for that. I was hoping to avoid it since it introduces too much complexity to code.
Something like that:

      attributes:
        sensors: >-
          {% if not is_state('group.fire_sensor_group', 'unavailable') %}
            {% set ns = namespace(namelist=[]) %}
            {% set sensor_list = expand('group.fire_sensor_group') 
                | rejectattr('state', 'in', ['unavailable','unknown', 'None']) | list %}
            {% for sensor in sensor_list %}
              {% if sensor.state | int > states('input_number.fire_alert_trigger_degree') | int %}
                {% set ns.namelist = [sensor.name] + ns.namelist %}
              {% endif %}
            {%endfor%}
          {% endif %}
          {{ns.namelist | join(', ')}}

Did you test what I suggested and it failed?

I have put what you suggested inside the for loop. It is working! No problem, thanks (:

What I suggested doesn’t need a for-loop.

sorry for the confusion. This one is working as you suggested. I additionally created an attribute with this to show which sensors triggered the binary.