Template to list sensors with state 'low' or <= 20.0

I’m trying to create template to list sensors (friendly names of the sensors) with states: ‘low’ or <= 20.0
Sensor match is working but there is problem with adding matched sensors to the comma separated list

Code
{% set sensors_to_check = [
  'sensor.contact_sensor_battery',
  'sensor.entrance_door_battery',
  'sensor.mateusz_s_door_battery',
  'sensor.shower_door_contact_sensor_battery',
  'sensor.shower_motion_battery_state',
  'sensor.shower_sensor_battery',
  'sensor.motion_sensor_sink_battery_state',
  'sensor.t_h_sensor_battery_state',
  'sensor.motion_sensor_toilet_battery_state',
  'sensor.tomasz_s_door_battery'
] %}

{% set string_sensors = [
  'sensor.shower_motion_battery_state',
  'sensor.motion_sensor_sink_battery_state',
  'sensor.t_h_sensor_battery_state',
  'sensor.motion_sensor_toilet_battery_state'
] %}

{% set numeric_sensors = sensors_to_check | reject('in', string_sensors) %}

{# Define thresholds and matching criteria #}
{% set numeric_threshold = 20.0%}
{% set string_match = 'low' %}

{# Initialize the list for matching sensor names #}
{% set matching_sensors = [] %}

{# Collect matching sensors for string sensors #}
{% set string_matches = [] %}
{% for sensor in string_sensors %}
  {% set sensor_state = states(sensor) | string | trim %}
  {% if sensor_state == string_match %}
    {% set string_matches = string_matches + [sensor.split('.')[1]] %}
    {{ sensor }}
  {% endif %}
{% endfor %}

{# Collect matching sensors for numeric sensors #}
{% set numeric_matches = [] %}
{% for sensor in numeric_sensors %}
  {% set sensor_state = states(sensor) | string | trim %}
  {% set sensor_value = sensor_state | float(0) %}
  {% if sensor_value <= numeric_threshold %}
    {% set numeric_matches = numeric_matches + [sensor.split('.')[1]] %}
    {{ sensor }}
  {% endif %}
{% endfor %}

{# Combine and format matching sensor names #}
{% set all_matches = string_matches + numeric_matches %}
{% set formatted_matches = all_matches | join(', ') %}

{# Output the results #}
Matching Sensors:
{% if true %}
  {{ formatted_matches }}
{% else %}
  Off
{% endif %}
Debug code
{% set sensors_to_check = [
  'sensor.contact_sensor_battery',
  'sensor.entrance_door_battery',
  'sensor.mateusz_s_door_battery',
  'sensor.shower_door_contact_sensor_battery',
  'sensor.shower_motion_battery_state',
  'sensor.shower_sensor_battery',
  'sensor.motion_sensor_sink_battery_state',
  'sensor.t_h_sensor_battery_state',
  'sensor.motion_sensor_toilet_battery_state',
  'sensor.tomasz_s_door_battery'
] %}

{% set string_sensors = [
  'sensor.shower_motion_battery_state',
  'sensor.motion_sensor_sink_battery_state',
  'sensor.t_h_sensor_battery_state',
  'sensor.motion_sensor_toilet_battery_state'
] %}

{% set numeric_sensors = sensors_to_check | reject('in', string_sensors) %}

{# Define thresholds and matching criteria #}
{% set numeric_threshold = 100.0 %}
{% set string_match = 'high' %}

{# Initialize the list for matching sensor names #}
{% set matching_sensors = [] %}

{# Collect matching sensors for string sensors #}
{% set string_matches = [] %}
{% for sensor in string_sensors %}
  {% set sensor_state = states(sensor) | string | trim %}
  {% if sensor_state == string_match %}
    {% set string_matches = string_matches + [sensor.split('.')[1]] %}
    {{ sensor }}
  {% endif %}
{% endfor %}

{# Collect matching sensors for numeric sensors #}
{% set numeric_matches = [] %}
{% for sensor in numeric_sensors %}
  {% set sensor_state = states(sensor) | string | trim %}
  {% set sensor_value = sensor_state | float(0) %}
  {% if sensor_value <= numeric_threshold %}
    {% set numeric_matches = numeric_matches + [sensor.split('.')[1]] %}
    {{ sensor }}
  {% endif %}
{% endfor %}

{# Combine and format matching sensor names #}
{% set all_matches = string_matches + numeric_matches %}
{% set formatted_matches = all_matches | join(', ') %}

{# Output the results #}
Matching Sensors:
{% if true %}
  {{ formatted_matches }}
{% else %}
  Off
{% endif %}
Debug output

sensor.shower_motion_battery_state
sensor.motion_sensor_sink_battery_state
sensor.motion_sensor_toilet_battery_state
sensor.contact_sensor_battery
sensor.entrance_door_battery
sensor.mateusz_s_door_battery
sensor.shower_door_contact_sensor_battery
sensor.shower_sensor_battery
sensor.tomasz_s_door_battery

Matching Sensors:

Sorry for any errors English isn’t my first language and i’m new to the forum

Smaller code and rewrote it:

{% set threshold = 100.0 %}
{% set entities = states.sensor 
  | selectattr('entity_id', 'search', 'battery')
  | rejectattr('entity_id', 'search', '(note|sm|quest|motorola|pot)')
  | map(attribute = 'entity_id') | list %}
{% if (entities != '') %}
  {% for sensor in entities if states(sensor) != 'unknown' and states(sensor) != 'unavailable'
and states(sensor) != 'None' and states(sensor) != 'middle' and states(sensor) != 'high' and states(sensor) | int(default=0) <= int(threshold) %}
    {{ state_attr(sensor, 'friendly_name') | replace(" Battery", "") | replace(" Level", "") | replace(" power", "") | replace(" Power", "") }} ({{ states(sensor) }} %)
    {%if states(sensor) == 'low'%}
      {{ state_attr(sensor, 'friendly_name') | replace(" Battery", "") | replace(" Level", "") | replace(" power", "") | replace(" Power", "") }} ({{ states(sensor) }})
    {%endif%}
  {% endfor %}
{% endif %}

It works little better but still can’t print them in comma separated list

Formatted for use
{% set threshold = 100.0 %}
{% set entities = states.sensor 
  | selectattr('entity_id', 'search', 'battery')
  | rejectattr('entity_id', 'search', '(note|sm|quest|motorola|pot)')
  | map(attribute = 'entity_id') | list %}
{% if (entities != '') %}
  {% for sensor in entities if states(sensor) != 'unknown' and states(sensor) != 'unavailable'
and states(sensor) != 'None' and states(sensor) != 'middle' and states(sensor) != 'high' and states(sensor) | int(default=0) <= int(threshold) %}
{%if states(sensor) == 'low'%}
  {{ state_attr(sensor, 'friendly_name') | replace(" Battery", "") | replace(" Level", "") | replace(" power", "") | replace(" Power", "") }} ({{ states(sensor) }}){%endif%}{{ state_attr(sensor, 'friendly_name') | replace(" Battery", "") | replace(" Level", "") | replace(" power", "") | replace(" Power", "") }} ({{ states(sensor) }} %){% endfor %}
{% endif %}

Output:

shower sensor (100.0 %)
Contact Sensor Bathroom (100.0 %)
Mateusz's Door (100.0 %)
Tomasz's door (100.0 %)
Entrance door (100.0 %)
Shower Door Contact Sensor (100.0 %)
Battery Sensors (100.0 %)

if you put your numeric sensors with a label “numeric_sensors” and the low/med/high on to have a label “string_sensors”, you could do the following to get each of them in their own lists.

{{ label_entities('numeric_sensors') | selectattr('state', 'le', '90') | map(attribute='name') | list }}
{{ label_entities('string_sensors') | selectattr('state', 'eq', 'low') | map(attribute='name') | list }}

if you want them in a single list, put them all in ‘selected_sensors’ label and this should do it.

{{ label_entities('selected_sensors') | rejectattr('state', 'gt', '90') | rejectattr('state', 'ne', 'low') | map(attribute='name') | list }}
1 Like
{% set selected_sensors = [
  'sensor.contact_sensor_battery',
  'sensor.entrance_door_battery',
  'sensor.mateusz_s_door_battery',
  'sensor.shower_door_contact_sensor_battery',
  'sensor.shower_motion_battery_state',
  'sensor.shower_sensor_battery',
  'sensor.motion_sensor_sink_battery_state',
  'sensor.t_h_sensor_battery_state',
  'sensor.motion_sensor_toilet_battery_state',
  'sensor.tomasz_s_door_battery'
] %}

{{ label_entities('selected_sensors') | rejectattr('state', 'gt', 90 ) | rejectattr('state', 'ne', 'low') | list }}

Output:
[]

my code has them as labels, not as a list.
if you want to do it via a jinja list, then do this:

{% set selected_sensors = [
  'sensor.contact_sensor_battery',
  'sensor.entrance_door_battery',
  'sensor.mateusz_s_door_battery',
  'sensor.shower_door_contact_sensor_battery',
  'sensor.shower_motion_battery_state',
  'sensor.shower_sensor_battery',
  'sensor.motion_sensor_sink_battery_state',
  'sensor.t_h_sensor_battery_state',
  'sensor.motion_sensor_toilet_battery_state',
  'sensor.tomasz_s_door_battery'
] %}

{{ expand(selected_sensors) | rejectattr('state', 'gt', '90' ) | rejectattr('state', 'ne', 'low') | map(attribute='name') | list }}

i personally prefer to do things like this as labels because if you add or remove one, you can do that in the ui and then all the code that enumerates them automatically update. whereas if you do this list approach that you have, you have to go to each code location where you are enumerating them and update it.

1 Like

Sorry i don’t understand yaml very well, i’m just learning it now.
[Edit] Oh… labels are for that…

Output
TypeError: ‘>’ not supported between instances of ‘str’ and ‘int’

[Edit] Oh… labels are for that…

Now with labels i get other error:
Code: {{ label_entities('selected_sensors') | rejectattr('state', 'gt', 90 ) | rejectattr('state', 'ne', 'low') | list }}
UndefinedError: 'str object' has no attribute 'state'

That’s why it doesn’t work

@armedad
fixed code converting labels to lists
i had to separate numerical values from strings

{% set numeric_sensors = label_entities('numeric_sensors') %}
{% set string_sensors = label_entities('string_sensors') %}

{# Filter string sensors with state 'low' #}
{% set low_string_states = expand(string_sensors) 
   | selectattr('state', 'eq', 'low') 
   | map(attribute='state') 
   | list %}

{# Filter numeric sensors, convert state to float, and compare #}
{% set low_numeric_states = expand(numeric_sensors)
   | map(attribute='state') 
   | map('float') 
   | select('le', 25.0) 
   | list %}

{# Combine both results into a single variable #}
{% set combined_states = low_string_states + low_numeric_states %}

{# Output combined states #}
{% if combined_states | length > 0 %}
{{ combined_states | join(', ')}}
{% else %}
  off
{% endif %}

Debug

debug code:

{% set numeric_sensors = label_entities('numeric_sensors') %}
{% set string_sensors = label_entities('string_sensors') %}

{# Filter string sensors with state 'low' #}
{% set low_string_states = expand(string_sensors) 
   | selectattr('state', 'ne', 'low') 
   | map(attribute='state') 
   | list %}

{# Filter numeric sensors, convert state to float, and compare #}
{% set low_numeric_states = expand(numeric_sensors)
   | map(attribute='state') 
   | map('float') 
   | select('ge', 25.0) 
   | list %}

{# Combine both results into a single variable #}
{% set combined_states = low_string_states + low_numeric_states %}

{# Output combined states #}
{{ combined_states }}

debug output:

Revised code that outputs the name:

{% set numeric_sensors = label_entities('numeric_sensors') %}
{% set string_sensors = label_entities('string_sensors') %}

{# Filter string sensors with state 'low' #}
{% set low_string_states = expand(string_sensors) 
   | selectattr('state', 'eq', 'low') 
   | map(attribute='name') 
   | list %}

{# Convert sensor states to float #}
{% set numeric_states_with_names = expand(numeric_sensors)
   | map(attribute='state')
   | map('float', default=0.0)
   | list %}

{# Manually create list of tuples (sensor name, state) #}
{% set named_states = namespace(pairs=[]) %}
{% set sensors = expand(numeric_sensors) %}

{% for i in range(sensors | length) %}
  {% set sensor = sensors[i] %}
  {% set state = numeric_states_with_names[i] %}
  {% set named_states.pairs = named_states.pairs + [(sensor.name, state)] %}
{% endfor %}

{# Filter based on the state and extract names #}
{% set low_numeric_states = named_states.pairs
   | selectattr('1', 'le', 25.0)
   | map(attribute='0')
   | list %}

{# Combine both results into a single variable #}
{% set combined_states = low_string_states + low_numeric_states %}

{# Output combined states #}
{% if combined_states | length > 0 %}
{{ combined_states | join(', ')}}
{% else %}
  off
{% endif %}

Thank you @armedad.

You’ll be able to simplify the tuple portion of the code once the zip function becomes available.

1 Like

glad you got it to work for you.

in terms of what the error is…

back at your post right after my very first one, you changed the ‘90’ to 90. i didn’t catch that, but that’s incorrect. since i didn’t catch that you changed it then in my reply, i left it as 90 (i’ve gone back and edited it).

you needed to keep the ‘90’ as i had in my original post.

so as an array you should use this verbatim:

{% set selected_sensors = [
  'sensor.contact_sensor_battery',
  'sensor.entrance_door_battery',
  'sensor.mateusz_s_door_battery',
  'sensor.shower_door_contact_sensor_battery',
  'sensor.shower_motion_battery_state',
  'sensor.shower_sensor_battery',
  'sensor.motion_sensor_sink_battery_state',
  'sensor.t_h_sensor_battery_state',
  'sensor.motion_sensor_toilet_battery_state',
  'sensor.tomasz_s_door_battery'
] %}

{{ expand(selected_sensors) | rejectattr('state', 'gt', '90' ) | rejectattr('state', 'ne', 'low') | map(attribute='name') | list }}

this will give you the friendly names. if you want the entity id, change ‘name’ to ‘entity_id’

if you don’t want to use arrays but have all your sensors marked with a label named ‘selected_sensors’

then you can use:

{{ label_entities('selected_sensors') | rejectattr('state', 'gt', '90' ) | rejectattr('state', 'ne', 'low') | map(attribute='name') | list }}

You’d need to map the states to float() otherwise you run into issues with string comparison.

For example this renders as true
{{ '90' > '100' }}

1 Like

This can be simplified :slight_smile:

{% set low_threshold = 25 %}
{% set numeric_sensors = label_entities('numeric_sensors') %}
{% set string_sensors = label_entities('string_sensors') %}

{# Filter string sensors with state 'low' #}
{% set low_string_states = expand(string_sensors) 
   | selectattr('state', 'eq', 'low') 
   | map(attribute='name') 
   | list %}

{# Add numeric sensor names below threshold #}
{% set ns = namespace(low_states=low_string_states) %}
{% for s in numeric_sensors if states(s) | float(101) <= low_threshold %}
  {% set ns.low_states = ns.low_states + [state_attr(s, 'friendly_name')] %}
{% endfor %}

{# Output combined states #}
{% if ns.low_states | length > 0 %}
  {{ ns.low_states | join(', ') }}
{% else %}
  off
{% endif %}

@mekaneck also no need for zip then

No need for sure, but even in your code it would eliminate the for loop and the use of namespace(). But perhaps at the expense of another line or two of code.

1 Like

code:
{{ label_entities(‘batt_sensors’) | rejectattr(‘state’, ‘gt’, ‘90’ ) | rejectattr(‘state’, ‘ne’, ‘low’) | map(attribute=‘name’) | list }}
Error:
UndefinedError: ‘str object’ has no attribute ‘state’

Exactly

I tried to use loops with list appending but it seems my installation has some problem with that.
Even when i change low_threshold to 100 it says off

You are using label entities which returns a list of entities.
To access the state attribute you first need to use expand
But this won’t work as you are doing a string comparison on numeric values, and as explained earlier "90" < "100" will return false

1 Like

Did you assign labels to the entities?

1 Like

@TheFes Yes i did.

if i add:

{{numeric_sensors + string_sensors}}
ns.low_states:{{ns.low_states}} length: {{ns.low_states | length}}

@TheFes edited becouse of reply time limit

Oh… my bad
all are at 100…
thought it was <= not <
sorry… newbie mistake.
Now it works…

Thank you