OpenLatch System Diagrams
Door State Machine
stateDiagram-v2
[*] --> Locked : Power on / Boot
Locked --> Validating : NFC card scanned
Locked --> ShutdownCountdown : Shutdown button pressed (inside)
Locked --> Locked : Shutdown countdown complete
Validating --> CheckingSchedule : UID found in allow-list
Validating --> Denied : UID not found
Validating --> WaitingApproval : CONDITIONAL key + network available
Validating --> Denied : CONDITIONAL key + no network
CheckingSchedule --> Unlocking : UNRESTRICTED / within time slot
CheckingSchedule --> Unlocking : EMERGENCY (queue notification)
CheckingSchedule --> GraceUnlock : Within grace period
CheckingSchedule --> LockOnly : Expired but within grace
CheckingSchedule --> Denied : Outside time slot / expired
WaitingApproval --> Unlocking : Organizer approves
WaitingApproval --> Denied : Organizer denies / timeout (60s)
Unlocking --> Unlocked : NUKI confirms unlatch
Unlocking --> Error : NUKI BLE command failed
GraceUnlock --> Unlocked : NUKI confirms unlatch
LockOnly --> Locking : NUKI lock command sent
Unlocked --> AutoRelockWait : Auto-relock timer starts (5s)
Unlocked --> Locking : NFC card scanned (manual lock)
AutoRelockWait --> Locking : Timer expired
AutoRelockWait --> AutoRelockWait : NFC card scanned (reset timer)
Locking --> Locked : NUKI confirms locked
Locking --> Error : NUKI BLE command failed
Denied --> Locked : Red LED + buzzer (2s)
Denied --> EscalationWait : 3rd denied scan within 60s
EscalationWait --> Locked : Timeout (no code entered)
EscalationWait --> Unlocking : Valid TOTP/HOTP code on keypad
EscalationWait --> Denied : Invalid code (rate limited after 3 fails)
Error --> Locked : Error LED pattern (5s)
ShutdownCountdown --> Locked : Countdown canceled (button again)
ShutdownCountdown --> Locking : Countdown finished (120s)
note right of Validating
Lookup UID in RAM allow-list
Check access_type
end note
note right of WaitingApproval
ESP32 connects to network
POST /api/v1/device/approve
Waits up to 60 seconds
LED blinks blue
end note
note right of EscalationWait
LED blinks yellow
Waiting for guarantor
TOTP code on keypad
end note
note right of GraceUnlock
Grace period active
Queue anomaly notification
if NOTIFY_ON_ANOMALY set
end note
note left of ShutdownCountdown
Space shutdown sequence
Buzzer beeps every 5s
LED flashes yellow
People exit the space
end note
Communication Architecture
flowchart TB
subgraph Outside ["Outside the Space"]
NFC[/"CONLAN M1200\nNFC Terminal\n(IP67 outdoor)"/]
CARD((NFC Card\nMIFARE DESFire))
end
subgraph Door ["Door Frame"]
NUKI["NUKI Smart Lock\nUltra\n(battery powered)"]
end
subgraph Inside ["Inside the Space"]
ESP["ESP32\nController"]
BTN[/"Shutdown\nButton"/]
LED[/"LED + Buzzer\nFeedback"/]
end
subgraph Network ["Network"]
WIFI{{"WiFi\nAccess Point"}}
end
subgraph Server ["Backend Server"]
API["FastAPI\nBackend"]
DB[("PostgreSQL\nDatabase")]
ADMIN[/"Admin UI\n(Web)"/]
end
subgraph External ["External Services"]
ICS[/"ICS Calendar\nFeed"/]
NOTIFY[/"Notification\nService"/]
end
CARD -- "DESFire\nchallenge-response\n(contactless)" --> NFC
NFC -- "Wiegand 26/34-bit\n(D0/D1 wires)" --> ESP
ESP -- "BLE\n(lock/unlock/unlatch\ncommands)" --> NUKI
BTN -- "GPIO\n(digital input)" --> ESP
ESP -- "GPIO\n(digital output)" --> LED
ESP -- "HTTPS poll\nevery 5 min\n(ESP32 initiates)" --> WIFI
WIFI -- "TCP/IP" --> API
API -- "SQL" --> DB
ADMIN -- "REST API\n(JWT auth)" --> API
ICS -- "HTTP GET\n(periodic fetch)" --> API
API -- "Push / SMS / Email" --> NOTIFY
style ESP fill:#2d5016,color:#fff
style NUKI fill:#1a3a5c,color:#fff
style NFC fill:#5c3a1a,color:#fff
style API fill:#3a1a5c,color:#fff
style DB fill:#1a5c5c,color:#fff
Data Flow: Allow-List Sync
sequenceDiagram
participant ESP as ESP32
participant WIFI as WiFi AP
participant API as Backend API
participant DB as PostgreSQL
Note over ESP: Every 5 minutes
ESP->>ESP: Disconnect BLE (release radio)
ESP->>WIFI: Connect (STA mode)
WIFI-->>ESP: IP assigned
ESP->>API: GET /api/v1/device/allowlist?current_version=42
Note over ESP,API: Authorization: Bearer <device-token>
API->>DB: Fetch current allow-list version
alt No update needed
API-->>ESP: 304 Not Modified
else New version available
API->>DB: Fetch pre-signed binary payload
API-->>ESP: 200 OK (pre-signed binary payload)
ESP->>ESP: Verify sub-key certificate (master key)
ESP->>ESP: Verify payload signature (sub-key)
ESP->>ESP: Check version > current
ESP->>ESP: Write to LittleFS
ESP->>ESP: Reload allow-list into RAM
end
opt Queued events to report
ESP->>API: POST /api/v1/device/events
Note over ESP,API: Access logs, anomalies, emergency alerts
end
ESP->>WIFI: Disconnect
ESP->>ESP: Re-enable BLE (NUKI)
Data Flow: Denied Card Escalation
sequenceDiagram
participant M as Member
participant T as NFC Terminal + Keypad
participant ESP as ESP32
participant API as Backend API
participant G as Guarantor
M->>T: Scan card (1st)
T->>ESP: Wiegand: card UID
ESP->>ESP: Lookup UID → SCHEDULED, outside time slot
ESP-->>M: Red LED + buzzer (DENIED)
ESP->>ESP: record_denial(uid, count=1)
M->>T: Scan card (2nd, within 60s)
T->>ESP: Wiegand: card UID
ESP-->>M: Red LED + buzzer (DENIED)
ESP->>ESP: record_denial(uid, count=2)
M->>T: Scan card (3rd, within 60s)
T->>ESP: Wiegand: card UID
ESP-->>M: Yellow LED blink (ESCALATION)
ESP->>ESP: record_denial(uid, count=3) → escalated!
ESP->>API: POST /api/v1/device/events [{escalation_triggered, uid}]
Note over API: Event queued (group notification: future)
M->>G: Member calls guarantor by phone
Note over M,G: Member explains situation
G->>G: Open authenticator app → read TOTP code
Note over G,M: Guarantor tells key-ID + code by phone
M->>T: Keypad: 5#287082#
T->>ESP: Wiegand: 4-bit per keypress
ESP->>ESP: Lookup key_id=5 → guarantor entry
ESP->>ESP: Verify TOTP (window T-10 to T+1)
ESP->>ESP: Code valid → GRANTED
ESP-->>M: Green LED + beep
ESP->>ESP: clear_denial(uid)
Data Flow: Conditional Access (Backup Keyholder)
sequenceDiagram
participant USER as Backup Keyholder
participant T as NFC Terminal + Keypad
participant ESP as ESP32
participant API as Backend API
participant G as Guarantor (Phone)
USER->>T: Present NFC card
T->>ESP: Wiegand: card UID
ESP->>ESP: Lookup UID → ACCESS_CONDITIONAL
Note over ESP: LED blinks blue = "awaiting approval"
opt Network available
ESP->>API: POST /api/v1/device/events [{conditional_request, uid}]
Note over API: Event queued (group notification: future)
end
USER->>G: Keyholder calls guarantor by phone
Note over USER,G: Keyholder explains situation
G->>G: Open authenticator app → read TOTP code
Note over G,USER: Guarantor tells key-ID + code by phone
USER->>T: Keypad: 5#287082#
T->>ESP: Wiegand: 4-bit per keypress
ESP->>ESP: Lookup key_id=5 → guarantor entry
ESP->>ESP: Verify TOTP (window T-10 to T+1)
ESP->>ESP: Code valid → GRANTED
ESP->>ESP: NUKI BLE → unlatch
Note over ESP: Green LED + beep
Data Flow: Emergency Access (Landlord)
sequenceDiagram
participant LL as Landlord
participant NFC as NFC Terminal
participant ESP as ESP32
participant NUKI as NUKI Lock
participant API as Backend API
participant ADMINS as Admins (Phone/Email)
LL->>NFC: Present emergency NFC card
NFC->>ESP: Wiegand: card UID
ESP->>ESP: Lookup UID → ACCESS_EMERGENCY
ESP->>NUKI: BLE: unlatch
NUKI-->>ESP: Confirmed
Note over ESP: Green LED + beep (immediate access)
par Notify immediately
ESP->>ESP: Connect to network
ESP->>API: POST /api/v1/device/events [{emergency_access, uid, ts}]
API->>ADMINS: URGENT: Emergency key used by Landlord at 14:32
end
Space Shutdown Sequence
sequenceDiagram
participant P as Person Inside
participant BTN as Shutdown Button
participant ESP as ESP32
participant LED as LED + Buzzer
participant NUKI as NUKI Lock
P->>BTN: Press shutdown button
ESP->>LED: Yellow LED flashing + beep
Note over ESP: 120s countdown starts
loop Every 5 seconds
ESP->>LED: Beep (increasingly urgent)
end
alt Cancel
P->>BTN: Press button again
ESP->>LED: Cancel confirmation (white flash)
Note over ESP: Countdown canceled
else Countdown complete
ESP->>NUKI: BLE: lock
NUKI-->>ESP: Confirmed
ESP->>LED: Solid green + long beep
ESP->>ESP: Log: locked_by_button
end