DRV-27: Add Alarm Control Panel entity support#43
Conversation
v20250709: Added support for encrypted connections using the device encryption key
…ith drivercentral (#4)
…de and update dependencies (#7)
Also make a slight change to authentication flow to still authenticate if there is no password.
- Add fatal error mechanism for async authentication failures - Improve driver status messages during password authentication - Remove deprecated _authenticated flag and authRequired parameter Fixes #12
…e tracking (#15) * Replace bit and protobuf libraries with vendored version and move all vendored libraries outside the src folder * Refactor and standardize callback handling with auto-registration, timeouts, and hierarchical lookup * Add device log forwarding feature to ESPHome driver * Upgrade bitn and protobuf libraries to v0.2.0 * Add Bluetooth proxy support with multi-proxy coordination and presence tracking Bluetooth Proxy Infrastructure: - ESPHome driver detects bluetooth_proxy capability and exposes BLE device selection - Scanner with pluggable nodes for local (ESPHome direct) and coordinator modes - Advertisement parsing for BTHome, SwitchBot, and Govee protocols - GATT connection management with auto-connect and slot tracking - Scanner watchdog with automatic device restart on stuck scanner detection Bluetooth Coordinator Driver: - Aggregates multiple ESPHome Bluetooth proxies via single binding - RSSI-based intelligent routing to optimal proxy per device - Connection failover with automatic retry through alternate proxies - Device registry with RSSI freshness tracking - Dynamic bindings for discovered BLE devices Room Presence Tracking: - ESPresense-style room detection using RSSI signal strength - Anti-flapping: RSSI smoothing (EMA), hysteresis margin, dwell time - Per-device and per-room events (entered/left room, home/away) - Contact sensor bindings for room occupancy and device presence - Minimum RSSI threshold for sparse coverage scenarios (global + per-proxy override) - Variables: Room, Distance, RSSI for each tracked device Sub-drivers: - ESPHome BTHome: Shelly BLU, BTHome v1/v2 sensors (passive) - ESPHome Govee: Temperature/humidity sensors, meat thermometers (passive) - ESPHome SwitchBot: Bot, Plug Mini, Meter, Motion, Contact (active + passive) Library improvements: - Dynamic bindings with namespace isolation and persistence - Event management with dynamic creation and cleanup - Values module for variables and properties with persistence - AES-CTR encryption for SwitchBot device communication
Add ESPHome fan entity integration through the Control4 fan proxy with 12 driver variants (6 speed counts x 2 direction modes). Includes preprocessor enhancements for variant-level #ifdef conditions, cross-product dimension definitions, and consolidated PDF generation.
New Yale/August BLE lock driver with secure GATT handshake, lock/unlock control, DoorSense contact sensor, battery monitoring, and Yale Cloud API integration for offline key provisioning. Supports persistent and poll connection modes. Bluetooth proxy: V3 BLE CCCD descriptor write support, consistent BLE address derivation via BLEAddress.fromString, improved GATT logging. Replaced custom Lua AES-CTR implementation with native C4 crypto. Documentation: Yale compatibility table with supported lock models, DoorSense calibration note, key rotation troubleshooting, encryption tip for BLE-only proxies to reduce controller CPU load, BTHome and coordinator style corrections. Normalize action command names to underscore format across all drivers.
- Add run_test.sh as general-purpose test runner that sets up LUA_PATH, loads C4 shim, and passes env vars to test files - Add runEventLoop() to c4_shim so tests don't duplicate the event loop - Remove hardcoded passwords and encryption keys from examples - Normalize test files: consistent env var usage, 2-space indent, explicit dependency requires
JSON serialization of 48-bit BLE addresses via SerializeSafe loses precision in scientific notation (e.g., 266846339488132 becomes 266846339488130, off by 2). This caused the coordinator to send wrong addresses to ESP proxies, resulting in 20-second connection timeouts. Refactored so numeric addresses only exist inside client.lua at the protobuf boundary. All layers above (coordinator, router, bluetooth proxy capability) now pass MAC strings exclusively. The client converts MAC to number internally right before protobuf encoding. Also switches gen-squishy from system Lua to LuaJIT to match the Control4 runtime, and adds a generated-file header to README.md.
* Fix listEntities() never resolving due to callback registration order ListEntitiesDoneResponse has no `ifdef` option in its proto schema. The entity type check was evaluated before the DoneResponse check, causing it to be skipped as "Unknown entity type". The Done callback was never registered, so the deferred never resolved and entity discovery silently failed. Sub-drivers (lights, fans, locks) stayed Disconnected. Fix: check for ListEntitiesDoneResponse first, before the entity type lookup. * Fix sub-drivers staying Disconnected after parent connects ListEntitiesDoneResponse has no `ifdef` option in its proto schema. The entity type check rejected it as unknown before reaching the Done callback registration, so the deferred never resolved and entity discovery silently failed. Fix: check for ListEntitiesDoneResponse first, before the entity type lookup. Also log unknown entity types at trace level instead of silently skipping them.
Apply control4-driver-template to manage shared infrastructure via Copier. Adds copier answers, Makefile, CONTRIBUTING, and syncs shared libs. Co-authored-by: Derek Miller <derek@finitelabs.com>
* AGENT-30: Deduplicate DoorSense contact sensor notifications
Track last known door status and only send proxy notification when
the state actually changes. Previously, updateDoorStatus() fired
SendToProxy on every poll cycle regardless of state change, causing
Control4 to log repeated contact sensor events every ~1 minute.
Reset lastDoorStatus to nil on disconnect/reconnect and driver reset
so the first poll after reconnect always reports the current state.
* refactor: persist door status via values lib instead of local variable
Replace the in-memory lastDoorStatus local with values:update('Door Status'),
which persists state across controller reboots and driver updates. The values
lib's update() method returns false when the value hasn't changed, providing
the same deduplication behavior while also surviving isolate restarts.
- Remove lastDoorStatus local variable and all reset sites
- Use values:update() return value for change detection
- Property updates now handled by values lib (no manual UpdateProperty)
- values:reset() in EC.Reset_Driver already clears persisted state
- values:restoreValues() on init restores last known door state
---------
Co-authored-by: OpenClaw <openclaw@dmiller.me>
Added missing Unreleased section to CHANGELOG.md documenting the DoorSense contact sensor deduplication fix. Rebuilt to embed updated changelog in README. Refs: AGENT-30 Co-authored-by: OpenClaw <openclaw@dmiller.me>
Co-authored-by: OpenClaw <openclaw@dmiller.me>
* TPL-2: copier update to v0.4.0 - add GitHub Actions CI * fix: regenerate README.md to match pandoc output The committed README.md had a trailing backslash line break that pandoc converts to two trailing spaces, causing the dirty-tree CI check to fail. * fix: remove trailing whitespace from README.md * fix: remove trailing backslash line break in source doc Applies the same fix from README.md to the source documentation. Addresses review comment on PR #26. * fix: join reflowed lines in source doc and README to match pandoc output The previous commit removed trailing spaces (markdown line breaks) but left the text split across lines. Pandoc reflows this into a single paragraph, causing the dirty-tree CI check to fail. Join the lines to match pandoc's expected output. --------- Co-authored-by: OpenClaw <openclaw@dmiller.me> Co-authored-by: openclaw[bot] <openclaw[bot]@users.noreply.github.com>
…28) Co-authored-by: OpenClaw <openclaw@dmiller.me>
Use values:update() to track cover open/closed contact state and only send proxy notifications when the state actually changes. Prevents duplicate history agent entries during open/close operations when ESPHome sends multiple position updates.
Automatically creates a GitHub release when a version tag is pushed. Waits for the existing build workflow to complete, downloads the oss artifacts, and publishes with auto-generated release notes.
Co-authored-by: OpenClaw <openclaw@dmiller.me>
…ks (#33) - Remove module-level isLeaderInstance variable - Only check gInitialized in OPC guards (not isLeaderInstance) - Scope isLeaderInstance locally inside the heartbeat timer - Add changelog entry wrapped in ifndef DRIVERCENTRAL guards Co-authored-by: svc-finitelabs[bot] <269744575+svc-finitelabs[bot]@users.noreply.github.com>
|
Hmm, the approach will need to be looked at again. We will need to use the companion driver approach and integrate the panel using the security panel/partition proxy. |
* DRV-25: Add Select entity support - Create src/esphome/entities/select.lua (~130 lines) - STRING variable with current option, writable via variable writes - selectRegistry pattern for programming commands (matches button.lua) - Add 'Set Select' command with dynamic Select and Option dropdowns - Register SelectEntity in driver.lua entity table - Add Set Select command definition to driver.xml - Update README: entity support table, variables table, commands table - Add CHANGELOG entry * fix: update source documentation with Select entity support The README was updated manually but the source documentation (drivers/esphome/www/documentation/index.md) still had Select marked as unsupported. The build regenerates README from source via pandoc, causing 'Uncommitted changes after build' CI failure. Updates source documentation to match: - Mark Select entity as supported (✅) - Add Select variable row to Variables table - Add Set Select command to Commands table - Update notes to mention Select entities * chore: fix README.md formatting for dirty tree check * fix: use nil check instead of IsEmpty for option value An empty string is a valid select option. Using IsEmpty would reject it, preventing the command from executing. Check for nil instead to only guard against missing parameters. --------- Co-authored-by: svc-finitelabs[bot] <269744575+svc-finitelabs[bot]@users.noreply.github.com> Co-authored-by: OpenClaw <openclaw@dmiller.me> Co-authored-by: svc-finitelabs[bot] <svc-finitelabs[bot]@users.noreply.github.com>
* DRV-31: Add Event entity support Implement ESPHome event entity handling for stateless triggers (button presses, gestures, doorbell rings). Events create Control4 events for programming and track the last event type in a variable. Changes: - Add src/esphome/entities/event.lua with discovered/updated handlers - Register EventEntity in driver.lua entity table - Fix client.lua subscribeStates to handle EventResponse (does not match *StateResponse pattern) - Update README entity table, variables, and events documentation - Add CHANGELOG entry * fix: move event docs to source index.md so build doesn't produce uncommitted changes README.md is generated from drivers/esphome/www/documentation/index.md during the build. The event entity docs were added to README.md directly but not the source doc, causing the build to overwrite them and fail the uncommitted-changes check. Also wraps long CHANGELOG.md line to satisfy prettier formatting. * fix: align blockquote line wrap with pandoc output --------- Co-authored-by: OpenClaw <openclaw@dmiller.me> Co-authored-by: svc-finitelabs[bot] <svc-finitelabs[bot]@users.noreply.github.com>
|
Got it — I'll rework this to use a companion sub-driver with security panel/partition proxies instead of the current variables + device actions approach. Similar pattern to how climate (PR #44) uses a thermostatV2 sub-driver. I'll move the ticket back to In Progress and update the PR once the rewrite is ready. |
b0e78da to
185ef80
Compare
Rewrite: Security Panel/Partition Sub-Driver ApproachThis PR has been completely rewritten to use a proper security panel/partition sub-driver approach instead of the original variables + device actions approach. What changedEntity handler (
Sub-driver (
Main driver:
Both |
* DRV-30: Add Date, Time, and Datetime entity support - date.lua: formats as YYYY-MM-DD, writable (parses back to year/month/day) - time.lua: formats as HH:MM:SS, writable (parses back to hour/minute/second) - datetime.lua: converts epoch_seconds to YYYY-MM-DD HH:MM:SS, writable - All three handle missing_state by setting variable to empty string - Registered all three in driver.lua entity table - Updated README, index.md, and CHANGELOG * docs: alphabetically sort entity tables in documentation * fix: re-sort documentation tables to match build output The entity variable reference tables in README.md and the driver documentation were manually ordered, but prettier --prose-wrap always reformats them alphabetically during build. Commit the post-build output so the dirty-tree CI check passes. --------- Co-authored-by: svc-finitelabs[bot] <269744575+svc-finitelabs[bot]@users.noreply.github.com> Co-authored-by: OpenClaw <openclaw@dmiller.me>
|
Can you download https://github.com/snap-one/docs-driverworks-proxyprotocol-security and make sure we are fully implementing the proxy correctly? |
|
Can you recommend any esphome devices with this entity? Or a way to build a mock one? I need something to test this with. |
Add Voice Assistant footnote to entity table
|
You don't need a specific device — ESPHome's Template Alarm Control Panel platform lets you turn any ESP32 into a mock alarm panel using GPIO binary sensors (or even virtual ones from HA). Minimal mock config (ESP32 + zero hardware): # Use template binary sensors so you can toggle zones from HA
binary_sensor:
- platform: homeassistant
id: zone_1
name: "Zone 1 - Front Door"
entity_id: input_boolean.mock_zone_1
- platform: homeassistant
id: zone_2
name: "Zone 2 - Motion"
entity_id: input_boolean.mock_zone_2
alarm_control_panel:
- platform: template
name: "Mock Alarm Panel"
id: acp1
codes:
- "1234"
requires_code_to_arm: true
arming_away_time: 5s
arming_home_time: 2s
pending_time: 5s
trigger_time: 30s
binary_sensors:
- input: zone_1
trigger_mode: delayed
- input: zone_2
bypass_armed_home: true
trigger_mode: delayed_followerThen create If you want real GPIO zones, just wire momentary buttons or reed switches to any two GPIO pins on a spare ESP32 and swap the binary_sensor:
- platform: gpio
id: zone_1
name: "Zone 1"
device_class: door
pin:
number: GPIO4
mode: INPUT_PULLUP
inverted: trueWhat you get to test with: arm_away, arm_home, arm_night, disarm (with code), arming/pending/triggered state transitions, zone bypass, and the chime callback. Covers all the states the driver would need to handle. The full template alarm control panel docs are here: https://esphome.io/components/alarm_control_panel/template/ |
Security Proxy Spec ReviewDownloaded and reviewed the full snap-one security proxy protocol docs. Here's how the current implementation maps: ✅ Implemented correctly
|
| Spec Item | Priority | Notes |
|---|---|---|
| EXECUTE_EMERGENCY command | Medium | Spec supports Fire/Medical/Police/Panic emergency types. ESPHome has no emergency command, but we could map to TRIGGER if supported. |
| KEY_PRESS command | Low | Virtual keypad support. ESPHome panels don't have keypads. |
| EXECUTE_FUNCTION command | Low | Custom function execution. No ESPHome equivalent. |
| ARM_FAILED / DISARM_FAILED notifications | Medium | Spec has these. Currently no failure handling — if ESPHome rejects a command, we don't notify C4. |
| DISPLAY_TEXT / TROUBLE_TEXT variables | Low | Panel display text. ESPHome doesn't expose these. |
| Zone management (HAS_ZONE, ZONE_STATE, etc.) | Low | ESPHome alarm entities don't expose zone data. |
| Partition capabilities (has_fire, has_medical, has_police, has_panic) | Low | Emergency button visibility. Default false is fine unless we add EXECUTE_EMERGENCY. |
| supports_virtual_keypad capability | Low | Should explicitly set to false since ESPHome panels don't support it. |
🎯 Recommendation
The core is solid. The main gaps are around failure notifications (ARM_FAILED/DISARM_FAILED) and emergency support. Since ESPHome's alarm_control_panel doesn't expose failure callbacks, we'd need to handle these with timeouts or ignore them. The zone/keypad/display features don't apply to ESPHome panels.
One thing I'd add: explicitly send supports_virtual_keypad = false as a capability so C4 doesn't show the keypad UI.
…sub-driver Rewrite alarm control panel support from variables+device actions to a proper security panel/partition sub-driver approach, following the pattern of other sub-drivers (climate, fan, light, lock). Entity handler (alarm_control_panel.lua): - Creates ESPHOME_ALARM dynamic bindings for discovered alarm entities - Forwards state updates and commands between main driver and sub-driver Sub-driver (esphome_alarm/): - Security panel proxy (binding 5001) + partition proxy (binding 5002) - Maps ESPHome alarm states to C4 partition states (ARMED, DISARMED_READY, EXIT_DELAY, ENTRY_DELAY, ALARM) - Handles PARTITION_ARM, PARTITION_DISARM, ARM_CANCEL commands - Reports arm_states, code requirements, and partition state changes - Supports Stay, Away, Night, Vacation, and Custom Bypass arm types Main driver registration: - Added AlarmControlPanelEntity to entity table - Added esphome_alarm.c4z to OSS filenames
The name field was set to 'drv27-alarm-rewrite' instead of 'control4-esphome', causing npm install to regenerate it during CI and fail the dirty-tree check.
6435704 to
5ca44c4
Compare
Summary
Adds support for ESPHome
alarm_control_panelentities using Option B (variables + device actions) as recommended in the ticket.What's New
src/esphome/entities/alarm_control_panel.lua{name} State(STRING) tracks the current alarm state with human-readable names (Disarmed, Armed Home, Armed Away, Armed Night, Armed Vacation, Armed Custom Bypass, Pending, Arming, Disarming, Triggered)Paneldropdown (populated from discovered panels)Commanddropdown (Arm Away, Arm Home, Arm Night, Arm Vacation, Arm Custom Bypass, Disarm, Trigger)Codestring parameter for panels that require a codedriver.luaentity tabledriver.xmlPattern
Follows the same patterns as Button (for programming commands with GCPL/EC) and Switch/Text (for state variables via
values:update). No sub-driver or proxy bindings, keeping it simple and adequate for most use cases.Fixes DRV-27