New template function: natsort -> natural sorting of sensor values

If you sort sensor values or attributes with the “sort” filter, they get sorted by their ASCII value. But if you have numbers, the result ist not what you want:

{{ ["5","1","31","23","9","22","11"]|sort }}

gives you [‘1’, ‘11’, ‘22’, ‘23’, ‘31’, ‘5’, ‘9’]. So if you sort for example battery sensors, the list is not in the order you want it. So I developed a “natsort” filter. “natsort” splits text and numbers and sorts the numbers separate from the text.

So

{{ ["5","1","31","23","9","22","11"]|natsort }}

gives you [‘1’, ‘5’, ‘9’, ‘11’, ‘22’, ‘23’, ‘31’].

Another example for testing: battery values:

{{ states.sensor| selectattr('attributes.device_class', 'defined')|selectattr('attributes.device_class', 'eq','battery')|natsort(attribute="state")|list|join("\n\n") }}

I would like to make a change request to helpers/template.py if this function is desired.
For this, it is also necessary that HA installs the natsort module (dependency). How is this achieved?

1 Like

That is not a list of numbers (integers). Those are strings.

This is simply fixed as follows:

{% set l = [55, 4, 33, 2, 11] %}
{{ l | sort }}
{% set l = ["55", "4", "33", "2" , "11"] %}
{{ l | sort }}
{{ l | map("int") | list | sort }}

Wow, amazing!
Now do the same magic with

{% set l = ["55%", "4%", "33%", "2%", "11%", "100%"] %}

(this is what you get if you fetch the list of battery sensors)

1 Like

Haha, nice challenge!

I think your suggestion is a good idea for this case. I’m simply showing this as a somewhat contrived alternative.

I had to jump through a few hoops for this one…

{% set l = ["55%", "4%", "33%", "2%", "11%", "100%"] %}
{{ l }}
{% set l_as_sorted_ints = l | map('replace', '%', '') | map("int") | list | sort %}
{{ l_as_sorted_ints }}
{% set l_in_json_obj = '{"l": ["' + (l_as_sorted_ints | join('%","')) + '%"]}' %}
{{ l_in_json_obj }}
{{ (l_in_json_obj | from_json)['l'] }}
{{ (l_in_json_obj | from_json)['l'][3] }}

Output:

['55%', '4%', '33%', '2%', '11%', '100%']
[2, 4, 11, 33, 55, 100]
{"l": ["2%","4%","11%","33%","55%","100%"]}
['2%', '4%', '11%', '33%', '55%', '100%']
33%

Neither Jinja2 nor HA’s templating engine has a way to split a string, hence the from_json trick.

Which integrations (that you’re using) include a percent symbol in their battery value?

The reason why I am asking is because none of the integrations I use do that.

{{ ["55%", "4%", "33%", "2%", "11%", "100%"]
  | map('replace', '%', '')
  | map('int', 0)
  | sort
  | map('string')
  | map('regex_replace', '(\d+)', '\\1%')
  | list
}}
1 Like

Well done!
My Version:

{% set l = ["55%", "4%", "33%", "2%", "11%", "100%"] %}
{{ l|natsort }}

And now this list of battery levels without “natsort”:

{% for i in states.sensor|natsort(attribute="state")|map(attribute='entity_id') %}
{%- if state_attr(i,"device_class")=="battery"  and states(i)!='unavailable' and states(i)!='unknown' -%}
{{ states(i)|int(0) }}%  {{ state_attr(i,"friendly_name") }}
{% endif -%}
{% endfor %}

That master has spoken. Nice one!

1 Like

Which integrations append % to the battery level?

I just double-checked and confirmed every integration I have that reports battery level does so without appending a % symbol.

FWIW, I use the Auto Entities custom card to display a sorted list of names and levels of all entities whose device_class is battery. No template required.