From 66a5c45b7e3f6f8396d013b7ebfa5ed3dd5ba662 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Thu, 3 Sep 2026 07:08:06 -0500 Subject: [PATCH] =?UTF-8?q?feat(doorman):=20unlock=20dispatch=20=E2=80=94?= =?UTF-8?q?=20arm=20switch=20+=20rest=5Fcommand=20to=20doorctl=20[#345]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- packages/doorman.yaml | 56 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/doorman.yaml b/packages/doorman.yaml index 2f5c5ff..b50a9f3 100644 --- a/packages/doorman.yaml +++ b/packages/doorman.yaml @@ -8,10 +8,11 @@ # mechanics, NO submodule (git_pull add-on lacks submodule support), # NO local copy: access-roster remains the single source of truth. # -# UNLOCK IS DELIBERATELY NOT WIRED. Known badges are logged and -# notified; nothing actuates. The HA->Pi relay control path is an open -# design decision (#345 sketch, GLPI #741). Fail-safe: this automation -# can never open the door. +# UNLOCK PATH (founder-approved 2026-09-03): valid badge + the +# doorman_unlock_enabled input_boolean ON -> rest_command fires the +# doorctl endpoint on pfvsvrpi (Tailscale-bound, token+source +# 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 # the pfv-bms master pipeline (webhook fast path + sha-watch safety @@ -72,6 +73,23 @@ input_datetime: has_date: 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: - id: doorman_badge_scan alias: Doorman - badge scan ingest + decision @@ -137,8 +155,8 @@ automation: checked: roster sensors are not populated. Logged only; whitelist NOT evaluated. Check gitea connectivity / KNEL/access-roster. - # Known + enabled badge: log + quiet notification. Unlock not - # wired by design (see header). + # Known + enabled badge: log, then dispatch unlock IF armed + # (founder-level input_boolean). Disarmed = log + notice only. - conditions: "{{ is_known and is_enabled }}" sequence: - action: logbook.log @@ -146,11 +164,27 @@ automation: name: Doorman message: >- Badge {{ badge_id }} ({{ holder }}) accepted at - {{ reader }} — logged, unlock not wired (#741). - - action: notify.persistent_notification - data: - title: Doorman — badge accepted - message: "{{ holder }} scanned at {{ reader }} ({{ scanned }}). Logged. Unlock not wired (#741)." + {{ reader }}. + - if: + - condition: state + entity_id: input_boolean.doorman_unlock_enabled + 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 # badge use is exactly what we want to know about). - conditions: "{{ is_known and not is_enabled }}"