System crashes when using extensive template sensors

Hi folks
I figured out that when I deploy too many of the template sensors below the system gets first very unstable (when deploying about 5-10) and then crashes all the time (when deploying more than 10). As soon as I deactivate enough sensors the system gets back operational as if nothing happened.
So I assume its either a) system related or my template sensors are b) poorly written (I’m definetly not a pro :sweat_smile:)

Goal of the whole thing is to have specific “Problem”-Sensors per Room and also per Problem Category so I can use them in Lovelace and also for reporting as text-to-speech.

To get that I’m at the stage where I need a sensor like the one below per Room/Level and House but also a “count” of that same result, since I cannot use the same template to do both (but maybe I’m wrong here). If I count that correctly I would have to deploy about 40 of them (1x 20 to get the sensor with text and 1x 20 to get the sensor count) which would definitely be overload as it stands now.

I assume that something which would help a lot is not iterating over all binary sensors severall times, just the ones of interest: beginning with

  • binary_sensor.problem*
  • binary_sensor.climate*
    but I do not know how to convert the “states.binary_sensor” part into a working “states.binary_sensor.problem*” thing…

Or maybe there is a totally different idea around here how to filter several sensors by entity_id-Beginning and labels?
So any help in either pointing me into the right direction or optimizing the template would be appreciated.

Current setup:
HassOS direct on Beelink S12 pro with 16GB RAM and 512GB SSD
Core: 2025.1.4
Supervisor: 2025.02.1
Operating System: 14.2
Frontend: 20250109.2

Same behaviour tested and occurs with Core 2025.2.3

Here is the code:

{# create list to exclude #}
{% set label = label_entities('to_be_installed') %}
{% set exclude = states.binary_sensor | selectattr('entity_id', 'in', label) 
  | map(attribute='entity_id') | list %}

{# create list to ignore for display #}
{% set label = label_entities('problem_exclude') %}
{% set exclude_disp = states.binary_sensor | selectattr('entity_id', 'in', label) 
  | map(attribute='entity_id') | list %}

{# create list to include #}
{% set label = label_entities('problem_gets_critical_when_nobody_home') %}
{% set include = states.binary_sensor | selectattr('entity_id', 'in', label) 
  | map(attribute='entity_id') | list %}

{# create list of binary_sensors to check, exlude level changes on occupancy #}
{% set labeltype = label_entities('problem_house') %}
{% set labellevel = label_entities('problem_critical') %}
{% set labelleveldrop_a = label_entities('problem_gets_info_when_nobody_home') %}
{% set labelleveldrop_b = label_entities('problem_gets_important_when_nobody_home') %}
{% set list = states.binary_sensor | expand()
  | selectattr('entity_id', 'in', labeltype) 
  | selectattr('entity_id', 'in', labellevel) 
  | rejectattr('entity_id', 'in', labelleveldrop_a) 
  | rejectattr('entity_id', 'in', labelleveldrop_b) 
  | map(attribute='entity_id') | list %}

{# add includes #}
{% set list = list + include %}

{# exlude not installed and to ignore from displaying #}
{% set list = expand(list)
  | rejectattr('entity_id', 'in', exclude) 
  | rejectattr('entity_id', 'in', exclude_disp) 
  | map(attribute='entity_id') | list %}

{# check for state #}
{% set list = expand(list) 
  | selectattr('state', 'eq', 'on')  
  | map(attribute='entity_id') | sort | reverse | list %}

{# swap entity_id with friendly name #}
{% set list = expand(list) 
| map(attribute='attributes.friendly_name') | list %}

{# if list is very long... #}
{% if list | count > 10 -%}Es gibt {{ list |count }} kritische Probleme, diese solltest du schnellstmöglich beheben.
{# if list NOT empty... #}
{% elif list | count > 0 -%}Kritische Probleme solltest du schnellstmöglich beheben, und zwar:
{# if list is NOT empty then create readable output. #}
{%- if list | count > 2 -%}
{% for item in list -%}
{% if loop.revindex == 2 -%}{{ item }} {% elif loop.last -%} und {{ item }}. {% else -%}{{ item }}, {% endif -%}{% endfor -%}
{% elif list | count == 2 %}{% for item in list -%}{% if loop.last -%} und {{ item }}. {% else -%}{{ item }} {% endif -%}{% endfor -%}
{% elif list | count == 1 %}{% for item in list -%}{{ item }}.{% endfor -%}
{% endif %}
{# if list IS empty... #}
{% else %}none
{% endif %}

Using the state object method (states.binary_sensors) multiple times then essentially de-expanding and re-expanding is inefficient. Since functions like label_entities() return a list of entity IDs, you can improve it by using functions and tests that work on entity IDs. A lot of HA-specific tests have been added over the past couple years for use with the select() filter to help make templates more efficient.

For example, instead of starting multiple lists from a large list of state objects, then selecting only the state objects whose entity IDs are in a smaller list based on a label; start with the smaller label-based list and use select() with match as your test to reduce the pool to just binary_sensor entities.

Another useful method is to use the map() filter to iterate other functions over your list. In this case a filter like | map('state_attr', 'friendly_name') can be applied to a list of entity IDs and replaces expanding the list, then de-expanding it by mapping the friendly_name attribute.

{# create list to exclude #}
{% set label = label_entities('to_be_installed') %}
{% set exclude = label | select('match', 'binary_sensor.') | list %}

{# create list to ignore for display #}
{% set label = label_entities('problem_exclude') %}
{% set exclude_disp = label | select('match', 'binary_sensor.') | list %}

{# create list to include #}
{% set label = label_entities('problem_gets_critical_when_nobody_home') %}
{% set include = label | select('match', 'binary_sensor.') | list %}

{# create list of binary_sensors to check, exlude level changes on occupancy #}
{% set labeltype = label_entities('problem_house') %}
{% set labellevel = label_entities('problem_critical') %}
{% set labelleveldrop_a = label_entities('problem_gets_info_when_nobody_home') %}
{% set labelleveldrop_b = label_entities('problem_gets_important_when_nobody_home') %}
{% set selected = (labeltype + labellevel) | unique | select('match', 'binary_sensor.') | list %}
{% set rejected = (labelleveldrop_a + labelleveldrop_b) | unique | select('match', 'binary_sensor.') | list %}
{% set list = selected | reject('in', rejected) | list %}

{# add includes #}
{% set list = list + include %}

{# exclude not installed and to ignore from displaying #}
{% set list = list
  | reject('in', exclude) 
  | reject('in', exclude_disp)
  | list %}

{# check for state #}
{% set list = list 
  | select('is_state', 'on')  
  | sort(reverse=true) | list %}

{# count #}
{% set l_count = list | count %}

{# if list is very long... #}
{% if l_count > 10 -%}Es gibt {{ l_count }} kritische Probleme, diese solltest du schnellstmöglich beheben.
{# if list NOT empty... #}
{% elif l_count > 0 -%}Kritische Probleme solltest du schnellstmöglich beheben, und zwar:
{# if list is NOT empty then create readable output with friendly names. #}
{% set list = list | map('state_attr', 'friendly_name') | list %}
  {{' und '.join((list | join(', ')).rsplit(', ', 1)) }}.
{# if list IS empty... #}
{% else %}none
{% endif %}
2 Likes

Wow, I knew there is a better way :sweat_smile: thank you very much!

Would someone also have an idea if the friendly name contains a string how to replace that in the resulting list from above?

Like the list output would be something like this:
[‘Climate 012’, ‘Climate 012 to cold’, ‘Climate 012 to hot’, ‘Problem Air 012’, ‘Problem Door 012’, ‘Problem Ventilate 012’, ‘Problem Ventilate 012 Openings’]
How to replace " 012" on each item with nothing or something else?

I’ve tried with a for loop but couldn’t get it working.

{{ […] | map('replace', '012', 'something else') | list }}
1 Like

@Didgeridrew and @Mayhem_SWE Wow, many thanks to both of you! Working like a charm now :slight_smile: