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.