Counting matching states

What is the best way to count the number of states matching a value?
I’d like to create summaries of number of open doors/windows, lights/switches turned on, etc.

Create a template sensor. Iterate over all sensors in a for loop. User variable to count.

See here for loops: http://jinja.pocoo.org/docs/2.9/templates/#for

and here for math: http://jinja.pocoo.org/docs/2.9/templates/#math

If you need an example just say the word I always give as less information as I think is needed for you to help yourself :slight_smile:

~Cheers

Thanks.
Yes, I’d like examples.
How do I use a variable in a math expression, where I want to achieve something like this?

{% set counter = 0 %}
{% for state in states.light if state.state -%}
  {{ counter + 1 }}
{%- endfor %}
{{ counter }}

How do I match substrings? E.g. for something like this:

{% for state in states.binary_sensor
   if ( state.state == 'open' and state.name contains 'door' ) -%}

For your first problem you would use something like this:

{% for state in states.light if state.state == 'on' -%}
  {% if loop.first %}
    {{loop.length}}
    {% endif %}
{% endfor %}

The problem here is jinja variable scope. I forgot about that. The counter you use in a loop stays there. And there is not really a way to get around that. But jinja offers some fields for loops like “loop.lenght” which gives the counter for the loop and as we only need it once I checked if it is the first iteration.

For your second point jinja is pretty much python so string get handled like arrays of chars. So you can build substrings like this:

{{ state.state[:2] }}

would give you “on” if the state is “on” and “of” if the state was “off”.
But what you want to do is contains which is done like this:

{% for state in states.binary_sensor
   if ( state.state == 'open' and 'door' in state.name ) -%}

~Cheers

Is there an easy way to iterate over both state.light and state.switch?
Something like this: {% for state in [ states.light, states.switch ] %}

I can’t test it right now but this should work

{% set list = states.light+states.switch %}
{% for s in list %}

Maybe even this will work:

{% for s in (states.light+states.switch) %}

~Cheers

Neither works.

If both don’t work there is no easy way I think.

~Cheers

You could iterate over all states and filter for those two domains.

~Cheers

Thanks. This works, but I think it is a bug in HA: If any of my two light entities are on, the count is 2!

       {% for state in states
           if ( 'light' in state.entity_id 
                and state.domain in ['light','switch']
                and state.state == 'on' ) -%}
         {% if loop.first -%}
           {{ loop.length }}
         {%- endif %}
       {%- endfor %}

Correction: Using loop.length always results in 2 when 1 state are ‘on’. It counts 0,2,2,3,4,5,… Why is that?

1 Like

loop.index on the last iteration gave a correct count:

       {% for state in states
           if ( 'light' in state.entity_id 
                and state.domain in ['light','switch']
                and state.state == 'on' ) -%}
         {% if loop.last -%}
           {{ loop.index }}
         {%- endif %}
       {% else %}
         0
       {%- endfor %}
1 Like

I created a dictionary of known power for each light, and I want to sum the output of the loop. How do I do that?

      {% for state in states.light if state.state == 'on' -%}
        {{ list[state.entity_id] }}
      {% endfor %}

I have 7 cams and want to get a notification when 3 or more cams are down. I have ping sensors for all of them. They are called like binary_sensor.driveway_cam, binary_sensor.frontdoor_cam etc.

Can someone help in how can rebuild above to facilitate that? Above iterates a full domain while I only want to evaluate the 7 specific binary sensors.

First you have to declare your set of the 7 cams you want to have allowed like this:

{%- set cams = ["binary_sensor.driveway_cam", "binary_sensor.frontdoor_cam", "..."] -%}

after that you iterate over all of those with a condition like this:

{%- for cam in cams if is_state(cam, 'off') -%}

{%- endfor -%}

in this loop we can check the loop.length like this:

  {% if loop.length >= 3 -%}
    //STUFF THAT HAPPENS WHEN 3 ARE DOWN
  {% endif -%}

So all in all it looks like this:

{%- set cams = ["binary_sensor.driveway_cam", "binary_sensor.frontdoor_cam", "..."] -%}
{%- for cam in cams if is_state(cam, 'off') -%}
  {% if loop.length >= 3 -%}
    //STUFF THAT HAPPENS WHEN 3 ARE DOWN
  {% endif -%}
{%- endfor -%}

Hope that helps

~Cheers


Edit: Removed debug output in last statement

Did you try the sum filter?

{{ items | sum(attribute='points') }}

~Cheers

Ow, nice! Thanks @PhyberApex

Will try to implement tonight. QQ; Although I have made some template sensors myself , that is also the only way I uses this jinja scripting. So my Q is how to use this? Probably the easiest is to make a sensor out of it that becomes true when 3 cams are down?

That’d work yes you just have to edit it a bit like this:

{%- set cams = ["binary_sensor.driveway_cam", "binary_sensor.frontdoor_cam", "..."] -%}
{%- for cam in cams if is_state(cam, 'off') -%}
  {% if loop.length >= 3 AND loop.first-%}
    true
  {% elif loop.length <= 3 AND loop.first-%}
    false
  {% endif -%}
{%- endfor -%}

The additional ´´´loop.first``` is necessary to not have it return multiple trues or false but just one.

~Cheers

I was having a play with some of the solutions in this thread and they were really helpful for getting started, but I ended up settling on a more FP style:

{{ states.light
    | selectattr('state', 'eq', 'on')
    | rejectattr('attributes.is_hue_group')
    | list
    | count
}}

Which I find a little clearer and it cleanly handles the empty list state.

6 Likes

Holy moly where did you find out about those two filters? I could not find them in the documentation!

~Cheers