Jinja template regex to narrow list of sensors

I would like to obtain a list of sensors that has state “on” and “update” string in the entity_id or friendly name. I have ready the part to the “and”

{{ states['binary_sensor'] | selectattr('state','eq','on') | map(attribute='name') | list | join(', ') }}

Unfortunately can not narrow to the sensors with string “update”. Tried with regex_search(“update”) in few places of that template but with no luck.

1 Like

Unfortunately there’s no way to do that template using selectattr. At least not that I’ve figured out, maybe someone has a trick, if so would love to hear it.

The problem is that you can only use tests in select and selectattr filters and Jinja does not have a built-in test for contains. Home Assistant has extended Jinja with regular expression support as you noted but regex_search and regex_match are defined as filters not tests. This means they cannot be used in a select or selectattr statement even though it makes sense to do so. I created a bug on it but looks like its an enhancement. Meant to look into it but haven’t gotten around to it.

Without those being tests you’ll have to use a for loop to get this done. Something like this should do the trick:

{% set ns = namespace(update_sensors=[],on_sensors=(states.binary_sensor | selectattr('state', 'eq', 'on') | list)) %}
{% for s in ns.on_sensors %}
  {% if "update" in s.name | lower or "update" in s.entity_id %}
    {% set ns.update_sensors = ns.update_sensors + [s] %}
  {% endif %}
{% endfor %}
{{ ns.update_sensors | map(attribute="entity_id") | unique | join(", ") }} 

EDIT: Since I got a notification that @brodykenrick liked this I should probably update it to not lead people on the wrong path. This comment here:

Home Assistant has extended Jinja with regular expression support as you noted but regex_search and regex_match are defined as filters not tests.

Is not true anymore since search and match were added as tests :tada:

So nowadays this is a better way to solve this without involving a for loop:

{% set update_name = states.binary_sensor | selectattr('state', 'eq', 'on')
    | selectattr('name', 'search', '(?i)update') | list %}
{% set update_id = states.binary_sensor | selectattr('state', 'eq', 'on')
    | selectattr('entity_id', 'search', 'update') | list %}
{{ (update_name + update_id) | map(attribute='entity_id') | unique | join(", ") }}

Although that or still requires multiple concatenated templates instead of a single one (either the name OR the ID contains update). If you only care about entities that have update in their ID then you can simplify this to one template:

{{ states.binary_sensor | selectattr('state', 'eq', 'on') | selectattr('entity_id', 'search', 'update')
    | map(attribute='entity_id') | unique | join(", ") }}
2 Likes

I avoid using the entity’s name to store metadata. Why? For the reason you have already discovered, it’s challenging to use it in a query.

My preference is to use custom attributes. They’re easy to add to entities and, more importantly, easy to use in a template. If the idea interests you, refer to the following post:

1 Like

@CentralCommand
Thanks Mike. It works great!

@123
I have no clear opinion about storing data in entity’s name but “update” is standard entity_id fragment for some devices which have info about software updates as one of the entities (shelly e.g.).
Changing entities default id names is something also to consider. Especially for customizations which has their own logic in naming.
Changing entities friendly_names is another topic.
Another one, for me, ore general for HA to consider is: should devices store data as separate entities or attributes?

But anyway, additional, custom attributes sounds smart. I should try that logic.
And thanks a lot for guide in naming entities. It was on my ToDo list to take a closer look at that topic.

You can move the entire if statement up one line and combine it with the for statement. That will result in the for-loop iterating only through the desired binary_sensors (as opposed to iterating through all binary_sensors that are on).

2 Likes

Nice, forgot about that, that would be cleaner

… is there a chance to take to the list, in a smart way, only characters before the second white space of name?
And the way to use new line sign instead of , . Because \n works great in developer tools but for notify service make an error

and… for numbers should it works?:

{% set ns = namespace(low_battery_sensors=[],on_sensors=(states.sensor | select('greaterthan', 5) | select('lessthan', 11) | list)) %}
{% for s in ns.on_sensors %}
  {% if "battery" in s.entity_id  %}
    {% set ns.low_battery_sensors = ns.low_battery_sensors + [s] %}
  {% endif %}
{% endfor %}
{{ ns.low_battery_sensors | map(attribute="name") | unique | join(", ") }} 

Sure, just a regex.
jinja</s> <s>{{ "my name is mike" | regex_replace('^((?:\\S+\\s+){2}).*$', '\\1') }}</s> <s>
Result: my name

EDIT: actually I guess this would be better, you probably don’t want an extra whitespace character on the end:

{{ "my name is mike" | regex_replace('^(\\S+\\s+\\S+)\\s+.*$', '\\1') }}

Result: my name

Not really sure what this means? The template above uses join(", "). If you want newlines in dev tools can switch it to join("\n"). Does that work? you might have to clarify otherwise.

Not quite. Unfortunately numbers are harder because state is a string for all entities and there is no mapattr option to easily convert it without losing access to the rest of the entity data. So you’ll have to do something like this:

{% set ns = namespace(low_battery_sensors=[]) %}
{% for s in states.sensor if "battery" in s.entity_id %}
  {% set value = s.state | float %}
  {% if value > 5 and value < 11 %}
    {% set ns.low_battery_sensors = ns.low_battery_sensors + [s] %}
  {% endif %}
{% endfor %}
{{ ns.low_battery_sensors | map(attribute="name") | unique | join(", ") }}
1 Like