Filtering and counting entities in state by area - Jinja templates in front end?

I’m trying to build a “room summary” dashboard card based on button-card. Along with some quick access buttons (for scenes, etc), I want it to have info like:

  • Area name
  • Count of of lights on
  • Count of shades open

I already have all my lights organized by area, so a template like this produces the correct count:

{{
  states.light
  | selectattr('entity_id', 'in', area_entities('Family Room'))
  | map(attribute='entity_id')
  | list | count
}}

However, as far as I can tell, you can’t just use Jinja templates (and the HASS extensions) in a lovelace/dashboard configuration file.

Is the only way to turn the above into a sensor entity? Like this:

template:
  - sensor:
    - name: Number of lights on in Family Room
      state: >
        {{
          states.light
          | selectattr('entity_id', 'in', area_entities('Family Room'))
          | map(attribute='entity_id')
          | list | count
        }}

I can do that of course, and then use that entity in my card, but then I have to create sensors for every single area. Is there a way to achieve something like what I want without having to resort to manually creating sensors for every room?

2 Likes

The standard set of cards (with the exception of the Markdown card) don’t support templates. So your options are to either use a custom card that does support templates or create Template Sensors (one for each area, or a single sensor with multiple attributes representing each area).

OK, thanks, I figured that was the case for the front end.

What I’m thinking about now is trying to have a set of “sensors” that I can access per group or area, without having to manually create them all, but that doesn’t seem to be possible either. It doesn’t seem like groups have any inherent attributes (like the number of lights on within them). The best I can come up with right now is to manually create something like this for every area:

template:
  - sensor:
    - name: Area Family Room
      state: >
        {%
          set lightson = states.light
          | selectattr('entity_id', 'in', area_entities('Family Room'))
          | selectattr('state', 'eq', 'on')
          | map(attribute='entity_id')
          | list
          | count
        %}
        {% if (lightson > 0) %}on{% else %}off{% endif %}
      attributes:
        lights_count: >
          {{ states.light|selectattr('entity_id', 'in', area_entities('Family Room'))|list|count }}
        lights_on: >
          {{ states.light|selectattr('entity_id', 'in', area_entities('Family Room'))|select('is_state', 'on')|list|count }}
        shades_count: >
          {{ states.cover|selectattr('entity_id', 'in', area_entities('Family Room'))|list|count }}
        shades_open: >
          {{ states.cover|selectattr('entity_id', 'in', area_entities('Family Room'))|select('is_state', 'open')|list|count }}

Which is fine I guess, but it just kind of sucks because it’s not DRY…I have over a dozen areas, and the only difference between each of these will be the area name…is there any way to “parameterize” a sensor entity like this?

1 Like

A Template Sensor is created at startup based on whatever you had specified in its configuration. First the YAML is processed followed by the Jinja2 templates. To “parameterize” would require some sort of pre-processor for the Jinja2 processor (i.e. it doesn’t exist here so, no, you can’t “parameterize” it).


Based on what I’ve seen in your example, I suggest you consider creating a Template Binary Sensor (its meant for simply displaying on/off states).

template:
  - binary_sensor:
      - name: Area Family Room
        state: "{{ area_entities('Family Room') | select('match', 'light') | select('is_state', 'on') | list | count > 0 }}"
        attributes:
          lights_count: "{{ area_entities('Family Room') | select('match', 'light') | list | count }}"
          lights_on: "{{ area_entities('Family Room') | select('match', 'light') | select('is_state', 'on') | list | count }}"
          shades_count: "{{ area_entities('Family Room') | select('match', 'cover') | list | count }}"
          shades_open: "{{ area_entities('Family Room') | select('match', 'cover') | select('is_state', 'open') | list | count }}"

In addition, there’s no need for the templates to begin by filtering entire domains (like states.light and states.cover). Start with area_entities for the desired area and then use match for the desired domain.


BTW, you may have noticed there’s a fair bit of ‘template duplication’ and it would be nice if Template Sensors supported variables like automation and scripts. If you like that idea, consider voting for the following Feature Request:


EDIT

Here’s a little trick I just tried and it appears to work. It might save you a few keystrokes when creating Template Binary Sensors for each area in your home.

Instead of explicitly specifying the area’s name in each template, use the self-referential this.name

For example, if the Template Binary Sensor’s name is simply the area name, like Family, then the value of this.name will be Family wherever used in the template.

template:
  - binary_sensor:
      - name: Family
        state: "{{ area_entities(this.name) | select('match', 'light') | select('is_state', 'on') | list | count > 0 }}"
        attributes:
          lights_count: "{{ area_entities(this.name) | select('match', 'light') | list | count }}"
          lights_on: "{{ area_entities(this.name) | select('match', 'light') | select('is_state', 'on') | list | count }}"
          shades_count: "{{ area_entities(this.name) | select('match', 'cover') | list | count }}"
          shades_open: "{{ area_entities(this.name) | select('match', 'cover') | select('is_state', 'open') | list | count }}"

This makes the configuration a bit more generic and can be copy-pasted, to create another Template Binary Sensor, with minimal editing (just change the value of name).

If you want the value of name to be more descriptive, like Area Family Room then you’ll need a bit more than merely this.name because you’ll have to extract the string Family out of Area Family Room. For example:

template:
  - binary_sensor:
      - name: Area Family Room
        state: "{{ area_entities(this.name[5:-5]) | select('match', 'light') | select('is_state', 'on') | list | count > 0 }}"
        attributes:
          lights_count: "{{ area_entities(this.name[5:-5]) | select('match', 'light') | list | count }}"
          lights_on: "{{ area_entities(this.name[5:-5]) | select('match', 'light') | select('is_state', 'on') | list | count }}"
          shades_count: "{{ area_entities(this.name[5:-5]) | select('match', 'cover') | list | count }}"
          shades_open: "{{ area_entities(this.name[5:-5]) | select('match', 'cover') | select('is_state', 'open') | list | count }}"
3 Likes

Hello @rogersmj Can you explain how you get that attribute into your light ?

I’m also counting but via using group of lights created manualy.

area_entities isn’t an attribute it’s a templating function.

1 Like

I believe I have a means for you to create multiple Template Binary Sensors, one per area, with the least amount of effort. It employs this.name, as mentioned in my previous post, plus the use of YAML anchors and aliases.

The following configuration creates four Template Binary Sensors. The second through fourth sensors share the same configuration as the first except they have their own unique name option. Any change you make to the first one’s options or templates are inherited by the other three sensors.

template:
  - binary_sensor:
      - name: Area Family Room
        <<: &area_info
          state: "{{ area_entities(this.name[5:-5]) | select('match', 'light') | select('is_state', 'on') | list | count > 0 }}"
          attributes:
            lights_count: "{{ area_entities(this.name[5:-5]) | select('match', 'light') | list | count }}"
            lights_on: "{{ area_entities(this.name[5:-5]) | select('match', 'light') | select('is_state', 'on') | list | count }}"
            shades_count: "{{ area_entities(this.name[5:-5]) | select('match', 'cover') | list | count }}"
            shades_open: "{{ area_entities(this.name[5:-5]) | select('match', 'cover') | select('is_state', 'open') | list | count }}"

      - name: Area Dining Room
        <<: *area_info
      - name: Area Living Room
        <<: *area_info
      - name: Area Kitchen Room
        <<: *area_info

The only remaining task is to adopt a uniform naming convention for the sensors so that it’s easy to consistently extract the sensor’s area from its name. The example above assumes the name is always three words in the format “Area TheAreaName Room” where “TheAreaName” is the actual area name. Obviously the easiest format would be to simply set the sensor’s name to the area’s name without additional words.

5 Likes

Any progress to report?

That is a really cool idea – I haven’t had time to work on my HA much the last couple of weeks, but I’m going to give that a go this weekend hopefully. Thanks for all your research on this!

That worked great. I used it to count and list the lights on.
Now I’m wondering how to create one sensor for the whole house, that basically adds together all the ones from above

How to select objects in multiple areas?
For example: a list of temperature sensors placed in the kitchen, hall and bedroom.

@123

It’s a kind of copy/paste of the whole section ?
Do you have any documentation or website that describe those technics ?

Thank you

YAML Anchors and Aliases

1 Like

I love this solution. I am building an area based dashboard and wanted the state of the area to be maintained by the logic, if 1 light is on the entire area is seen as on. I made an entry for each area into YAML and boom amazingly simple. Thanks @123 for the post. I have been using Home assistant for 2+ years but just now indulging templates and this was a huge eye opener for me on the templates capabilities.

I iterated off the discussion here to create this binary sensor which updates the icon based on my garage door state and updates the state based on the lights in the area.

template:
  - binary_sensor:
       - name: Area Garage
        state: "{{ area_entities(this.name[5:]) | select('match', 'light') | select('is_state', 'on') | list | count > 0 }}"
        icon: >
          {% if is_state("cover.garage_door", ["open"]) %}
            mdi:garage-open
          {% elif is_state("cover.garage_door", ["opening", "closing"]) %}
            mdi:garage-alert
          {% else %}
            mdi:garage
          {% endif %}
        attributes:
          lights_count: "{{ area_entities(this.name[5:]) | select('match', 'light') | list | count }}"
          lights_on: "{{ area_entities(this.name[5:]) | select('match', 'light') | select('is_state', 'on') | list | count }}"

You’re welcome!

BTW, there’s a minor typo in your example’s icon template. Replace the word closeing with closing.

EDIT

Also adjust the indentation of the line containing the name option (move it to the left by one space).

Thanks, I would have been troubleshooting that for at least an hour. :wink:

This is really great. I expanded upon it a bit.

- binary_sensor:
    - name: Living Room Summary
      <<: &area_info
          state: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'light' ) | selectattr ( 'state' , 'eq' , 'on' ) | list | count > 0 }}"
          attributes:
            lights_count: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'light' ) | list | count }}"
            lights_on: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'light' ) | selectattr ( 'state' , 'eq' , 'on' ) | list | count }}"
            lights_off: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'light' ) | selectattr ( 'state' , 'eq' , 'off' ) | list | count }}"
            covers_count: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'cover' ) | list | count }}"
            covers_open: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'cover' ) | selectattr ( 'state' , 'eq' , 'open' ) | list | count }}"
            covers_closed: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'cover' ) | selectattr ( 'state' , 'eq' , 'closed' ) | list | count }}"
            fans_count: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'fan' ) | list | count }}"
            fans_on: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'fan' ) | selectattr ( 'state' , 'eq' , 'on' ) | list | count }}"
            fans_off: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'fan' ) | selectattr ( 'state' , 'eq' , 'off' ) | list | count }}"
            switches_count: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'switch' ) | list | count }}"
            switches_on: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'switch' ) | selectattr ( 'state' , 'eq' , 'on' ) | list | count }}"
            switches_off: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'switch' ) | selectattr ( 'state' , 'eq' , 'off' ) | list | count }}"
            locks_count: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'lock' ) | list | count }}"
            locks_locked: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'lock' ) | selectattr ( 'state' , 'eq' , 'locked' ) | list | count }}"
            locks_unlocked: "{{ expand(area_entities(this.name[:-7])) | selectattr ( 'domain' , 'eq' , 'lock' ) | selectattr ( 'state' , 'eq' , 'unlocked' ) | list | count }}"
    
    - name: Kitchen Summary
      <<: *area_info
        
    - name: Dining Room Summary
      <<: *area_info
        
    - name: Master Bedroom Summary
      <<: *area_info