Selectattr to match part of the attribute

Is there a filter in selectattr to match part of the attribute…

say I want to get all the entitiy_id with battery in their name:

{{ states   | selectattr('entity_id', 'match', 'battery_level')
                | map(attribute='name')
                | list
                | join(', ') }}

Did you try

selectattr('entity_id', 'match', '*battery*')

I can’t test at the moment.

Maybe this part of the docs helps as qell.

See this topic.

it’s not possible using select_attr because it doesn’t have a test to check parts of a string. selectattr is all or nothing. Well… I lied, there is a way to do it for the front of a string only. It’s painful…

{{ states | selectattr('entity_id.0', 'eq', 'f') 
          | selectattr('entity_id.1', 'eq', 'o') 
          | selectattr('entity_id.2', 'eq', 'o') }}

That would check to see if the first part of your entity_id starts with foo. But that many generators would be a hog. Better off just using a for loop.

This is quite an old topic but may be of some use to you:

          {% set ns = namespace(battery=[]) %}
          {% for s in states.sensor if 'battery_level' in s.entity_id %}
            {% set ns.battery = ns.battery + [ s ] %}
          {% endfor %}
          {{ ns.battery | map(attribute='name') | join(', ') }} 

If you wish to use that to create a “battery alert” sensor, that indicates how many devices currently have low battery levels, there’s a recent thread about that very same subject here (same as Troon’s link).

For example, this will report how many sensors (whose entity name contains ‘battery_level’) have levels below 50. The names of the corresponding sensors are contained in an attribute called battery_low.

sensor:
- platform: template
  sensors:
    battery_alert:
      entity_id: sensor.date
      value_template: >
        {% set ns = namespace(below=[]) %}
        {% for s in states.sensor 
              if 'battery_level' in s.entity_id and s.state != 'unknown' and s.state|int < 50 %}
          {% set ns.below = ns.below + [ s ] %}
        {% endfor %}
        {{ ns.below | count }} 
      attribute_templates:
        battery_low: >
          {% set ns = namespace(below=[]) %}
          {% for s in states.sensor 
                  if 'battery_level' in s.entity_id and s.state != 'unknown' and s.state|int < 50 %}
            {% set ns.below = ns.below + [ s.name ~ ' (' ~ s.state ~ '%)'] %}
          {% endfor %}
          {{ ns.below | join(', ') }}
3 Likes

Thanks for suggestions, actually I wanted to create a sensor that count the number of sensors with zero battery.

I figured out another way to create the filter using device_class as follow:

          {% set count = states | selectattr("attributes.device_class", 'eq', 'battery')
                                | selectattr('state', 'eq', '0')
                                | map(attribute='entity_id')
                                | list
                                | length %}
          {{ count }}
2 Likes

That’s a fine solution, but not the answer to your original question:

2 Likes

This is a great solution, thanks! Sorry for bumping this old thread, but I think this is the proper solution to the original question.

As a test for the template editor, this will print the entity_id of all sensors with “battery” in their id.

{% for state in states.sensor | selectattr('entity_id', 'match', '.*battery.*') %}
     {{state.entity_id}}
{% endfor %}
9 Likes

Let’s mark this as solution!

Was searching like crazy (initially for checking the end of a string (friendly_name or entity_id) with string$ which never worked, while using *string$ gave an error - using .*string$ finally works :partying_face: - seems like * needs . as kind of an escape character)

That’s an interesting interpretation but the period doesn’t serve as an escape character.

The match test accepts a regex pattern (REGular EXpression) so the period has special meaning. It serves as a wildcard character to indicate “match any character”. The asterisk following the period serves to “match zero or more of the preceding character”. Therefore the combination of period and asterisk means “match zero or more characters of any kind”.

1 Like

Got it, interesting. I’m just wondering why this works (one of few where I use * for a long time):

{{ (states.input_boolean | selectattr('entity_id','match','input_boolean.*_sonnenschutz_aktiv') | selectattr('state','eq','on') | list | length) }}

which gives all entities of entity_id input_boolean.*_sonnenschutz_aktiv like

  • :white_check_mark: input_boolean.front_sonnenschutz_aktiv or
  • :white_check_mark: input_boolean.back_sonnenschutz_aktiv etc.

Why wouldn’t it work? :slightly_smiling_face:

You’re assuming that * is the wildcard, but you’re actually using .* as the wildcard — and that includes your ..

It’d also match input_boolean_no_dot_here_sonnenschutz_aktiv.

2 Likes

hi, I would like to display all device with low battery level. The following obviously does not work because state is not numeric. Could you please show me how to make it work?

{{ states.sensor
  | selectattr('attributes.device_class', 'defined')
  | selectattr('attributes.device_class', 'eq', 'battery')
  | selectattr('state', 'lt', 25) <- this will not work
  | map(attribute='attributes.friendly_name' )
  | list }}

Go to the search bar, type “battery level template”, hit the Enter key… there are multiple ways to do this depending on exactly what you need as your output.

{% set ns = namespace(below=[]) %}
{% for s in states.sensor
    | selectattr('attributes.device_class', 'defined')
    | selectattr('attributes.device_class', 'eq', 'battery')
    if has_value(s.entity_id) and s.state|int(0) < 25 %}
  {% set ns.below = ns.below + [ s.name ] %}
{% endfor %}
{{ ns.below }}
1 Like