Template For Loop issues

Hi,

I have limited experience with templates and never used a for loop so might be missing something very obvious here. I have a group created using batter sensors from multiple devices.

I’d like to iterate through the entities in this group and get a count of how many are running battery level set in the helper.

{% set count = 0 %}
  {% for battery in state_attr('group.monitored_batteries', 'entity_id')  %}
    {% if (states(battery) | int) < (states('input_number.battery_levels') | int) %}
      {{ states(battery) }}
      {% set count = count + 1 %}
    {% endif %}  
  {% endfor %}
{{count}}

In the template editor, it is showing all the right values that are less than the value set in helper (> 70%) but the count still comes as “0”.

battery

What am I missing from above syntax?

Thanks!

You have defined count outside the loop as = 0, in order to extract what happens inside the loop after the loop ends you would use a namespace (see below). However, you can acheived what you are trying to do without using a loop, by using the built-in Jinja filters…

This will give you the count: EDIT: See @123’s post below :man_facepalming:t2:

{{expand(‘group.monitored_batteries’) | selectattr(‘state’, ‘lt’, states(‘input_number.battery_levels’)) | list | count}}

If you wanted to use the loop you would do the following:

{% set ns = namespace(count = 0) %}
{% for battery in expand('group.monitored_batteries') | map(attribute='state') | list %}
  {% if battery | int < states('input_number.battery_levels') | int %}
    {{ battery }}  {# remove this line if you don't want it to print the level values #}
    {% set ns.count = ns.count + 1 %}
  {% endif %}  
{% endfor %}
{{ ns.count }}
2 Likes

This part of the template:

selectattr('state', 'lt', states('input_number.battery_levels'))

is comparing strings, not numbers. It’s comparing the ASCII value of each character of one string to another.

To perform a numeric comparison, you first have to convert each string to a number (using either int or float).

{{ expand('group.monitored_batteries') | map(attribute='state')
  | reject('in', ['unavailable', 'unknown']) | map('int')
  | select('lt', (states('input_number.battery_levels') | int(0)))
  | list | count }}

The following screenshot shows the difference between performing a string vs number-based comparison. The threshold is 5 so only 1 of the group’s four values qualifies but the string-based comparison reports 2.

1 Like

Thanks @Didgeridrew Drew & @123 Taras!!

Great to learn two different methods for the same thing!!

As I am getting a better hang of template, I have really started liking Developers Tools and it is my favorite go-to place in the UI :sunglasses:

@123 I am just playing around with expand() to see its full feature functionality and stumbled upon following scenario:

{% for battery in expand('group.monitored_batteries')  %}
  {{ battery}}
{% endfor %}

Interestingly it created output like following (for each entity in the group.monitored_batteries):

<template TemplateState(<state sensor.ewelink_ms01_a33e5c24_power=66; state_class=measurement, battery_voltage=2.9, unit_of_measurement=%, device_class=battery, friendly_name=zMotionSensorFrontDoorInside @ 2022-02-01T20:25:28.421427-08:00>)>

But I was not able to tweak the template code to output the friendly_name for the entity. Is there a way to get friendly_name as part of the template output in such scenarios?

There’s more than one way to get what you want.

Example 1

{% for battery in expand('group.monitored_batteries') | map(attribute='name') %}
  {{ battery }}
{% endfor %}

Example 2

{% for battery in expand('group.monitored_batteries') %}
  {{ battery.name }}
{% endfor %}

Example 3

{{ expand('group.monitored_batteries') | map(attribute='name') | join('\n') }}

I completely missed the “map(attribute=‘name’)” part so no surprises why it did not work but I did try combinations like:

{{state_attr('battery', 'name')}}
{{battery['name']}} # thinking it might be a JSON object
And few other variants

Looking at your code I understand that “battery” is not an entity but an instance of a temporary object within the context of “for loop”. So normal features of entity like “states” or “state_attr” will not work here. Is this correct?

But why is “{{ battery.name }}” working but not “{{battery[‘name’]}}”?

Thanks, this discussion has been very educational for me!

I don’t know because it works for me:

1 Like

That’s great, at least I was thinking correctly about the whole concept but must’ve typed something incorrectly into the template editor. Thanks for confirming :grinning: :smiley: