Skip to content

Add Osborne-Hoffman (OH) protocol support to SIA integration#164635

Open
wichers wants to merge 15 commits intohome-assistant:devfrom
wichers:dev
Open

Add Osborne-Hoffman (OH) protocol support to SIA integration#164635
wichers wants to merge 15 commits intohome-assistant:devfrom
wichers:dev

Conversation

@wichers
Copy link
Copy Markdown

@wichers wichers commented Mar 2, 2026

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

  • Clean protocol separation: Standard SIA TCP/UDP continues to use pysiaalarm. OH uses the new osbornehoffman library. The entity layer is fully protocol-agnostic.
  • Adapter pattern: oh_adapter.py converts OHEventSIAEvent so all downstream entity logic (alarm_control_panel, binary_sensor) works unchanged for both protocols.
  • Protocol-aware config flow: Validates OH accounts via OHAccount.validate_account() (panel ID + account) and SIA accounts via SIAAccount.validate_account() (account + encryption key). OH-specific fields (panel_id, forward_heartbeat) are stored per-account; SIA-specific fields (encryption_key) remain separate.
  • No breaking changes: Existing SIA TCP/UDP configurations are fully unaffected. All alarm code mappings remain unchanged.

Type of change

  • [x ] New feature (which adds functionality to an existing integration)

Additional information

  • New dependency: osbornehoffman==2.2.1 (PyPI)
  • Also adds FR (Fire Restore) to the smoke binary sensor code mappings — an alternate restore code used by some panels that was previously missing

Checklist

  • The code change is tested and works locally.
  • Local tests pass. New tests have been added to verify the OH config flow path.
  • There is no commented out code in this PR.
  • I have followed the [development checklist][dev-checklist].
  • I have followed the [perfect PR recommendations][perfect-pr].
  • The code has been formatted using Ruff (ruff format).
  • The code passes Ruff lint checks (ruff check).
  • requirements_all.txt and requirements_test_all.txt have been updated.
  • strings.json has been updated with new config flow fields.

Files changed (11 files, +498 / -41)

File Change
homeassistant/components/sia/oh_adapter.py New — OHEvent → SIAEvent adapter
homeassistant/components/sia/hub.py OH protocol path: OHReceiver lifecycle alongside SIAClient
homeassistant/components/sia/config_flow.py OH validation, OH-specific account fields, top-level osbornehoffman imports
homeassistant/components/sia/const.py PROTOCOL_OH, CONF_PANEL_ID, CONF_FORWARD_HEARTBEAT constants
homeassistant/components/sia/binary_sensor.py Add FR (Fire Restore) code
homeassistant/components/sia/__init__.py Platform setup adjustment
homeassistant/components/sia/manifest.json Add osbornehoffman==2.2.1 dependency
homeassistant/components/sia/strings.json OH config flow labels (panel_id, forward_heartbeat)
requirements_all.txt Add osbornehoffman==2.2.1
requirements_test_all.txt Add osbornehoffman==2.2.1
tests/components/sia/test_config_flow.py OH protocol config flow tests (7 test functions)

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.
Copilot AI review requested due to automatic review settings March 2, 2026 22:29
Copy link
Copy Markdown
Contributor

@home-assistant home-assistant Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @wichers

It seems you haven't yet signed a CLA. Please do so here.

Once you do that we will be able to review and accept this pull request.

Thanks!

@home-assistant
Copy link
Copy Markdown
Contributor

home-assistant Bot commented Mar 2, 2026

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@home-assistant
Copy link
Copy Markdown
Contributor

home-assistant Bot commented Mar 2, 2026

Hey there @eavanvalkenburg, mind taking a look at this pull request as it has been labeled with an integration (sia) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of sia can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign sia Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 OHReceiver lifecycle 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

Comment thread tests/components/sia/test_config_flow.py
Comment thread homeassistant/components/sia/hub.py Outdated
wichers added 4 commits March 2, 2026 23:56
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.
@wichers wichers marked this pull request as ready for review March 2, 2026 23:22
Copilot AI review requested due to automatic review settings March 2, 2026 23:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 (and ACCOUNT_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,
    }

Comment thread homeassistant/components/sia/hub.py
Comment thread homeassistant/components/sia/oh_adapter.py
Comment thread homeassistant/components/sia/oh_adapter.py Outdated
Comment thread homeassistant/components/sia/hub.py Outdated
wichers added 2 commits March 3, 2026 09:38
- 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
Copilot AI review requested due to automatic review settings March 3, 2026 08:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Comment thread homeassistant/components/sia/oh_adapter.py
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.
Copy link
Copy Markdown
Member

@joostlek joostlek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@wichers
Copy link
Copy Markdown
Author

wichers commented Mar 19, 2026

Thank you for the follow-up. There's quite a range of panels using this:

  • CSX75 Panel Range (CS7050(N) TCP/IP gateway)
  • CSX75 Panel Range (CS9104/9204 Video Verification Module)
  • CSX75 Panel Range (CS7002(N) GSM/GPRS module)
  • ATS MASTER Panel range (ATS2xxx, ATS3xxx, ATS4xxx with ATS1806 or ATS1809)
  • ATS Advanced (with TDA74xx GRPS/IP modules or ATS7310 GSM/GPRS module)
  • ATS Advanced IP (ATSx000A-IP, ATSx500A-IP)
  • NetworX Panel Range (NX-590(N)E TCP/IP gateway)
  • NetworX Panel Range (NX-9104/9204 Video Verification Module)
  • NetworX Panel Range (NX-7002(N) GSM/GPRS module)
  • Simon Panel Range (60-938)

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:
https://github.com/wichers/python-osbornehoffman/tree/main/osbornehoffman

@wichers
Copy link
Copy Markdown
Author

wichers commented Mar 19, 2026

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?

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..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants