Evan Moses

My blog and projects

Home Assistant: using target in blueprints

TL;DR

  • Here’s a snippet for use in Home Assistant for turning an arbitrary target selector into a list of entity ids
  • Also here’s a low-battery warning Blueprint that uses it.

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:

Target selector image from HA doc, showing a chooser for entities, devices, areas, and labels

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:

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.