Templating custom filters

My filter is very important for HA because HA stores sensor values in strings and there is no way to sort them in a useful way. For example I have a list of battery states, and the normal sort filter sorts them like 1%, 100%, 2%, 20%, 30%, 5%, 60%
With my filter, they get sorted 1%, 2%, 5%, 20%, 30%, 60%, 100%

You can store values in attributes as-is. If you store them in the state, they will be strings.

Secondly, the sorting is messed up because you’re storing the values with %. There’s no reason to attach the units to the value.

A simple way to handle this is to remove the last character and convert them to numbers. then sort. All doable in 1 line of jinja.

{{ values | map('replace', '%', '') | map('float') | sort }}

I’m not storing values in sensors. HA is doing that. What do see when you try

{{ states.sensor|sort(attribute="state")|map(attribute="state")|list }}

in the developer tools. You get a list that is somehow ordered, but not in way that is useful.

then you can do exactly what I just said…

{{ states.sensor | map(attribute="state") | map('float', None) | reject('none') | sort }}

You missunderstood my example. The real word case is

{{ states.sensor|sort(attribute="state")|list }}

The reason is that normally I want to keep the whole sensor object and not only the state. If I want a list of empty batteries, of course I not only want the battery value but also the name of the device.

Yes, then this goes back to my original comment that you should use the custom integration with a manifest. I feel like we are going in circles here.

@petro been a hectic few months and I’m getting back to this. I have this code in a file in the custom_templates file and I am able to call it in the Dev tools template editor using this:

{% from 'convertieee.jinja' import flt_2_ieee %}
{{ flt_2_ieee(39) }}

which returns back [0x421C,0x0] as i expect

I have a script that is trying the same thing and I get an error:
Error rendering data template: UndefinedError: 'flt_2_ieee' is undefined
Here is the script config:

modbus_brite_2_set_temp:
  alias: Modbus Brite_2 set temp
  sequence:
    - service: modbus.write_register
      data:
        value: >
          {% from 'convertieee.jinja' import flt_2_ieee %}
          "{{ flt_2_ieee(temp) }}"
        hub: modbus_hub
        unit: 2
        address: 00
  mode: parallel
  max: 3

What am I doing wrong?
HA: 8.4

sounds like you didn’t create the macro properly in the system

1 Like

Since it is working in the Template Dev tool, I concluded that the system was configured correctly. is that not a good test?

if it’s working in dev tools, it’ll work in wherever else you put the template.

I stripped everything out and rebuilt from scratch and it is now working as a custom template macro using the code @petro provided above.

Great job. do you @jms3000 happen to share the code please? I am also dealing something like that. Perhaps @blizzrdof77 can add it to his repo of filters

Thats a good start but unfortunately it targets just the list or the string. What I want is to filter sort the whole list of templatestates based on attribute so that the final results hold all the attributes of the templatestates

What?

You’re going to have to explain a lot better what you want to do, with some example data.

well this is list

[
<template TemplateState(<state input_number.spotify_playback_volume_bathroom_speaker=5.0; initial=None, editable=True, min=0.0, max=100.0, step=5.0, mode=slider, unit_of_measurement=%, friendly_name=Spotify Playback Volume - Bathroom Speaker @ 2023-11-29T10:56:45.390229+01:00>)>,

<template TemplateState(<state input_number.spotify_playback_volume_bedroom_clock=10.0; initial=None, editable=True, min=0.0, max=100.0, step=5.0, mode=slider, unit_of_measurement=%, friendly_name=Spotify Playback Volume - Bedroom Clock @ 2023-11-29T11:16:39.865984+01:00>)>,

<template TemplateState(<state input_number.spotify_playback_volume_living_room_clock=20.0; initial=None, editable=True, min=0.0, max=100.0, step=5.0, mode=slider, unit_of_measurement=%, friendly_name=Spotify Playback Volume - Living Room Clock @ 2023-11-29T11:16:43.477724+01:00>)>,

<template TemplateState(<state input_number.spotify_playback_volume_kitchen_clock=15.0; initial=None, editable=True, min=0.0, max=100.0, step=5.0, mode=slider, unit_of_measurement=%, friendly_name=Spotify Playback Volume - Kitchen Clock @ 2023-11-29T11:16:41.696763+01:00>)>,

<template TemplateState(<state input_number.spotify_playback_volume_office_room_clock=25.0; initial=None, editable=True, min=0.0, max=100.0, step=5.0, mode=slider, unit_of_measurement=%, friendly_name=Spotify Playback Volume - Office Room Clock @ 2023-11-29T11:16:46.284887+01:00>)>
]

here you can see that the list is filled with different TemplateStates. Now I wish to filter out only the TemplateState which has the maximum value in its state.

{{ states.input_number | selectattr('entity_id','search', 'spotify_playback_volume_') | selectattr('state', '>', '0.00') | max(attribute='state') }}

having this filter gives out the following output:

<template TemplateState(<state input_number.spotify_playback_volume_bathroom_speaker=5.0; initial=None, editable=True, min=0.0, max=100.0, step=5.0, mode=slider, unit_of_measurement=%, friendly_name=Spotify Playback Volume - Bathroom Speaker @ 2023-11-29T10:56:45.390229+01:00>)>

which is wrong as you can see the max state value is not in the input_number.spotify_playback_volume_bathroom_speaker but input_number.spotify_playback_volume_office_room_clock

Your problem is that states are strings (as you know), and “5” is greater than “25” alphanumerically.

This might do it: find the maximum state value (having converted to float, then find the first entity that has that state:

{% set max_vol = states.input_number
                 | selectattr('entity_id','search', 'spotify_playback_volume_')
                 | map(attribute='state')
                 | map('float', default=0)
                 | list | max %}
{{ states.input_number
                 | selectattr('entity_id','search', 'spotify_playback_volume_')
                 | selectattr('state','eq',max_vol|round(1)|string)
                 | list | first }}
1 Like

Thanks a lot. I was wondering if is possible to do it in a single query instead of 2 queries?

You can make use of namespace, that’s about it.

{% set ns = namespace(item=None) %}
{% for e in states.input_number | selectattr('entity_id','search', 'spotify_playback_volume_') %}
  {% set s = e.state | float(None) %}
  {% if s is not none and (ns.item is none or (ns.item is not none and s > ns.item)) %}
    {% set ns.item = e.entity_id %}
  {% endif %}
{% endfor %}
{{ ns.item }}

This is a single iteration using a generator. It’ll be the most optimized you can get in regards to speed.

1 Like

Thanks both… my issues are solved by using your code. I ended up using the version from @Troon