Why is it necessary to add these strange filters using selectattr?

Hi guys,
I want to show a counter with devices of which the battery level is below a certain value (in this case, 40%). However, when battery levels are “100”, “100.0”, I need to add filters to exclude them. Does anyone know why? Below is the code used.
Thanks for your thoughts on this!

Patrick

{{ states
| selectattr('attributes.device_class', 'defined')
| selectattr('entity_id', 'contains', 'battery_level')
| selectattr('state',  '<', '40')
| selectattr('state',  '!=', '100.0')
| selectattr('state',  '!=', '100')
| map(attribute='entity_id')
| list
| count }}

I think states are always strings unless converted, so you’re doing a comparison of two strings. I’m not exactly sure what < or > do on a string compare. You might try this:

{{ states
| selectattr('attributes.device_class', 'defined')
| selectattr('entity_id', 'contains', 'battery_level')
| selectattr('state' | int(0),  '<', 40)
| map(attribute='entity_id')
| list
| count }}

Without knowing the context of this/his template, wouldn’t it create Alot Usage, as domain-battery is not defined … going through All entities.attributes. to pick those who have an attribute battery-level seems to be overwork !, to my lazy-mind

The state value of any entity is a string. Even if it appears to be numeric, it’s a string.

Comparing numeric strings doesn’t behave the same way as comparing numbers. For example, “100” and “100.0” are not greater than “40”. That’s why that template excludes them.

Here’s another way to do it. It selects all entities containing the string “battery_level” in their object_id then converts their state value to a floating point number, then selects all values less than 40 (performing a numerical comparison, not string comparison), then reports how many it found.

{{ states
| selectattr('attributes.device_class', 'defined')
| selectattr('object_id', 'contains', 'battery_level')
| map(attribute='state')
| map('float', 0)
| select('<', 40)
| list
| count }}

I’m not sure why your template contains that first test but I left it in. After your template selects entities with a device_class attribute, it does nothing with them. It should then filter for entities having a specific value of device_class but it doesn’t. I suspect that if you remove the first line, the final count will remain unchanged.

You can’t add filters to the first argument of selectattr.

1 Like

Hi all,

Thanks for trying to help out here. However, the idea of @pkscout ideas unfortunately doesn’t give me what I want, as it errors out.

selectattr('state' | int(0),  '<', 40)

This gives me this error, like @123 said:

UndefinedError: homeassistant.helpers.template.TemplateState object has no element 0

Also, when using @123 's solution, I get a list of the entity values (of the correct entities!) like below, but I need the entity ids. I can’t seem to get my head around how to accomplish this.

[37.0, 20.0, 25.0]

Thanks again in advance!
Patrick

Where in your first post did you mention you wanted a list of entity_ids? Your own template example reports a count, not a list of entity_ids.

I suggest you use your template without the final count filter.

You are right, my first post included a count, so the ids did not matter for that. As a matter of fact, I need both the count -of the correct entities- as well as a list of the ids… :grimacing:

Same answer; use the existing template.

Sorry for the late reply but is there a way to get back to the entity_id after converting to a number and filtering?

1 Like

thanks - it doesn’t quite work for me as I a using for loop (I need to do it this way as it is a template filter in side auto-entities:

{% for x in states.sensor
  | selectattr("entity_id","search","_battery_plus")
  -%}
  {{
    ...
  }}
{%- endfor %}

Any ideas how to convert your solution for a for loop?

There’s no discussion of using a for-loop is this long-dormant topic. I suggest you start a new one that looks asks for a solution using your preferred for-loop method.

1 Like

fair enough - thanks.