feat(doorman): unlock dispatch — arm switch + rest_command to doorctl [#345]
Founder-approved: valid badge + armed input_boolean -> rest_command fires the pfvsvrpi doorctl endpoint (Tailscale-bound, token+source allowlisted, self-securing relay). Disarm = boolean off = log-only. URL secret on-box only (doorman_unlock_url). https://projects.knownelement.com/issues/345
This commit is contained in:
+45
-11
@@ -8,10 +8,11 @@
|
|||||||
# mechanics, NO submodule (git_pull add-on lacks submodule support),
|
# mechanics, NO submodule (git_pull add-on lacks submodule support),
|
||||||
# NO local copy: access-roster remains the single source of truth.
|
# NO local copy: access-roster remains the single source of truth.
|
||||||
#
|
#
|
||||||
# UNLOCK IS DELIBERATELY NOT WIRED. Known badges are logged and
|
# UNLOCK PATH (founder-approved 2026-09-03): valid badge + the
|
||||||
# notified; nothing actuates. The HA->Pi relay control path is an open
|
# doorman_unlock_enabled input_boolean ON -> rest_command fires the
|
||||||
# design decision (#345 sketch, GLPI #741). Fail-safe: this automation
|
# doorctl endpoint on pfvsvrpi (Tailscale-bound, token+source
|
||||||
# can never open the door.
|
# allowlisted, self-securing relay — KNEL/doorman README). Disarm = the
|
||||||
|
# boolean OFF; unknown/disabled badges never reach this path.
|
||||||
#
|
#
|
||||||
# Change control (founder ruling 2026-09-02/03): this file deploys via
|
# Change control (founder ruling 2026-09-02/03): this file deploys via
|
||||||
# the pfv-bms master pipeline (webhook fast path + sha-watch safety
|
# the pfv-bms master pipeline (webhook fast path + sha-watch safety
|
||||||
@@ -72,6 +73,23 @@ input_datetime:
|
|||||||
has_date: true
|
has_date: true
|
||||||
has_time: true
|
has_time: true
|
||||||
|
|
||||||
|
input_boolean:
|
||||||
|
# Arming switch (founder-level): ON = a valid badge DISPATCHES the
|
||||||
|
# unlock to the pfvsvrpi relay. OFF = valid badges log + notify but
|
||||||
|
# the door stays shut.
|
||||||
|
doorman_unlock_enabled:
|
||||||
|
name: Doorman unlock armed
|
||||||
|
icon: mdi:lock-open-variant
|
||||||
|
|
||||||
|
rest_command:
|
||||||
|
# Fires the doorctl endpoint on pfvsvrpi (systemd socket-activated,
|
||||||
|
# Tailscale-bound, token+source allowlisted; self-securing relay).
|
||||||
|
# URL contains the token -> secret. KNEL/doorman README documents it.
|
||||||
|
doorman_unlock:
|
||||||
|
url: !secret doorman_unlock_url
|
||||||
|
method: get
|
||||||
|
timeout: 20
|
||||||
|
|
||||||
automation:
|
automation:
|
||||||
- id: doorman_badge_scan
|
- id: doorman_badge_scan
|
||||||
alias: Doorman - badge scan ingest + decision
|
alias: Doorman - badge scan ingest + decision
|
||||||
@@ -137,8 +155,8 @@ 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 + quiet notification. Unlock not
|
# Known + enabled badge: log, then dispatch unlock IF armed
|
||||||
# wired by design (see header).
|
# (founder-level input_boolean). Disarmed = log + notice only.
|
||||||
- conditions: "{{ is_known and is_enabled }}"
|
- conditions: "{{ is_known and is_enabled }}"
|
||||||
sequence:
|
sequence:
|
||||||
- action: logbook.log
|
- action: logbook.log
|
||||||
@@ -146,11 +164,27 @@ automation:
|
|||||||
name: Doorman
|
name: Doorman
|
||||||
message: >-
|
message: >-
|
||||||
Badge {{ badge_id }} ({{ holder }}) accepted at
|
Badge {{ badge_id }} ({{ holder }}) accepted at
|
||||||
{{ reader }} — logged, unlock not wired (#741).
|
{{ reader }}.
|
||||||
- action: notify.persistent_notification
|
- if:
|
||||||
data:
|
- condition: state
|
||||||
title: Doorman — badge accepted
|
entity_id: input_boolean.doorman_unlock_enabled
|
||||||
message: "{{ holder }} scanned at {{ reader }} ({{ scanned }}). Logged. Unlock not wired (#741)."
|
state: 'on'
|
||||||
|
then:
|
||||||
|
- action: rest_command.doorman_unlock
|
||||||
|
continue_on_error: true
|
||||||
|
- action: logbook.log
|
||||||
|
data:
|
||||||
|
name: Doorman
|
||||||
|
message: "UNLOCK dispatched to the {{ reader }} relay (armed)."
|
||||||
|
- action: notify.persistent_notification
|
||||||
|
data:
|
||||||
|
title: Doorman — badge accepted, UNLOCK dispatched
|
||||||
|
message: "{{ holder }} scanned at {{ reader }} ({{ scanned }}). Door released for the hold window."
|
||||||
|
else:
|
||||||
|
- action: persistent_notification.create
|
||||||
|
data:
|
||||||
|
title: Doorman — badge accepted, unlock NOT armed
|
||||||
|
message: "{{ holder }} scanned at {{ reader }} ({{ scanned }}). Unlock is disarmed — door stayed shut."
|
||||||
# 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 }}"
|
||||||
|
|||||||
Reference in New Issue
Block a user