Skip to content
Go back

Home Assistant Blueprints: Write Once

By KingPin 13 min read
Home Assistant Blueprints: Write Once
Contents

You have nine motion-sensor lights. You wrote the first automation, copied it eight times, and swapped the entity IDs. Then you decided the lights should wait 2 minutes instead of 1, and you got to edit nine files. At 2 AM, with one of them still wrong.

A blueprint fixes this. It is an automation (or script) with the changeable parts cut out and replaced by named inputs. You write the logic once, then create as many automations from it as you want, each with its own sensor and light. Fix a bug in the blueprint and every automation built from it gets the fix.

Full example: Clone the example files at github.com/KingPin/sumguy-examples/home-automation/home-assistant-blueprints

Copy-paste is the problem

Here is what the nine-copy approach looks like. Same trigger, same actions, one entity ID different:

automations.yaml (the pain)
- alias: Hallway motion light
triggers:
- trigger: state
entity_id: binary_sensor.hallway_motion
to: "on"
actions:
- action: light.turn_on
target:
entity_id: light.hallway
- delay: 120
- action: light.turn_off
target:
entity_id: light.hallway

Copy that for the kitchen, the garage, the porch. Now find the bug where the light turns off while you are still in the room. You fix it eight times and miss one. A blueprint turns those nine copies into nine short instances of one definition.

This post uses the current YAML syntax from Home Assistant 2024.10 onward: triggers:, conditions:, and actions: are plural, and each trigger starts with - trigger: state (it used to be platform:). The latest release at the time of writing is 2026.11.0. Everything below follows the official docs for that version.

Import a community blueprint

Before you write anything, import one. Go to Settings > Automations & scenes > Blueprints, select Import blueprint, paste an address, and select Preview, then Import blueprint. Home Assistant imports from the community forum, GitHub, GitHub gists, and the Home Assistant website. For other addresses it asks you to continue only if you trust the source. A blueprint can run actions on your house, so treat an import like running a script from the internet.

Many blueprint pages also have an Import blueprint button. That button is a My Home Assistant link. You can build your own for any blueprint file:

https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=<URL-OF-THE-YAML-FILE>

It opens the import dialog on your instance with the address pre-filled.

Imported blueprints land on disk like this:

config/blueprints/automation/<author>/<name>.yaml
config/blueprints/script/<author>/<name>.yaml

The folder name comes from the source. For GitHub it is the user, and the file keeps its name. You can create your own subfolders, and you can skip the UI import and drop a file in by hand. Reload automations (or scripts) and it shows up.

Anatomy of a blueprint

A blueprint is a normal automation with a blueprint: header on top. Only name and domain are required. Here is the header from the motion light we build in this post:

blueprints/automation/sumguy/motion_light.yaml (header)
blueprint:
name: Motion Light (SumGuy)
description: >
Turn lights on when motion is detected, keep them on for a while after
the last motion, then turn them off. Optionally only when it is dark.
domain: automation
author: SumGuy
source_url: https://github.com/KingPin/sumguy-examples/blob/main/home-automation/home-assistant-blueprints/blueprints/automation/sumguy/motion_light.yaml
homeassistant:
min_version: 2024.10.0
input:
motion_sensor:
name: Motion sensor
selector:
entity:
filter:
- domain: binary_sensor
device_class: motion
- domain: binary_sensor
device_class: occupancy

The keys:

Inputs and selectors

Each input can have a name, description, default, and a selector. The selector controls the form widget. Without one, the user gets a bare text box and has to guess what goes in it. That is how you end up with a bug report from yourself.

The selectors used in this post:

Inputs from this post's blueprints (abridged)
lights:
name: Lights
selector:
target:
entity:
domain: light
no_motion_wait:
name: Wait time
default: 120
selector:
number:
min: 0
max: 3600
unit_of_measurement: seconds
only_when_dark:
name: Only when dark
default: false
selector:
boolean:
active_after:
name: Active after
default: "00:00:00"
selector:
time:
alert_actions:
name: Alert actions
default: []
selector:
action:

alert_actions comes from the door alert blueprint later in this post. In the motion light, the boolean and time inputs sit inside a collapsible Extras section, shown further down.

Using inputs with !input

Anywhere outside the blueprint: header, !input name is replaced with whatever the user chose (or the default). It works for whole objects too, not just strings:

blueprints/automation/sumguy/motion_light.yaml (body, abridged)
triggers:
- trigger: state
entity_id: !input motion_sensor
from: "off"
to: "on"
actions:
- alias: Turn on the light
action: light.turn_on
target: !input lights
- alias: Wait for motion to stop
wait_for_trigger:
- trigger: state
entity_id: !input motion_sensor
from: "on"
to: "off"
- delay:
seconds: !input no_motion_wait
- action: light.turn_off
target: !input lights

Pair it with mode: restart so a new motion event resets the timer. That is the same shape as the built-in Motion-activated Light blueprint that ships with Home Assistant.

The template gotcha

You cannot write !input inside a Jinja template. The docs are direct about it: inputs are custom YAML tags, not template variables. This fails:

# Broken: !input is not available inside {{ }}
brightness_pct: "{{ !input brightness }}"

Assign the input to a variable first, then use the variable:

blueprints/automation/sumguy/motion_light.yaml (variables)
variables:
brightness: !input brightness
only_when_dark: !input only_when_dark
lux_sensor: !input lux_sensor
dark_below: !input dark_below
conditions:
- condition: template
value_template: >
{{ not only_when_dark
or lux_sensor == ''
or states(lux_sensor) | float(0) < dark_below | float(0) }}
actions:
- action: light.turn_on
target: !input lights
data:
brightness_pct: "{{ brightness }}"

The variables: block makes each input a real template variable. Script blueprints do the same thing with a script-level variables: block.

trigger_variables for triggers

variables: only exists once a run has started, so triggers cannot see it. For templates inside triggers, such as a trigger’s enabled option, use trigger_variables:. Home Assistant evaluates these when it sets up the triggers (when the automation loads or turns on), and only limited templates work there. A plain !input assignment is fine:

blueprints/automation/sumguy/door_open_alert.yaml (trigger_variables)
trigger_variables:
repeat_reminder: !input repeat_reminder
triggers:
- trigger: state
entity_id: !input door_sensor
to: "on"
for:
minutes: !input open_minutes
- trigger: time_pattern
minutes: "/30"
enabled: "{{ repeat_reminder }}"

The second trigger exists only when the user flips the repeat toggle. If the input is a lone boolean and you do not need a template, skip all this and write enabled: !input repeat_reminder directly. The time_pattern reminder is clock-aligned: it fires at :00 and :30 of each hour, not 30 minutes after the first alert.

The same blueprint ends with actions: !input alert_actions. The user builds the notification in the normal action editor, and the blueprint runs it. One blueprint, any alert.

Collapsible input sections

A blueprint with twelve inputs is a wall of form fields. Release 2024.6 added input sections: group inputs under a key that has its own input: map, and optionally collapse it.

blueprints/automation/sumguy/motion_light.yaml (sections)
extras:
name: Extras
icon: mdi:tune
description: Optional tweaks. Safe to leave alone.
collapsed: true
input:
brightness:
name: Brightness
default: 100
selector:
number:
min: 1
max: 100
unit_of_measurement: "%"
only_when_dark:
name: Only when dark
default: false
selector:
boolean:

Three rules. Sections are display only: input names must stay unique across the whole blueprint, and !input brightness ignores the section. Every input in a collapsed section needs a default, or the user cannot leave it hidden. And set min_version to at least 2024.6.0, or older versions throw errors.

Create automations from it

In the UI, open the blueprint and select Create automation. Fill in the form, save. In YAML, you point at the file by its path under blueprints/automation/:

automations.yaml
- alias: Hallway motion light
use_blueprint:
path: sumguy/motion_light.yaml
input:
motion_sensor: binary_sensor.hallway_motion
lights:
entity_id: light.hallway
no_motion_wait: 60
- alias: Kitchen motion light
use_blueprint:
path: sumguy/motion_light.yaml
input:
motion_sensor: binary_sensor.kitchen_motion
lights:
area_id: kitchen
no_motion_wait: 300
only_when_dark: true
lux_sensor: sensor.kitchen_lux
dark_below: 30

Each instance is under a dozen lines. Nine rooms are nine small blocks, and the logic lives in one file.

Test with one instance first. Create the hallway automation, walk past the sensor, then open the automation and check its Traces tab. The trace shows which trigger fired, whether the template condition passed, and what each action did. If the dark-only condition blocks you, the trace says so, and you fix the blueprint once instead of nine times. Only then add the other rooms.

Script blueprints

Script blueprints arrived in release 2023.7. The shape is the same, with domain: script and a sequence: instead of triggers:/actions:. Scripts have no triggers, so they are good for reusable things you call from automations, dashboards, or voice.

blueprints/script/sumguy/fade_lights_off.yaml
blueprint:
name: Fade Lights Off (SumGuy)
description: Dim lights in steps, then turn them off. Good for bedtime.
domain: script
homeassistant:
min_version: 2024.10.0
input:
lights:
name: Lights
selector:
target:
entity:
domain: light
steps:
name: Number of steps
default: 5
selector:
number:
min: 2
max: 30
step_seconds:
name: Seconds per step
default: 60
selector:
number:
min: 1
max: 600
unit_of_measurement: seconds
mode: restart
variables:
steps: !input steps
sequence:
- repeat:
count: !input steps
sequence:
- action: light.turn_on
target: !input lights
data:
brightness_pct: >
{{ [ (100 * (steps - repeat.index) / steps) | round(0), 1 ] | max }}
- delay:
seconds: !input step_seconds
- action: light.turn_off
target: !input lights

Instances go in scripts.yaml with the same use_blueprint: block, or you create them from the Blueprints page.

scripts.yaml
bedtime_fade:
alias: Bedtime fade
use_blueprint:
path: sumguy/fade_lights_off.yaml
input:
lights:
entity_id: light.bedroom
steps: 10
step_seconds: 30

The practical split: automation blueprints answer “when does this run”, script blueprints answer “what does this do”. If you want one behavior called from five places, write a script blueprint. If you want one trigger pattern repeated across rooms, write an automation blueprint. Combine them by letting an automation blueprint take an action input that calls your script.

Updating, and the gotcha that eats your edits

When the author publishes a fix, open the Blueprints page, use the three dots menu on the blueprint, and select Re-import blueprint. This works only if the blueprint has a source_url, which imported blueprints and the built-in ones have. A file you wrote by hand without one shows “Unable to re-import blueprint: no source URL”.

Re-import overwrites the file. If you edited the YAML on disk to tweak it, your edits are gone. The docs also warn that an incompatible new version can break your automations, because renamed or removed inputs leave instances pointing at nothing. Before you re-import anything you depend on, copy the old file somewhere.

Changes take effect after a reload. Edit a blueprint file by hand, reload automations, and every automation built from it picks up the new logic. Home Assistant does not need a restart. This is the payoff and the risk in one: one edit changes nine automations at once.

Take control when a blueprint is too tight

Say the kitchen light needs one odd extra condition that the blueprint has no input for. Since release 2024.7 you can open that automation, use the three dots menu, and select Take control. Home Assistant shows a preview of the plain automation, and after you select Yes and save, that automation is a regular one you can edit freely.

The gotchas: it is one way. You cannot convert it back. The blueprint itself does not change, and the other automations keep using it. And it is unavailable for automations that are not stored in automations.yaml or have no ID.

Take control is a fine escape hatch for one weird room. If you find yourself taking control of every third instance, the blueprint is missing an input, so add it.

When not to use a blueprint

A blueprint is a forklift: great when you move the same pallet nine times, silly for one couch. My rules:

If you are unsure, write the plain automation first. Convert it when the second copy shows up. The tutorial in the Home Assistant docs does exactly this: take a working automation, replace the values with !input, add the blueprint: header.

Common Questions

Can I edit a Home Assistant blueprint after importing it?

Yes. Imported blueprints are plain YAML files under config/blueprints/, so you can edit them directly and reload automations to apply the change to every automation using that blueprint. The catch is that Re-import blueprint overwrites your edits. Keep a copy of the file before you re-import.

Do I need to restart Home Assistant after changing a blueprint?

No. Reloading automations is enough. After you edit a blueprint file, reload automations (or scripts, for a script blueprint) and every automation built from it uses the new version. A full restart is unnecessary, and you do not have to edit the individual automations.

Can I convert a blueprint automation back into a normal automation?

Yes, with Take control, available since Home Assistant 2024.7. Open the automation, choose Take control from the three dots menu, then confirm and save. The result is a regular automation. The conversion is permanent, and the original blueprint and its other automations stay unchanged.

What Home Assistant version do I need for blueprints with sections or script blueprints?

Script blueprints need 2023.7 or newer, and collapsible input sections need 2024.6 or newer. The plural triggers: and actions: syntax used in this post needs 2024.10 or newer. Set homeassistant: min_version in the blueprint header so older installs reject it with a clear message.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
systemd Credentials: Secrets Without Env

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts