Select all lights on one floor with a Template

Hi,

I would like to select all lights in a certain ‘room’ (‘ruimte’ in Dutch) so that I can inform with a mushroom card the user how many lights are (still) on on that floor, or in that room.

Now I have it hardcoded with a manual filled array of all lights on that floor like this:

      - type: vertical-stack
        cards:
          - type: custom:mushroom-title-card
            title: 1e Verdieping
            subtitle: >-
              Er zijn {{ [
              'light.badkamer_bad',
              'light.badkamer_douche', 
              'light.badkamer_toilet' ,
              'light.badkamer_wastafel' ,
              'light.slaapkamer_kleurenlamp',
              'light.overloop_lamp',
              'light.kantoor_burolamp',
              'light.kantoor_plafond_kast',
              'light.kantoor_plafond_lamp_printer', 
              'light.kantoor_plafond_schilderij'
              ] | select('is_state', 'on') | list | count }} op de eerste verdieping aan, {{user}}!

But that isn’t very flexible; as soon as I add a light, I need to code again.

On my first floor I have the following Rooms/Ruimtes (which I consider as a pretty stable list):

  • Slaapkamer
  • Logeerkamer
  • Badkamer
  • Kantoor
  • Overloop

How do I achieve that?

Regards,
Martin

Never tried this, but assigning an area to each light seems to give you a number of template filtering options:

You can sum the counts from different areas as follows:

- type: vertical-stack
  cards:
    - type: custom:mushroom-title-card
      title: 1e Verdieping
      subtitle: >-
        {% set ns = namespace(lights_on=[]) %}
        {% set region = ['Slaapkamer', 'Logeerkamer', 'Badkamer',
        'Kantoor', 'Overloop'] %}
        {% for room in region %}
        {% set ns.lights_on = ns.lights_on + [area_entities(room)
        | select('match', 'light.*') | select('is_state', 'on') | list | count ] %}
        {% endfor %}
        Er zijn {{ ns.lights_on | sum }} op de eerste verdieping aan, {{user}}!

However, using an Area-based approach may end up needing just as much maintenance as your original method. There can be numerous integrations that create light entities that you don’t want to include in your count which you will have to reject from the results of the template or disable. Light groups pose a similar issue that you will need to decide how to handle and possibly update every time you add or change a light group.

As I’m not (yet) so familiar with templating and this language,

Can you step by step explain to me what you are doing with this code so that I can reproduce it myself in other situations?

I have JavaScript and React experience, so I do understand programming in general but this syntax has some caviats I expect.

Thanks in advance

Sources for Jinja Templating Information

Home Assistant Templating : This is the the most comprehensive source for HA-specific functions

Jinja Docs: These docs are not geared toward new/casual users, but they contain a lot of important information.

Jinja for Ninjas: @skalavala has put together a well organized tutorial and group of examples. It’s been a while since this was updated, so there are better/easier ways to do some of the things shown, but it’s still a good source.

Many Python methods also to work in templates. I’ve never seen a complete list of which methods work and which don’t… But the Official Python docs have come in handy for me multiple times to figure out what’s going on in templates posted by some of the more advanced users on this forum.


{% set ns = namespace(lights_on=[]) %}
{% set region = ['Slaapkamer', 'Logeerkamer', 'Badkamer',
'Kantoor', 'Overloop'] %}

Setup variables:
A namespace is needed so that we can access the information outside of the loop.

{% for room in region %}
   {% set ns.lights_on = ns.lights_on + [area_entities(room)
   | select('match', 'light.*') | select('is_state', 'on') | list | count ] %}
{% endfor %}

Loop through rooms.
area_entities(room): Instantiate a list of the entitie IDs assigned to a given Area.
| select('match', 'light.*'): Keep all entity IDs that contain “light.”
| select('is_state', 'on'): Keep only entity IDs for entities whose state is “on”
| list | count : Generate a list of the entity IDs and return the count of items in the list

Er zijn {{ ns.lights_on | sum }} op de eerste verdieping aan, {{user}}!

Return results:
ns.lights_on | sum: Return the sum of all the counts in the list.

1 Like

You can use | reject('is_hidden_entity') to ease your life.

Came across this looking for a solution.
Didgeridew’s answer helped a lot, but I managed to improve upon it thanks to the recent addition of some functions
So I thought I’d share here for anyone else looking for a better answer

- type: vertical-stack
  cards:
    - type: custom:mushroom-title-card
      title: 1e Verdieping
      subtitle: >-
        {% set ns = namespace(lights_on=[]) %}
        {% set region = floor_areas('floor_1') %}
        {% for room in region %}
        {% set ns.lights_on = ns.lights_on + [area_entities(room)
        | select('match', 'light.*') | select('is_state', 'on') | list | count ] %}
        {% endfor %}
        Er zijn {{ ns.lights_on | sum }} op de eerste verdieping aan, {{user}}!
1 Like

Alternative that doesn’t use a for-loop.

- type: vertical-stack
  cards:
    - type: custom:mushroom-title-card
      title: 1e Verdieping
      subtitle: >-
        {% set x = floor_areas('floor_1') | map('area_entities') | sum(start=[])
          | select('match', 'light') | select('is_state', 'on') | list | count %}
        Er zijn {{ x }} op de eerste verdieping aan, {{ user }}!

Other examples here:

I slightly improved the code you provided to adapt it for my use. Sharing in case it helps someone, just as the code @123 helped me. I used Mushroom Template Card and two basics Home Assistant script.

Assistant script :


alias: light_ground_floor_off
sequence:
  - service: light.turn_off 
    data: {}
    target:
      floor_id: ground_floor
description: ""


alias: light_ground_floor_on
sequence:
  - service: light.turn_on
    data: {}
    target:
      floor_id: ground_floor
description: ""

This script toggles the lights on and off for the ground floor using the Mushroom Template Card.

Button mushroom-template-card :


type: custom:mushroom-template-card
primary: Ground Floor  # Title of the card 
icon: >
  # Initialize a namespace to keep track of lights on
  {% set ns = namespace(lights_on=[]) %}
  # Get all areas for the ground floor
  {% set region = floor_areas('ground_floor') %}
  # Loop through each room in the region
  {% for room in region %}
    # Add count of 'on' lights to the namespace list
    {% set ns.lights_on = ns.lights_on + 
    [area_entities(room) | select('match', 'light.*') | select('is_state', 'on') | list | count ] %}
  {% endfor %}
  # Set the icon based on whether any lights are on
  {{ 'mdi:lightbulb-on' if ns.lights_on | sum > 0 else 'mdi:lightbulb-off' }}
icon_color: >-
  # Same logic to determine the icon color
  {% set ns = namespace(lights_on=[]) %}
  {% set region = floor_areas('ground_floor') %}
  {% for room in region %}
    {% set ns.lights_on = ns.lights_on + 
    [area_entities(room) | select('match', 'light.*') | select('is_state', 'on') | list | count ] %}
  {% endfor %}
  # Set the icon color to yellow if any lights are on, otherwise gray
  {{ 'yellow' if ns.lights_on | sum > 0 else 'gray' }}
secondary: >
  # Calculate and display the number of lights on in the secondary text
  {% set ns = namespace(lights_on=[]) %}
  {% set region = floor_areas('ground_floor') %}
  {% for room in region %}
    {% set ns.lights_on = ns.lights_on + 
    [area_entities(room) | select('match', 'light.*') | select('is_state', 'on') | list | count ] %}
  {% endfor %}
  # Display the message with the count of lights on
  There are {{ ns.lights_on | sum }} lights on the ground floor, {{user}}!
tap_action:
  action: call-service
  service: script.light_ground_floor_off
  target: {}
badge_color: ''
multiline_secondary: false
hold_action:
  action: call-service
  service: script.light_ground_floor_on
  target: {}


Your comments will break those templates. Comments in Jinja need to be enclosed in comment delimiters {# #}.

As Taras showed in the previous post, there is no need to use all those loops since the area_entities function can be applied across the list of areas using map, and the list can be flattened with sum:

type: custom:mushroom-template-card
primary: Ground Floor
icon: >
  {% set x = floor_areas('ground_floor') | map('area_entities') | sum(start=[])
  | select('match', 'light') | select('is_state', 'on') | list  | count %}
  {{ 'mdi:lightbulb-on' if x > 0 else 'mdi:lightbulb-off' }}
icon_color: >-
  {% set x = floor_areas('ground_floor') | map('area_entities') | sum(start=[])
  | select('match', 'light') | select('is_state', 'on') | list  | count %}
  {{ 'yellow' if x > 0 else 'gray' }}
secondary: >
  {% set x = floor_areas('ground_floor') | map('area_entities') | sum(start=[])
  | select('match', 'light') | select('is_state', 'on') | list | count %}
  There are {{ x }} lights on the ground floor, {{user}}!
tap_action:
  action: call-service
  service: script.light_ground_floor_off
  target: {}
multiline_secondary: false
hold_action:
  action: call-service
  service: script.light_ground_floor_on
  target: {}
1 Like

I’d prefer to keep logic like that away from the dashboard. Put it in a template sensor then you don’t need to recalculate it three times.

It’s much easier to read and understand like this, thank you!

I wanted to add comments to provide a bit more explanation in case someone needed it, but I hadn’t included any in my code. I’ll remember to close my comments in the future! (I’m just getting started.:grin:)

Could you explain your solution a bit more? I haven’t used template sensors before and I’m curious how they could simplify things. How would this help in avoiding multiple recalculations? Thanks!

See the UI configuration section — very easy. Just use the counting template from above as the state template, then you have a sensor showing the lights on on that floor.

{{ floor_areas('ground_floor')
   |map('area_entities')|sum(start=[])
   |select('match', 'light')|select('is_state', 'on')
   |list|count }}

Then remove the {% set x... %} lines in your card and use states('sensor.gf_on')|int(0) (replace with the actual entity ID) in place of x.