Home Assistant: using target in blueprints
TL;DR
I’ve been using tykeal’s Low Battery blueprint (github) in my Home Assistant setup for some time, to give me an alert on any sensor batteries that need changing. There are a number of sensors marked “battery” that I don’t care to track (for example iPhone batteries), and the blueprint has a spot where you can list entities you want to exclude.
With the very exciting introduction of Labels in HA
2024.4, I decided
that, instead of listing excluded entities in the automation, I’d just tag them with a NoBatteryWarning
label, and then
choose the label in the Blueprint input. But it turns out that didn’t work: the blueprint specified you had to list
entities directly. I set out to fix it and it turned out it was pretty easy.
Why doesn’t it work?
Here’s an excerpt of the original blueprint:
blueprint:
input:
exclude:
name: Excluded Sensors
description: Battery sensors (e.g. smartphone) to exclude from detection.
Only entities are supported, devices must be expanded!
default: { entity_id: [] }
selector:
target:
entity:
device_class: battery
# ...
sensors: >-
{%- set result = namespace(sensors=[]) -%}
{%- for state in states.sensor | selectattr('attributes.device_class', '==', 'battery') -%}
{%- if 0 <= state.state | int(-1) < threshold | int and not state.entity_id in exclude.entity_id -%}
A selector
is a thing which shows up in the UI and lets you pick from a filtered list. The target
selector is the
control you’re used to seeing all over HA Settings pages, that looks like this:
It turns out when it’s evaluated and you consume it a as a variable, you get a map that looks like this (I wish this were in the doc but I can’t find it):
{
entity_id: [],
device_id: [],
area_id: [],
label_id: [],
}
The original blueprint was only iterating over the entity_id
list, so it was ignoring any other selections that you
made.
The fix: just get all the entity ids
But this isn’t really hard to fix. There are a set of functions that will give you all the entities in a label, area,
or device, called unsurprisingly label_entities
, area_entities
and device_entities
. I based the code on This
forum
post:
exclude: !input "exclude"
exclude_entities: >
{%- set ns = namespace(ret=[]) %}
{%- for key in ['device', 'area', 'entity', 'label'] %}
{%- set items = exclude.get(key ~ '_id', []) %}
{%- if items %}
{%- set items = [ items ] if items is string else items %}
{%- set items = items if key == 'entity' else items | map(key ~ '_entities') | sum(start=[]) %}
{%- set ns.ret = ns.ret + [ items ] %}
{%- endif %}
{%- endfor %}
{{ ns.ret | sum(start=[]) }}
# The same snippet from above but now it refers to exclude_entities instead of exclude.entity_id
sensors: >-
{%- set result = namespace(sensors=[]) -%}
{%- for state in states.sensor | selectattr('attributes.device_class', '==', 'battery') -%}
{%- if 0 <= state.state | int(-1) < threshold | int and not state.entity_id in exclude_entities -%}
So for each of the selector types (device, area, entity, and label), we expand them into entity ids and collect them into a list, then we have a list of all the entities.
I forked tykeal’s blueprint to my own repo, you can use it by clicking this badge: