Templating: filter list of entities by area?

I know how to get a list of entities matching certain criteria like this:

{{ 
  expand(states.light) 
  |selectattr('state', 'eq', 'on') 
  |map(attribute='entity_id')
  |join(',')  
}}

which gives me a list of light entities that are in the on state.

I’m trying to figure out if there’s a way to filter by the area in which the entity is located. Perhaps something like this

{{ 
  expand(states.light) 
  |selectattr('state', 'eq', 'on') 
  |selectattr('area_id', 'ne', 'Bedroom') 
  |map(attribute='entity_id')
  |join(',')  
}}

The use case for this would be wanting to call a service with something like “all lights that are on, but not in the Bedroom area”. Is there any way to do that with a template?

1 Like

I found the answer, thanks to @frenck’s presentation yesterday (nice job BTW!) - one of the new features added this year (2021.11, specifically) is a filter that does exactly what I needed.

And here are some updated versions of the above code with the new filter in use…

Lights that are on other than in the Bedroom

{{ 
  expand(states.light) 
  |selectattr('state', 'eq', 'on') 
  |rejectattr('entity_id', 'in', area_entities('Bedroom'))
  |map(attribute='entity_id')
  |list
}}

Lights that are on, but skip those in the Bedroom IFF someone is in bed:

{{ 
  expand(states.light) 
  |selectattr('state', 'eq', 'on') 
  |rejectattr(
    is_state('input_boolean.is_someone_in_bed', 'on')
    and 'entity_id', 'in', area_entities('Bedroom')
  )
  |map(attribute='entity_id')
  |list
}}

Edit: changed |join( ',' ) to |list in the last lines of the code blocks above. Thanks @123

9 Likes

If that’s the use case then the template can simply end with the list filter instead of the join(',') filter.

Cool! Thanks for the suggestion.

I’m using the above as the input for entity_id in the data_template part of a service call. Skipping the join() call cleans things up a bit.

If you wish, you can name it data because data_template was deprecated in favor of simply data several versions ago.

Nice - I still have data_template scattered around in lots of places in my HA YAML. Looks like I need to read the release notes more carefully! Definitely nice not to have to differentiate data and data_template anymore.

Just found this old post.
I was surprised when saw this “rejectattr(… and …)” construction.
Seems that it is only valid when that “is_state()” condition is TRUE.
Otherwise the template should return an error.

This is confirmed by this short code, test it in Dev tools → Template:

        {{ 
          states.light
          | rejectattr(
                true
                and
                'state','eq','on'
            )
          | map(attribute='entity_id')
          | list
        }}

If TRUE - then it return list of lights which are OFF (i.e. ON is rejected).
But if FALSE - it gives an error:

Your code test in Dev Tools → Template test is invalid.
It works if you actually test the state of an input boolean.

input_boolean.sleep_mode is off so the following test is True:

{{ 
  expand(states.light) 
  |selectattr('state', 'eq', 'off') 
  |rejectattr(
    is_state('input_boolean.sleep_mode', 'off')
    and 'entity_id', 'in', area_entities('Bedroom')
  )
  |map(attribute='entity_id')
  |list
}}

Change the 'eq','off' to 'on' so the test is False

{{ 
  expand(states.light) 
  |selectattr('state', 'eq', 'on') 
  |rejectattr(
    is_state('input_boolean.sleep_mode', 'on')
    and 'entity_id', 'in', area_entities('Bedroom')
  )
  |map(attribute='entity_id')
  |list
}}

and it works:

Would this be valid?

service: input_select.select_option
target:
  entity_id: >
   {{ 
    expand(states.select) 
    |selectattr('entity_id', 'in', area_entities('Tariffed Utility Meters')) 
    |map(attribute='entity_id')
    |list
   }}
  device_id: []
  area_id: []
data:
  option: Step 1

(See below for the correct syntax!)

1 Like

I have never tried to use a service call meant for an Input Select on a Select entity.

Let us know if it works.

Hmmm. Didn’t work.

Logger: homeassistant.helpers.service
Source: helpers/service.py:244
First occurred: 2:30:28 PM (1 occurrences)
Last logged: 2:30:28 PM

Referenced entities select.bathroom_plugs_annual_energy, select.bathroom_plugs_energy_today, select.bathroom_plugs_monthly_energy, select.bedroom_a_c_monthly_energy, select.coffee_maker_monthly_energy, select.computer_monthly_energy, select.dishwasher_annual_energy, select.dishwasher_energy_today, select.dishwasher_monthly_energy, select.dryer_annual_energy, select.dryer_energy_today, select.dryer_monthly_energy, select.electric_kettle_monthly_energy, select.emporia_total_daily_energy, select.ev_charger_energy_today, select.ev_charging_annual_energy, select.ev_charging_monthly_energy, select.ev_charging_session, select.fridge_annual_energy, select.fridge_energy_today, select.fridge_monthly_energy, select.furnace_annual_energy, select.furnace_energy_today, select.furnace_monthly_energy, select.garage_plugs_annual_energy, select.garage_plugs_energy_today, select.garage_plugs_monthly_energy, select.garburator_annual_energy, select.garburator_energy_today, select.garburator_monthly_energy, select.household_energy_annual, select.household_energy_monthly, select.household_energy_today, select.kitchen_17_monthly_energy, select.kitchen_plugs_17_annual_energy, select.kitchen_plugs_17_energy_today, select.kitchen_plugs_19_annual_energy, select.kitchen_plugs_19_energy_today, select.kitchen_plugs_19_monthly_energy, select.kitchen_plugs_21_annual_energy, select.kitchen_plugs_21_energy_today, select.kitchen_plugs_21_monthly_energy, select.kitchen_plugs_23_energy_today, select.kitchen_plugs_23_monthly_energy, select.kitchn_plugs_23_annual_energy, select.microwave_annual_energy, select.microwave_energy_today, select.microwave_monthly_energy, select.office_monthly_energy, select.outside_plugs_annual_energy, select.outside_plugs_energy_today, select.outside_plugs_monthly_energy, select.paul_s_bedroom_monthly_energy, select.range_annual_energy, select.range_energy_today, select.range_monthly_energy, select.washer_annual_energy, select.washer_energy_today, select.washer_monthly_energy 
are missing or not currently available

Could it be because the list is an array of comma separated entities rather than a hyphenated list?

No, it is because you are using the wrong service, as Taras pointed out above.

input_select services do not work with select entities.

These are the services available for select entities:

Doh! :saluting_face:
It works using the correct service for the utility meter select entities…

service: select.select_option
target:
  entity_id: >
   {{ expand(states.select)
    | selectattr('entity_id', 'in', area_entities('Tariffed Utility Meters'))
    | map(attribute='entity_id')
    | list 
   }}
  device_id: []
  area_id: []
data:
  option: "{{ trigger.to_state.state }}"
enabled: true

Thanks.

Here is my new completed automation…

description: ""
trigger:
  - platform: state
    entity_id:
      - input_select.bc_hydro_rate
    to: Step 2
    from: Step 1
    variables:
      step_rate: input_number.bchydro_step_2_rate
    id: Step 2
  - platform: state
    entity_id:
      - input_select.bc_hydro_rate
    to: Step 1
    from: Step 2
    variables:
      step_rate: input_number.bchydro_step_1_rate
    id: Step 1
condition: []
action:
  - service: input_number.set_value
    data:
      value: "{{ states(step_rate) }}"
    target:
      entity_id: input_number.bchydro_current_rate
  - service: select.select_option
    target:
      entity_id: |
        {{ expand(states.select)
         | selectattr('entity_id', 'in', area_entities('Tariffed Utility Meters'))
         | map(attribute='entity_id')
         | list 
        }}
      device_id: []
      area_id: []
    data:
      option: "{{ trigger.to_state.state }}"
    enabled: true
mode: single

If you want, you can replace all of this:

        {{ expand(states.select)
          | selectattr('entity_id', 'in', area_entities('Tariffed Utility Meters'))
          | map(attribute='entity_id')
          | list 
        }}

with this:

        {{ area_entities('Tariffed Utility Meters')
          | select('match', 'select') | list }}
2 Likes

Meanwhile, I’m over here wondering why expand(states.select) was even used in the first place. I’ve seen this a bunch recently. Expand does nothing on states.<domain>

1 Like

Ditto.

I assume the original template was created by copying bits from other examples containing the same anti-pattern.


For those unfamiliar with what the original template does:

It starts with the state objects of all select entities in the system, needlessly attempts to convert them into state objects, then whittles it down to match just the entities found in the “Tariffed Utility Meters” area, then extracts their entity_ids.

The suggested template starts with all entity_ids found in the “Tariffed Utility Meters” area then whittles it down to just select entities. Done.

2 Likes

Using Taras’s shorter template and a few other tweaks, this should also do the job for rather fewer lines:

trigger:
  - platform: state
    entity_id: input_select.bc_hydro_rate
    to:
      - Step 2
      - Step 1
    from:
      - Step 1
      - Step 2
action:
  - service: input_number.set_value
    data:
      value: "{{ states('input_number.bchydro_' ~ trigger.to_state.state|slugify ~ '_rate') }}"
    target:
      entity_id: input_number.bchydro_current_rate
  - service: select.select_option
    target:
      entity_id: "{{ area_entities('Tariffed Utility Meters')|select('match','select')|list }}"
    data:
      option: "{{ trigger.to_state.state }}"
1 Like

Thank you everybody! Here’s my new automation which now includes an email-based trigger for automatic switching plus the above suggestions:

Summary
alias: BCHydro - Rate Change
description: ""
trigger:
  - platform: numeric_state
    entity_id:
      - sensor.bc_hydro_bill_is_ready
    above: 0
    id: Step 1 Email
  - platform: numeric_state
    entity_id:
      - sensor.bc_hydro_higher_rate
    above: 0
    id: Step 2 Email
  - platform: state
    entity_id:
      - input_select.bc_hydro_rate
    to:
      - Step 1
      - Step 2
    from:
      - Step 1
      - Step 2
    id: Select Step
    enabled: true
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - Step 1 Email
        sequence:
          - service: input_select.select_option
            data:
              option: Step 1
            target:
              entity_id: input_select.bc_hydro_rate
          - service: input_number.set_value
            data:
              value: "{{ states('input_number.bchydro_step_1_rate') }}"
            target:
              entity_id: input_number.bchydro_current_rate
      - conditions:
          - condition: trigger
            id:
              - Step 2 Email
        sequence:
          - service: input_select.select_option
            data:
              option: Step 2
            target:
              entity_id: input_select.bc_hydro_rate
          - service: input_number.set_value
            data:
              value: "{{ states('input_number.bchydro_step_2_rate') }}"
            target:
              entity_id: input_number.bchydro_current_rate
      - conditions:
          - condition: trigger
            id:
              - Select Step
        sequence:
          - service: select.select_option
            target:
              entity_id: >
                {{ area_entities('Tariffed Utility Meters') | select('match',
                'select') | list }}
              device_id: []
              area_id: []
            data:
              option: "{{ trigger.to_state.state }}"
            enabled: true
mode: queued
max: 2

Multiple triggers and a choose aren’t necessary. Review Troon’s example.