Add Osborne-Hoffman (OH) protocol support to SIA integration#164635
Add Osborne-Hoffman (OH) protocol support to SIA integration#164635wichers wants to merge 15 commits intohome-assistant:devfrom
Conversation
Extend the existing SIA integration to accept OH alarm panels alongside standard SIA DC-09 panels via a new "OH" protocol option. Changes: - Add OH protocol option to config flow (TCP/UDP/OH selector) - Add panel_id and forward_heartbeat fields for OH accounts - Add OH account validation using osbornehoffman.OHAccount - Add OHClient lifecycle management in SIAHub (start/stop) - Add oh_adapter.py converting OHEvent to pysiaalarm SIAEvent - Add async_start() method to SIAHub for clean client startup - Add osbornehoffman==2.0.0 to manifest requirements - Update strings.json with OH field labels and error messages The OH adapter converts OHEvent objects from the osbornehoffman library into pysiaalarm SIAEvent instances so existing entity layers (alarm_control_panel, binary_sensor) work unchanged.
- Update osbornehoffman requirement from 2.0.0 to 2.1.1 - Add reuse_port=True to OH client start for quick restart resilience - Add keystore_path for V4 AES key persistence across HA restarts - Hide ignore_timestamps option for OH accounts (only relevant to SIA)
- CG (Group Close): ARMED_AWAY → ARMED_HOME (stay arm, perimeter only) - CF (Forced Close): ARMED_AWAY → ARMED_CUSTOM_BYPASS (armed with bypassed zones) - Add FR (Fire Restore) to smoke sensor as alternate fire restore code
- Rename _oh_client to _oh_receiver throughout - OHClient → OHReceiver to match library rename - Move osbornehoffman imports from lazy (inside method) to top-level
- Add test data for OH config entries (panel_id, forward_heartbeat) - Test OH config creation with panel_id hex → int conversion - Test OH additional account creation - Test OH-specific validation errors (panel_id format/length) - Test OH unknown exception handling - Test OH options flow excludes ignore_timestamps field
CG (Group Close) and CF (Forced Close) back to ARMED_AWAY to preserve backward compatibility with existing automations. FR smoke restore kept.
Move osbornehoffman imports to top-level in config_flow.py (PLC0415), fix import sorting in oh_adapter.py (I001), and suppress SLF001 for necessary private member access on SIAEvent in oh_adapter.py.
|
Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍 |
|
Hey there @eavanvalkenburg, mind taking a look at this pull request as it has been labeled with an integration ( Code owner commandsCode owners of
|
There was a problem hiding this comment.
Pull request overview
Adds Osborne‑Hoffman (OH) as an additional transport/protocol option for the existing sia integration so OH panels can reuse the existing SIA entities/events by adapting OH events into pysiaalarm’s SIAEvent shape.
Changes:
- Introduce an OH→SIA event adapter and wire an
OHReceiverlifecycle into the hub. - Extend config flow + strings to support OH-specific fields (
panel_id,forward_heartbeat) and validation. - Add OH config flow tests and update dependencies/manifest to include
osbornehoffman==2.2.1.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
homeassistant/components/sia/oh_adapter.py |
Adds OH→SIAEvent conversion so downstream entity logic stays unchanged |
homeassistant/components/sia/hub.py |
Adds OH receiver account management, start/stop, and event forwarding into HA dispatcher/bus |
homeassistant/components/sia/config_flow.py |
Adds OH protocol option, OH input validation, and protocol-aware account data storage |
homeassistant/components/sia/const.py |
Adds OH protocol + OH-specific config constants |
homeassistant/components/sia/binary_sensor.py |
Extends smoke mapping with FR restore code |
homeassistant/components/sia/__init__.py |
Uses hub-level async_start() abstraction to start the correct underlying server |
homeassistant/components/sia/manifest.json |
Adds osbornehoffman requirement + logger |
homeassistant/components/sia/strings.json |
Adds config flow labels and OH-specific validation error strings |
requirements_all.txt |
Adds osbornehoffman==2.2.1 to global requirements |
requirements_test_all.txt |
Adds osbornehoffman==2.2.1 to test requirements |
tests/components/sia/test_config_flow.py |
Adds OH config flow and options flow test coverage |
Patch homeassistant.components.sia.hub.OHReceiver and .hub.OHAccount instead of osbornehoffman.OHReceiver/OHAccount, and similarly for config_flow.OHAccount.validate_account. Since these are imported at module level, patching the source library has no effect on the already- imported references.
Previously _load_options only set CONF_IGNORE_TIMESTAMPS on accounts found in entry.options, leaving the key missing for accounts with partial/missing options. _update_sia_accounts then unconditionally indexed a[CONF_IGNORE_TIMESTAMPS], causing a KeyError during setup. Now always set both CONF_IGNORE_TIMESTAMPS (default False) and CONF_ZONES on every account regardless of options presence.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
homeassistant/components/sia/config_flow.py:64
HUB_SCHEMA(andACCOUNT_SCHEMA) always includes both SIA-specific (encryption_key) and OH-specific (panel_id,forward_heartbeat) fields, so users configuring TCP/UDP will still be shown OH-only inputs (and vice versa). Consider splitting the flow into an initial protocol-selection step and then presenting a protocol-specific schema, or dynamically generating the schema based on the selected protocol so only relevant fields are displayed/validated.
HUB_SCHEMA = vol.Schema(
{
vol.Required(CONF_PORT): int,
vol.Optional(CONF_PROTOCOL, default="TCP"): vol.In(["TCP", "UDP", "OH"]),
vol.Required(CONF_ACCOUNT): str,
vol.Optional(CONF_ENCRYPTION_KEY): str,
vol.Optional(CONF_PANEL_ID): str,
vol.Optional(CONF_FORWARD_HEARTBEAT, default=True): bool,
vol.Required(CONF_PING_INTERVAL, default=1): int,
vol.Required(CONF_ZONES, default=1): int,
vol.Optional(CONF_ADDITIONAL_ACCOUNTS, default=False): bool,
}
- Split config flow into protocol-selection step (port + protocol) and protocol-specific account step (SIA fields or OH fields), so users only see relevant fields for their protocol - Type _oh_receiver as OHReceiver | None instead of Any for proper type checking - Update update_accounts docstring to reflect dual-protocol support - Add pysiaalarm compatibility comments to oh_adapter.py documenting the internal invariants relied upon - Add test_oh_adapter.py with 20 unit tests covering OHEvent → SIAEvent conversion, message type mapping, timestamp parsing, field mapping, CRC bypass, and get_event_data_from_sia_event output - Update test_config_flow.py for the new 2-step flow with protocol-specific schema assertions - Update strings.json with new account step translations
The function returns the original string when ISO parsing fails, but the docstring said "datetime or None". Updated to reflect that unparseable strings are passed through as-is.
joostlek
left a comment
There was a problem hiding this comment.
Could you elaborate on what kind of panels this use and what this means for SIA? As in, wouldn't it make more sense to have this being used by the SIA library underwater?
|
Thank you for the follow-up. There's quite a range of panels using this:
The Osborne-Hoffman (OH+CID, OH+SIA or OH+XSIA) 'layer' sound like a layer, I wish it was, but the underlying CID/SIA implementation is completely different and not in SIA/CID spec as cleanly implemented by pysiaalarm. Thank you Honeywell ;-) It would have been so nice to integrate this in to pysiaalarm, but I believe (also discussed with @eavanvalkenburg some years ago) that this is not beneficial to the pysiaalarm implementation. For your interest here is the current implementation: |
Making pysiaalarm dependent on the SIA library could indeed be a an option, what's your preference? It's indeed ugly being dependent on the pysiaalarm event class. It does make further integration (adding OH video support for example) cleaner to keep them separated. Pros and cons.. |
Proposed change
Add support for the Osborne-Hoffman (OH) alarm protocol to the existing SIA integration, allowing OH-based alarm panels to report events alongside standard SIA DC-09 (TCP/UDP) panels.
OH is a proprietary protocol used by certain alarm panels that communicates SIA and Contact ID event codes over its own transport layer. By adding OH as a third protocol option, users with OH panels can reuse the existing SIA integration's alarm control panel, binary sensors, and event infrastructure without needing a separate custom component.
Key design decisions
pysiaalarm. OH uses the newosbornehoffmanlibrary. The entity layer is fully protocol-agnostic.oh_adapter.pyconvertsOHEvent→SIAEventso all downstream entity logic (alarm_control_panel, binary_sensor) works unchanged for both protocols.OHAccount.validate_account()(panel ID + account) and SIA accounts viaSIAAccount.validate_account()(account + encryption key). OH-specific fields (panel_id,forward_heartbeat) are stored per-account; SIA-specific fields (encryption_key) remain separate.Type of change
Additional information
osbornehoffman==2.2.1(PyPI)FR(Fire Restore) to the smoke binary sensor code mappings — an alternate restore code used by some panels that was previously missingChecklist
ruff format).ruff check).requirements_all.txtandrequirements_test_all.txthave been updated.strings.jsonhas been updated with new config flow fields.Files changed (11 files, +498 / -41)
homeassistant/components/sia/oh_adapter.pyhomeassistant/components/sia/hub.pyOHReceiverlifecycle alongsideSIAClienthomeassistant/components/sia/config_flow.pyhomeassistant/components/sia/const.pyPROTOCOL_OH,CONF_PANEL_ID,CONF_FORWARD_HEARTBEATconstantshomeassistant/components/sia/binary_sensor.pyFR(Fire Restore) codehomeassistant/components/sia/__init__.pyhomeassistant/components/sia/manifest.jsonosbornehoffman==2.2.1dependencyhomeassistant/components/sia/strings.jsonpanel_id,forward_heartbeat)requirements_all.txtosbornehoffman==2.2.1requirements_test_all.txtosbornehoffman==2.2.1tests/components/sia/test_config_flow.py