Repository navigation
feat(blueprints): add an HA automation blueprint for alert notifications - #64
Merged
Merged
Conversation
docs/automations.example.yaml opened with two automations that differed only in which alert binary_sensor they watched, which notify service they called, and the title string. Every alert rule a user adds means another 20-line copy-paste with the same entity id repeated inside two templates. That is the shape a blueprint exists for. The motivating failure is #62. `drone_detected` and `drone_conflict`/ `drone_nearby` fired on the same tick for essentially every close-in drone, because nothing stopped a user assembling overlapping examples by hand; the fix was a hand-written condition in the example. A blueprint instantiated once per rule cannot produce that class of bug at all. Design notes: An entity selector, not a hardcoded entity id. Alert entity ids derive from user-configured rule names, so the blueprint has no way to know them — but it does not need to. The user picks from whatever their own config produced, including rules that do not exist yet. The templates read only the triggering alert's own attributes. The existing drone examples cross-reference sensor.airspace_nearest_drone to get RID detail, which silently describes the wrong drone whenever a second, non-nearest drone is what actually tripped the rule (the alert-info-drone- fields TODO in publisher.py). Reading one entity is strictly correct; it just cannot show drone-specific fields until that TODO lands, so the drone automations stay in the examples file rather than being replaced. Blueprint `variables:` are safe here in a way they are not in a hand-written automation. The stored automation is only `use_blueprint: {path, input}`, so the UI editor never round-trips the body and cannot drop them — which is why docs/automations.example.yaml has to inline `{% set %}` in every single field and this does not. Only the two military_close examples are removed. The emergency-squawk pair is stateful (creates *and* dismisses a persistent notification) and the drone automations need the cross-reference above, so neither reduces to a per-rule notification. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FiVqZDdasMeshTd7CqdxEp
Required by the version-bumped CI gate (v1.1.2 is tagged). Note this release contains no runtime change at all — the diff is a blueprint, docs, and tests, with zero files under src/. The blueprint is consumed from the repo by raw URL, not from the add-on image, so it is available the moment this lands on main and tagging v1.1.3 is optional. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
docs/automations.example.yamlopened with two automations that differed onlyin which alert
binary_sensorthey watched, which notify service they called,and the title string. Every alert rule a user adds means another ~20-line
copy-paste with the same entity id repeated inside two Jinja templates. There
are eight alert rules in a typical config.
The motivating failure is #62.
drone_detectedanddrone_conflict/drone_nearbyfired on the same tick for essentially everyclose-in drone, because nothing stopped a user assembling overlapping examples
by hand — the fix was a hand-written condition in the example YAML. A blueprint
instantiated once per rule cannot produce that class of bug at all.
What it does
Import once, add one instance per rule from the UI. Six inputs: alert entity,
notify service, title, watchpoint, include-photo, notification group.
Photo and grouping degrade to
{}when unavailable, so an airframe with noPlanespotters photo just gets a plain notification.
Design notes
An entity selector, not a hardcoded entity id. Alert entity ids derive from
user-configured rule names, so the blueprint cannot know them — and does not
need to. The user picks from whatever their own config produced, including
rules that do not exist yet.
Reads only the triggering alert's own attributes. The existing drone
examples cross-reference
sensor.airspace_nearest_dronefor RID detail, whichsilently describes the wrong drone whenever a second, non-nearest drone is
what actually tripped the rule (the
alert-info-drone-fieldsTODO inpublisher.py). Reading one entity is strictly correct — it just cannot showdrone-specific fields until that TODO lands, which is why the drone
automations stay in the examples file rather than being replaced.
Blueprint
variables:are safe here in a way they are not in ahand-written automation: the stored automation is only
use_blueprint: {path, input}, so the UI editor never round-trips the body andcannot drop them. That is why
docs/automations.example.yamlhas to inline{% set %}in every field and this does not.Only two examples removed. The emergency-squawk pair is stateful (creates
and dismisses a persistent notification) and the drone automations need the
cross-reference above, so neither reduces to a per-rule notification.
Drift guard
tests/test_blueprints.py(13 tests). A blueprint is a public compatibilitysurface with no
schema_versionequivalent — rename an attribute and everyimported automation silently renders an empty notification, with nothing in HA
validating it. So:
publisher._alert_infoactually publishes
silently does nothing)
all attributes present and all absent
That last one earned its keep immediately: the first draft used
dict(**a, **b)to build the notification data. Valid Python; Jinja rejects itwith "invalid syntax for function call expression". It would have thrown at
runtime with no signal beyond a missing notification. Rewritten with a
namespace+dict()merge.Breaking?
No. Zero files under
src/— verified. No payload, discovery, or topicchanges, so the MQTT contract and
schema_versionare untouched. The onlyremoval is two examples from a docs file; copy-pasted automations live in the
user's own config and are unaffected.
Notes
jinja2dev dependency for the template-compile check.Not in the runtime path — the service never renders templates.
version bumpedCI gate. Thisrelease has no runtime change, and the blueprint is consumed from the repo by
raw URL rather than from the add-on image — so tagging v1.1.3 is optional.
Merging makes the blueprint importable immediately; tagging would push a
functionally identical image to every install.
alert-info-drone-fieldsTODO(
mqtt/publisher.py). Fixing it would let the blueprint fully replace thedrone examples and fix the existing silent-mismatch bug.
🤖 Generated with Claude Code