Setting a variable in a loop doesn't work?

The following always returns zero?? … How can I get this value:

{% set Count = 0 %}
{% for state in states.device_tracker %}
    {% if loop.first %}
        {% set Count = loop.length %}
    {% endif %}
{% endfor %}
{{ Count }}

Thanks,
Kris

I believe I just read tonight that some python flaw clears / resets all ‘set’ variables / values at the end of a loop. I just tried the same thing in a sensor myself today and had to go another route to get it to work correctly…

Use the namespace object.

https://jinja.palletsprojects.com/en/master/templates/#assignments

Here’s a working example from my config.

        {% set devices = state_attr('group.devices_connected', 'entity_id') %}
        {% set dev = namespace(value=0) %}
        {% for entity_id in devices -%}
          {% if states(entity_id) == 'unknown' %}{% set dev.value = dev.value + 1 %}
          {% elif states(entity_id) == 'unavailable' %}{% set dev.value = dev.value + 1 %}
          {% endif %}
        {%- endfor %}
        {% if dev.value | int > 0 %}
          {{ unavail }}
        {% else %}
          None
        {% endif %}
11 Likes

If the goal is to get the total number of device_trackers, there’s no need to use a for-loop.

{{ states.device_tracker | count }}

Here’s the proof, using binary_sensor instead of device_tracker. The total is 23 using either technique.

2 Likes

No, I think it was just demonstrating how to get a value out :man_shrugging:

I believe I just read tonight that some python flaw clears / resets all ‘set’ variables / values at the end of a loop.

While the templates look like python, it’s not python code that’s running. The jinja2 template engine has its own semantics. I’m not saying the behavior you describe is a bug or a feature, just be careful of your starting assumptions… The jinja2 web site has lots more detail, though the docs are mostly from the PoV of someone integrating it into their software stack primarily.

Well that would be the way to do it given that, in Jinja2, a variable’s scope within a for-loop is limited to the for-loop.

FWIW, I’ve come across several examples of for-loops that can be replaced with filters. I realize the ‘loop counting’ example may only be for demonstration purposes (to learn how to ‘get the value out of the loop’). However, if anyone wishes to simply count entities, they shouldn’t use that ‘for-loop’ example as the means to do it.

Yes I did want to do it for this loop … can you do it like @123 suggested with |count conditionally?:

for state in states.device_tracker if (state.state == 'home' and state.attributes.home_member == true)

… I just +1 in the loop :slight_smile:

Yes.

{{ states.device_tracker | selectattr('state', 'eq', 'home') 
                         | selectattr('attributes.home_member', 'eq', true) 
                         | list | count }}

You can define the template using one line or multiple lines. In the example, I used multiple lines to make it easier to read.

2 Likes

It’s a feature. Jinjas for loop scope is tailored to only display info, not to handle complex scenarios that arise in HA. HA is definitely using jinja beyond what it was originally meant for.

I know when I first started investigating templates in Home Assistant being surprised when I discovered the “filter” syntax in the templates, and trying to reconcile that with how you shove that into python. And it became very clear that it’s not python embedded in templates, but an entirely different language.

Just a bump in the road when spinning up Home Assistant.

And I wish embedded in the docs when the YAML files are introduced it’s made very clear and explicit that YAML is defining a data structure and that all the weird rules for whitespace and syntax are about defining lists and dictionaries. Then it sort of becomes more clear why things are they way they are, and you don’t have people puzzling over when to indent and when you need that “-” character in there.

It’s attempted in the Yaml section of the docs but it’s definitely directed at people who understand things instead of bringing it down to a beginner level.