Counting entities with a specific string in the object id

I’m trying to create a count of entities that contain the string “node_status” in the object_id. I’m then going to use that value in another part of the same template in a sensor.

Right now I’m manually creating a group for entities and counting the members of the group. But I’d like to not need to add members to the group when I add other entities and just automatically count them since they all have “node_status” in the object_id.

The original template using the group that works is:

{% set total = (state_attr('group.zwavejs_devices','entity_id') | count) %}
{% set dead = expand('group.zwavejs_devices')|selectattr('state', 'in', 'dead, unavailable, unknown')|map(attribute='entity_id') | list | count %}
{{ total - dead }}/{{ total }}

I can get a total count of entities using this in the template editor:

{% for state in states if (state.entity_id.find("node_status") | int>=0) %}
  {% if loop.first %}
    {{loop.length}}
  {% endif %}
{% endfor %}

it gives me a result of 29.

but I can’t use the count outside of the for loop and then use it to get the “total” in the above template.

I tried the following but it doesn’t return anything (no errors or complaints in the template editor):

{% set ns = namespace(count=0) %}
{% for state in states %}
  {% if (state.entity_id.find("node_status") | int>=0) %}
    {% set ns.count = ns.count + 1 %}
  {% endif %}
{% endfor %}
  {{ ns.count }}

I admit I have really no clue when it comes to using “namespace”.

I’ve looked in the forums for examples and used them to come up with the template above.

And I’ve looked in the jinja2 docs for “namespace” usage but it’s pretty limited and it looks like what I’m doing should work.

Any idea where I’m going wrong?

This all became much easier when regex tests were added as Jinja filters. It should be as simple as this to count the number of entities with node_status in their object_id:

{{ states|selectattr('object_id', 'search', '.*node_status.*')|list|count }}

The first two tests here were added around the beginning of the year: https://www.home-assistant.io/docs/configuration/templating/#regular-expressions

For what it’s worth, this, which is exactly what you had with some extra - added to remove whitespace, returns exactly the same value:

{% set ns = namespace(count=0) %}
{%- for state in states -%}
  {%- if (state.entity_id.find("node_status") | int>=0) -%}
    {% set ns.count = ns.count + 1 %}
  {%- endif -%}
{%- endfor -%}
  {{ ns.count }}
2 Likes

FWIW, for search, you can simply specify just the sub-string you want (a full regex pattern isn’t mandatory).

{{ states | selectattr('object_id', 'search', 'node_status') | list | count }}

For example, a count of all binary_sensors containing the string “door”.

2 Likes

Thanks to both of you for the help.

but it’s interesting that you said what I had originally should have worked.

Checking again I think I’ve run up against a strange behavior in the template editor.

If I put any of the individual code blocks into the editor then every individual one returns the correct value. Or if I put in both of just the “basic” templates provided by both of you at the same time it returns both values correctly:

and if I then add the loop template in after those two it works as expected:

And if I add the ns block at the beginning then everything is fine too.

but that’s where the correct results stop…

if I add the namespace code block to the end it doesn’t output any value:

I even tried changing the ns variable just in case it was interfering but it made no difference.

There are a few different ways of ordering the code block entries that will give different results depending on which way the blocks are entered.

Instead of listing all of the different ways I tried ordering them here are the different template blocks I used if you’d like to try it and see if you can come up with an explanation on why it gives correct results with some particular order and not with others:

{{ states|selectattr('object_id', 'search', '.*node_status.*')|list|count }}

{{ states|selectattr('object_id', 'search', 'node_status')|list|count }}

{% set ns = namespace(count=0) %}
{% for state in states %}
  {% if (state.entity_id.find("node_status") | int>=0) %}
    {% set ns.count = ns.count + 1 %}
  {% endif %}
{% endfor %}
  {{ ns.count }}

{% for entity in states if (entity.entity_id.find("node_status") | int>=0) %}
  {% if loop.first %}
    {{loop.length}}
  {% endif %}
{% endfor %}

I don’t think any of the variables are walking on each other.

otherwise I’ll call this one solved for the original question - it worked as I originally had it (minus the renderer weirdness) but you guys showed me a more efficient way of doing it. :slightly_smiling_face:

It does except you can’t see it because there are many blank lines in the results pane. The orange text: ‘This template listens for all state changed events’ isn’t visible because it’s waaay down on the bottom of many blank lines (and the result you want), one blank line for each entity that doesn’t contain ‘node_status’.

You can add hyphens to control whitespace {%- if etc -%}

:flushed: :flushed:

Well, that’s embarrassing…

nevermind…

It was working all the time but I was to dumb to know it. :laughing:

That’s the first time I’ve been bitten by whitespace that I didn’t notice.

Thanks again!

Don’t feel bad; I had the same reaction you did.

I copy-pasted your code, modified it to fit my environment and (mistakenly) assumed there was no output from the for-loop … which made no sense.

The explanation became self-evident only after I constrained the number of eligible entities to states.binary_sensor and noticed the desired result was several lines after many blank lines … and then there was a Doh! moment. :slight_smile:

1 Like

I’m looking to create some template entities that provide the number of z-wave nodes in various states. Pulling from the examples above, I’ve come up with the following:

{{ states|selectattr('object_id', 'search', 'node_status')|selectattr('state', 'search', 'alive')|list|count }}
{{ states|selectattr('object_id', 'search', 'node_status')|selectattr('state', 'search', 'asleep')|list|count }}
{{ states|selectattr('object_id', 'search', 'node_status')|selectattr('state', 'search', 'awake')|list|count }}
{{ states|selectattr('object_id', 'search', 'node_status')|selectattr('state', 'search', 'dead')|list|count }}

This is providing the expected values, however, I’m wondering if this is poor form as the Developer Tools template page lists This template listens for all state changed events. Is that cause for concern or will these function without issue as template entities?

The use of states casts a wide net; it listens to every entity in your system. This is known to be resource intensive so Rate Limiting is imposed.

If you know exactly what kind of entity domains you want to monitor, the template becomes more efficient and there’s far less rate limiting imposed.

For example:

{{ expand(states.sensor, states.binary_sensor, states.switch) | selectattr('object_id', 'search', 'node_status') ... etc }}
1 Like

I was able to make some of the code above work for my case:
I am trying to get all entry sensors from my simplisafe integration.
So everything with entry, remove the battery (as each sensor has an entity with _battery appended.
In this case, I am looking at all that are off…

{{ expand(states.binary_sensor) 
   | selectattr('object_id', 'search', 'entry') 
   | selectattr('state', 'search', 'off') 
   | rejectattr('object_id', 'search', 'battery') 
   | list | count 
}}

My question is, how can I add an attribute of area? Meaning get the sensors as listed above, but only in a specific area or combination of areas?
And also, is this the best approach for what I am trying to accomplish?

Also, is there a way to also retrieve the list of entities which are off? as a string or something?