Sort list of entities based on the numerical value of an attribute

I am trying to sort a list of entities based on the numerical value of the state attribute. The state attribute is a strong and I am not able to properly sort the list.

Another way to state the problem I am trying to solve is that I need the entity from a list on entities with the smallest numerical state value.

Are you trying to sort on the attribute value or the state value?

They are different things.

Where are you trying to sort?

In a card or a template?

Can you provide the list and what you want to sort on / use to select?

{% set x = (states.sensor 
| rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud')
| selectattr('attributes.device_class', 'eq', 'battery') 
| rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging'])
| sort(attribute='state', reverse=true) | list) %}
{{ x }}
2 Likes

Iā€™ve done this for finding the 2nd lowest illumance value in my house from a bunch of different light sensors,

([(states('sensor.master_bedroom_illuminance') | int(default=0)),
(states('sensor.motion_sensor_bathroom_illuminance') | int(default=0)),
(states('sensor.motion_sensor_ensuite_illuminance') | int(default=0)),
(states('sensor.motion_sensor_kitchen_illuminance') | int(default=0)),
(states('sensor.motion_sensor_powder_room_illuminance') | int(default=0)),
(states('sensor.motion_sensor_toilet_illuminance') | int(default=0))] | sort)[1]

In your case, you are sorting the values directly. I try to sort the entities based on the numerical value of their attributes.

sort(attribute='state', reverse=true)

In this case, the entities are sorted based on the string value of the ā€˜stateā€™.

FYI, this is how I solved something similar (I am by no means an expert, just wanted to share if someone happens to come accross this page)

{%set nameslist = expand("sensor.elektrische_verwarming_kinderkamer_energy_power",
"sensor.elektrische_verwarming_slaapkamer_energy_power",
"sensor.elektrische_verwarming_living_energy_power")%}
{%set nameslist=nameslist|sort(attribute='state',reverse=true)|map(attribute='entity_id')|list%}

This sorts the list, based on the state, returning an array of entity_idā€™s

Sorry to revive this thread, but I hope it may help someone

1 Like

Guys This is the only thread I found on sorting and selecting. I try to build a script which switches of consumers sorted on electricity consumption to stay below a certain threshold. But I cant get it to work yet. The idea is like this but I canā€™t get the coding right:

alias: Power Off
sequence:
  - service: variable.set_variable
    data:
      variable: power_consumers
      value:
        - entity_id: switch.sonoff_1000bee815
          consumption: "{{ states('sensor.kw_pool_pump') | float }}"
        - entity_id: switch.sonoff_1000fe1730
          consumption: "{{ states('sensor.sonoff_1000fe1730_power') | float }}"
        - entity_id: switch.sonoff_1000a012cb
          consumption: "{{ states('sensor.kw_ventilator_game_room') | float }}"
  - service: variable.sort
    data:
      variable: power_consumers
      attribute: consumption
      reverse: true
  - repeat:
      while:
        - condition: template
          value_template: >-
            {{
            states('sensor.smappee_1107002938_local_total_consumption_active_power')
            | float >= (4000 + states('sensor.kw_excess_solar_power') | float *
            1.0) }}
      sequence:
        - service: switch.turn_off
          data:
            entity_id: "{{ power_consumers[loop.index0].entity_id }}"
        - delay: "00:00:05"
        - service: homeassistant.update_entity
          entity_id: sensor.smappee_1107002938_local_total_consumption_active_power
mode: restart
variables:
  power_consumers: []
icon: mdi:transfer-down

The challenge is in the service: variable.set_variable. That is not working but I donā€™t know what to use or how to use a sorted list.

Any idea what I can use here?

On the basis of your examples I have rework it to:

alias: Power Off
sequence:
  - service: input_text.set_value
    data:
      entity_id: input_text.power_consumers
      value: >-
        {%- set consumers = [
          {'entity_id': 'switch.sonoff_1000bee815', 'consumption': states('sensor.kw_pool_pump') | float }, 
          {'entity_id': 'switch.sonoff_1000fe1730', 'consumption': states('sensor.sonoff_1000fe1730_power') | float },
          {'entity_id': 'switch.sonoff_1000a012cb', 'consumption': states('sensor.kw_ventilator_game_room') | float },
          ] -%}
        {% set consumers = consumers| sort(attribute='consumption',
        reverse=true) | map(attribute='entity_id') | list %}  
  - repeat:
      while:
        - condition: template
          value_template: >-
            {{
            states('sensor.smappee_1107xx2938_local_total_consumption_active_power')
            | float >= (4000 + states('sensor.kw_excess_solar_power') | float *
            1.0) }}
      sequence:
        - service: switch.turn_off
          data:
            entity_id: "{{ consumers[0] }}"
        - delay: "00:00:05"
        - service: homeassistant.update_entity
          entity_id: sensor.smappee_1107xx2938_local_total_consumption_active_power
mode: restart
variables:
  power_consumers: []
icon: mdi:transfer-down

The script runs in terms of coding but I am not sure about the sorting. I still need some serious testing but its closer alreadyā€¦

Clearing the list at the end I still have to correct.

1 Like

At the end it boils down to the following part works perfectly in the developers template tool but I canā€™t get it to work in the script. Somehow I canā€™t get the value or state of x assigned to an input text:

{% set x = [
                {'entity_id': 'switch.sonoff_1000bee815', 'consumption': states('sensor.kw_pool_pump') | float }, 
                {'entity_id': 'switch.sonoff_1000fe1730', 'consumption': 
                  states('sensor.sonoff_1000fe1730_power') | float },
                {'entity_id': 'switch.sonoff_1000a012cb', 'consumption': 
                  states('sensor.kw_ventilator_game_room') | float },
                ] %}
              {% set x = x | sort(attribute='consumption', reverse=true) | map(attribute='entity_id') | list %}  
              {% set x = x|first|string %}  

In the development template it gives perfectly the entity_id of the highest consumer (x) of the list but in the script I canā€™t make it work.

I used a similar solution to you @Ih8rain2 although my use case was a little different. In my case I wanted a list of the top 5 energy consumers in my house which displayed the friendly name of the entity. My solution works as follows:

I have a global macro defined in config/custom_templates/top_consumers.jinja

{% macro top_power_consumers(index) %}
{% set entity_list = [
    {'entity_id': 'sensor.power_1', 'consumption': states('sensor.power_1') | default(0) | float },
    {'entity_id': 'sensor.power_2', 'consumption': states('sensor.power_2') | default(0) | float },
    {'entity_id': 'sensor.power_3', 'consumption': states('sensor.power_3') | default(0) | float },
    {'entity_id': 'sensor.power_4', 'consumption': states('sensor.power_4') | default(0) | float },
    {'entity_id': 'sensor.power_5', 'consumption': states('sensor.power_5') | default(0) | float },
    {'entity_id': 'sensor.power_6', 'consumption': states('sensor.power_6') | default(0) | float },
    {'entity_id': 'sensor.power_7', 'consumption': states('sensor.power_7') | default(0) | float },
    {'entity_id': 'sensor.power_8', 'consumption': states('sensor.power_8') | default(0) | float },
    {'entity_id': 'sensor.power_9', 'consumption': states('sensor.power_9') | default(0) | float }] %}
{% set sorted_list=entity_list | sort(attribute='consumption',reverse=true) | map(attribute='entity_id') | list %}
{{ sorted_list[index] | trim }}
{% endmacro %}

Then I have 5 template sensors defined as follows which just index through the sorted_list (this is just the first sensor):

- sensor:
  - name: >
      {% from 'top_consumers.jinja' import top_power_consumers %}
      {% set name = state_attr(top_power_consumers(0) | trim, 'friendly_name') %}
      {{ name }}
    state: >
      {% from 'top_consumers.jinja' import top_power_consumers %}
      {% set state = states(top_power_consumers(0) | trim) %}
      {{ state }}
    unique_id: sensor.top_power_consumer_first
    unit_of_measurement: W
    state_class: measurement

Then I have an entites card in my custom energy dashboard which list each of the sensors:

type: entities
entities:
  - entity: sensor.top_power_consumer_first
  - entity: sensor.top_power_consumer_second
  - entity: sensor.top_power_consumer_third
  - entity: sensor.top_power_consumer_fourth
  - entity: sensor.top_power_consumer_fifth

I do the same for energy, which seems to work as expected, and the names and state values in the dashboard updated dynamically as devices switch on and off.

I had a couple of issues during implementing. Firstly, I had was the some leading/trailing whitespace in the return value of the macro. Unfortunately the trim at the end of the macro did not work, and I have to use trim in all the places where the macro is called. Perhaps the whitespace is also the cause of your problem and you need to call trim?

Secondly, there is some bug when the template sensors get created where they did not receive the unique_id I defined, and they ended up with some unique_id which was derived from the name. It was enough to find the sensor in the settings->entities list and manually modify the unique_id via the GUI.

1 Like

Ok, I am loving the concept here but pulling my hair out trying to get it up and running.

First time using jinja and macros so maybe making a very NOOB mistake but here goes.

Iā€™ve created top_consumers.jinja in config/custom_templates

{% macro top_consumers(index) %}
{% set entity_list = [
    {'entity_id': 'sensor.always_on_usage', 'consumption': states('sensor.always_on_usage') | default(0) | float },
    {'entity_id': 'sensor.1st_floor_ac_usage', 'consumption': states('sensor.1st_floor_ac_usage') | default(0) | float },
    {'entity_id': 'sensor.2nd_3rd_floor_ac_usage', 'consumption': states('sensor.2nd_3rd_floor_ac_usage') | default(0) | float },
    {'entity_id': 'sensor.3rd_floor_air_handler_usage', 'consumption': states('sensor.3rd_floor_air_handler_usage') | default(0) | float },
    {'entity_id': 'sensor.other_usage', 'consumption': states('sensor.other_usage') | default(0) | float },
    {'entity_id': 'sensor.condensate_pump_usage', 'consumption': states('sensor.condensate_pump_usage') | default(0) | float },
    {'entity_id': 'sensor.dishwasher_miele_washer_usage_2', 'consumption': states('sensor.dishwasher_miele_washer_usage_2') | default(0) | float },
    {'entity_id': 'sensor.dyson_usage', 'consumption': states('sensor.dyson_usage') | default(0) | float },
    {'entity_id': 'sensor.espresso_coffee_pot_usage', 'consumption': states('sensor.espresso_coffee_pot_usage') | default(0) | float },
    {'entity_id': 'sensor.freezer_defrost_usage', 'consumption': states('sensor.freezer_defrost_usage') | default(0) | float },
    {'entity_id': 'sensor.fridge_freezer_usage', 'consumption': states('sensor.fridge_freezer_usage') | default(0) | float },
..snip..
    {'entity_id': 'sensor.hair_dryer_usage', 'consumption': states('sensor.hair_dryer_usage') | default(0) | float },
    {'entity_id': 'sensor.ice_maker_usage', 'consumption': states('sensor.ice_maker_usage') | default(0) | float },
{% set sorted_list=entity_list | sort(attribute='consumption',reverse=true) | map(attribute='entity_id') | list %}
{{ sorted_list[index] | trim }}
{% endmacro %}

And created the first helper template.

- sensor:
  - name: >
      {% from 'top_consumers.jinja' import top_consumers %}
      {% set name = state_attr(top_consumers(0) | trim, 'friendly_name') %}
      {{ name }}
    state: >
      {% from 'top_consumers.jinja' import top_consumers %}
      {% set state = states(top_consumers(0) | trim) %}
      {{ state }}
    unique_id: sensor.top_consumer_1st
    unit_of_measurement: W
    state_class: measurement

But it continues to come up ā€œUnavailableā€.

HA seems to be loading the macro ok as it identifies all the individual sensors as listeners but then the log throws a bunch of errors (below).

  • TemplateError(ā€˜UndefinedError: ā€˜str objectā€™ has no attribute ā€˜stateā€™ā€™) while processing template ā€˜Template<template=({% set sorted_entities = [ states(ā€˜sensor.1st_floor_ac_usageā€™), states(ā€˜sensor.2nd_3rd_floor_ac_usageā€™), states(ā€˜sensor.always_on_usageā€™), states(ā€˜sensor.christmas_tree_and_laser_printer_usageā€™), states(ā€˜sensor.coffee_grinder_usageā€™), states(ā€˜sensor.condensate_pump_usageā€™), states(ā€˜sensor.dehumidifier_usageā€™), states(ā€˜sensor.dishwasher_miele_washer_usageā€™), states(ā€˜sensor.dishwasher_usageā€™), states(ā€˜sensor.dryer_usageā€™), states(ā€˜sensor.espresso_coffee_pot_usageā€™), states(ā€˜sensor.freezer_defrost_usageā€™), states(ā€˜sensor.fridge_freezer_usageā€™), states(ā€˜sensor.hair_dryer_usageā€™), states(ā€˜sensor.ice_dispenser_usageā€™), states(ā€˜sensor.ice_maker_usageā€™), states(ā€˜sensor.kettle_usageā€™), states(ā€˜sensor.microwave_usageā€™), states(ā€˜sensor.miele_dryer_usageā€™), states(ā€˜sensor.motor_3_usageā€™), states(ā€˜sensor.other_usageā€™), states(ā€˜sensor.toaster_usageā€™), states(ā€˜sensor.vacuum_usageā€™), states(ā€˜sensor.vacuum_usage_2ā€™), states(ā€˜sensor.washer_usageā€™) ] | map(attribute=ā€˜stateā€™) | map(ā€˜floatā€™) | sort(reverse=true) | list %} {{ sorted_entities[:5] | join(ā€™, ā€˜) }}) renders=4>ā€™ for attribute ā€˜_attr_native_valueā€™ in entity ā€˜sensor.top_5_sensorsā€™
  • TemplateError(ā€˜ValueError: Template error: float got invalid input ā€˜unknownā€™ when rendering template ā€˜- sensor: - name: > {% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set name = state_attr(top_consumers(0) | trim, ā€˜friendly_nameā€™) %} {{ name }} state: > {% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }} unique_id: sensor.top_consumer_1st unit_of_measurement: W state_class: measurementā€™ but no default was specifiedā€™) while processing template ā€˜Template<template=(- sensor: - name: > {% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set name = state_attr(top_consumers(0) | trim, ā€˜friendly_nameā€™) %} {{ name }} state: > {% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }} unique_id: sensor.top_consumer_1st unit_of_measurement: W state_class: measurement) renders=4>ā€™ for attribute ā€˜_attr_native_valueā€™ in entity ā€˜Noneā€™

Been trying to debug a few hours now and getting no where.

Any insights appreciated!

:point_up: :upside_down_face:

Make sure you are not pasting YAML configuration into the State template box. The Helper setup tool should look like:

image

Appreciate the fast reply but seems to be more than that causing the problem. Adjusted the Template sensor in the GUI and still getting similar errors in the logs.

  • TemplateError(ā€˜ValueError: Template error: float got invalid input ā€˜unknownā€™ when rendering template ā€˜{% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }} unique_id: sensor.top_consumer_1st unit_of_measurement: W state_class: measurementā€™ but no default was specifiedā€™) while processing template ā€˜Template<template=({% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }} unique_id: sensor.top_consumer_1st unit_of_measurement: W state_class: measurement) renders=4>ā€™ for attribute ā€˜_attr_native_valueā€™ in entity ā€˜Noneā€™
  • TemplateError(ā€˜ValueError: Template error: float got invalid input ā€˜unknownā€™ when rendering template ā€˜{% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }} unique_id: sensor.top_consumer_1st unit_of_measurement: W state_class: measurementā€™ but no default was specifiedā€™) while processing template ā€˜Template<template=({% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }} unique_id: sensor.top_consumer_1st unit_of_measurement: W state_class: measurement) renders=4>ā€™ for attribute ā€˜_attr_native_valueā€™ in entity ā€˜Noneā€™
  • TemplateError(ā€˜ValueError: Template error: float got invalid input ā€˜unknownā€™ when rendering template ā€˜{% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }}ā€™ but no default was specifiedā€™) while processing template ā€˜Template<template=({% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }}) renders=4>ā€™ for attribute ā€˜_attr_native_valueā€™ in entity ā€˜Noneā€™
  • TemplateError(ā€˜ValueError: Template error: float got invalid input ā€˜unknownā€™ when rendering template ā€˜{% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }}ā€™ but no default was specifiedā€™) while processing template ā€˜Template<template=({% from ā€˜top_consumers.jinjaā€™ import top_consumers %} {% set state = states(top_consumers(0) | trim) %} {{ state }}) renders=4>ā€™ for attribute ā€˜_attr_native_valueā€™ in entity ā€˜sensor.top_consumer_1stā€™

I think the issue is that at least one of the sensors is returning a None value and your default filters are not set up properly to handle that.

| default(0, true)

Have you checked that the macro is working in the Template editor in Developer Tools?

I would start basic and just check:

{% from 'top_consumers.jinja' import top_consumers %}
{{ top_consumers(0) }}

If that works add the filter then the function to see what throws the error.

1 Like

So part of the problem was 1 of my sensors in the macro was misnamed (missing ā€œ_usageā€) which was causing that to fail out of the gate.

Iā€™ve managed to get the template working in Dev Tools ā†’ Template editor but now stuck trying to get all 5 of the sensors defined per this:

Then I have 5 template sensors defined as follows which just index through the sorted_list (this is just the first sensor):

- sensor:
  - name: >
      {% from 'top_consumers.jinja' import top_power_consumers %}
      {% set name = state_attr(top_power_consumers(0) | trim, 'friendly_name') %}
      {{ name }}
    state: >
      {% from 'top_consumers.jinja' import top_power_consumers %}
      {% set state = states(top_power_consumers(0) | trim) %}
      {{ state }}
    unique_id: sensor.top_power_consumer_first
    unit_of_measurement: W
    state_class: measurement

Presumably since the nameā€™s are variable I need to define these in configuration.yaml and not the GUI template editor, but I am struggling with the syntax to get all 5 repetitions in.

Finally cobbled something together!
Below works although it reads pretty clunky with different syntax than @phio 's.
Any builds on simplifying this are welcome. Next challenge would be to pull in the entity.icon to display more visually than the default eyeball.

  - platform: template
    sensors:
      top_power_1:
        friendly_name: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set name = state_attr(top_consumers(0) | trim, 'friendly_name') %}
          {{ name }}
        value_template: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set state = states(top_consumers(0) | trim) %}
          {{ state }}
        unique_id: sensor.top_power_1
        unit_of_measurement: W
        # state_class: measurement
      top_power_2:
        friendly_name: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set name = state_attr(top_consumers(1) | trim, 'friendly_name') %}
          {{ name }}
        value_template: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set state = states(top_consumers(1) | trim) %}
          {{ state }}
        unique_id: sensor.top_power_2
        unit_of_measurement: W
        # state_class: measurement
      top_power_3:
        friendly_name: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set name = state_attr(top_consumers(2) | trim, 'friendly_name') %}
          {{ name }}
        value_template: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set state = states(top_consumers(2) | trim) %}
          {{ state }}
        unique_id: sensor.top_power_3
        unit_of_measurement: W
        # state_class: measurement
      top_power_4:
        friendly_name: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set name = state_attr(top_consumers(3) | trim, 'friendly_name') %}
          {{ name }}
        value_template: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set state = states(top_consumers(3) | trim) %}
          {{ state }}
        unique_id: sensor.top_power_4
        unit_of_measurement: W
        # state_class: measurement
      top_power_5:
        friendly_name: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set name = state_attr(top_consumers(4) | trim, 'friendly_name') %}
          {{ name }}
        value_template: >
          {% from 'top_consumers.jinja' import top_consumers %}
          {% set state = states(top_consumers(4) | trim) %}
          {{ state }}
        unique_id: sensor.top_power_5
        unit_of_measurement: W
        # state_class: measurement

You can make this a lot easier if you would put all of these sensors in a sensor group (you can do that in the Helpers section). It doesnā€™t really matter what kind of group you choose, but maybe something like sum makes sense.

Then you can do the following for the macro (replace sensor.your_new_sensor_group with the actual entity_id of your group)

{%- macro top_power_consumers(index=0, return='state') -%}
  {%- set ns = namespace(states=[]) -%}
  {%- for s in expand('sensor.your_new_sensor_group') | selectattr('state', 'is_number') -%}
    {%- set ns.states = ns.states + [dict(entity_id=s.entity_id, name=s.name, state=s.state | float)] -%}
  {%- endfor -%}
  {{- (ns.states | sort(attribute='state', reverse=true) | map(attribute=return) | list)[index] -}}
{%- endmacro -%}

After that you can do the following in your template sensors (please note you are using the legacy format in your last post, I would advice moving to the modern format like you did in earlier posts).

- sensor:
  - name: >
      {% from 'top_consumers.jinja' import top_power_consumers %}
      {{ top_power_consumers(0, 'name') }}
    state: >
      {% from 'top_consumers.jinja' import top_power_consumers %}
      {{ top_power_consumers() }}
    unique_id: sensor.top_power_consumer_first
    unit_of_measurement: W
    state_class: measurement

BTW, I provided defaults for the macro, so top_power_consumers() will give the state of the first entity (so the highest consumption)

2 Likes

I tried this, but something is wrong with my sensor:

Invalid config for ā€˜sensorā€™ at sensor/sensor.yaml, line 235: required key ā€˜platformā€™ not provided

Iā€™m lost. Well I put the sensors in a seperat file and include this. I tried to add the template but still fail to get it workingā€¦

I tried to add

  - platform: template
    sensors:

But get more errors:

Invalid config for ā€˜templateā€™ from integration ā€˜sensorā€™ at sensor/sensor.yaml, line 147: expected dictionary for dictionary value ā€˜sensorsā€™, got [{ā€˜nameā€™: ā€œ{% from ā€˜top_consumers.jinjaā€™ import top_power_consumers %} {{ top_power_consumers(0, ā€˜nameā€™) }}\nā€, ā€˜stateā€™: ā€œ{% from ā€˜top_consumers.jinjaā€™ import top_power_consumers %} {{ top_power_consumers() }}\nā€, ā€˜unique_idā€™: ā€˜sensor.top_power_consumer_firstā€™, ā€˜unit_of_measurementā€™: ā€˜Wā€™, ā€˜state_classā€™:

Is it:

template:
  - sensor:
      - name: "Average temperature"

or

 - platform: template
      sensors:

Old / new formatā€¦ Iā€™m lost.

Did you get this working?
I am keen on getting exactly the same top-5/top-10 of power/ā€¦ consumers :+1: