feat(doorman): honor local_unlock flag from break-glass scans [#345]

Break-glass badges fire the relay locally on the reader host (no HA
dependency); the flagged scan still reaches HA for the audit trail
but skips re-dispatch. NOTE: deployed while pfv-bms is down — YAML
lint clean, ha core check to run at Friday boot before arming.

https://projects.knownelement.com/issues/345
This commit is contained in:
2026-09-03 08:15:21 -05:00
parent 66a5c45b7e
commit 2519aec5b6
+12 -8
View File
@@ -155,8 +155,10 @@ automation:
checked: roster sensors are not populated. Logged checked: roster sensors are not populated. Logged
only; whitelist NOT evaluated. Check gitea only; whitelist NOT evaluated. Check gitea
connectivity / KNEL/access-roster. connectivity / KNEL/access-roster.
# Known + enabled badge: log, then dispatch unlock IF armed # Known + enabled badge: log, then dispatch unlock IF armed AND
# (founder-level input_boolean). Disarmed = log + notice only. # the listener didn't already handle it locally (break-glass
# scans carry local_unlock:true — relay already fired on the
# reader host; HA logs without re-dispatching).
- conditions: "{{ is_known and is_enabled }}" - conditions: "{{ is_known and is_enabled }}"
sequence: sequence:
- action: logbook.log - action: logbook.log
@@ -166,9 +168,10 @@ automation:
Badge {{ badge_id }} ({{ holder }}) accepted at Badge {{ badge_id }} ({{ holder }}) accepted at
{{ reader }}. {{ reader }}.
- if: - if:
- condition: state - condition: template
entity_id: input_boolean.doorman_unlock_enabled value_template: >-
state: 'on' {{ is_state('input_boolean.doorman_unlock_enabled', 'on')
and not (trigger.json.local_unlock | default(false) | bool(false)) }}
then: then:
- action: rest_command.doorman_unlock - action: rest_command.doorman_unlock
continue_on_error: true continue_on_error: true
@@ -181,10 +184,11 @@ automation:
title: Doorman — badge accepted, UNLOCK dispatched title: Doorman — badge accepted, UNLOCK dispatched
message: "{{ holder }} scanned at {{ reader }} ({{ scanned }}). Door released for the hold window." message: "{{ holder }} scanned at {{ reader }} ({{ scanned }}). Door released for the hold window."
else: else:
- action: persistent_notification.create - action: logbook.log
data: data:
title: Doorman — badge accepted, unlock NOT armed name: Doorman
message: "{{ holder }} scanned at {{ reader }} ({{ scanned }}). Unlock is disarmed — door stayed shut." message: >-
No HA dispatch — {{ 'already released locally via break-glass' if trigger.json.local_unlock | default(false) | bool(false) else 'unlock disarmed' }}.
# In roster but disabled: flag it loudly (stolen/suspended # In roster but disabled: flag it loudly (stolen/suspended
# badge use is exactly what we want to know about). # badge use is exactly what we want to know about).
- conditions: "{{ is_known and not is_enabled }}" - conditions: "{{ is_known and not is_enabled }}"