Skip to content

refactor(gateway): migrate Home Assistant adapter to bundled plugin#40709

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-a028df52
Jun 6, 2026
Merged

refactor(gateway): migrate Home Assistant adapter to bundled plugin#40709
teknium1 merged 1 commit into
mainfrom
hermes/hermes-a028df52

Conversation

@teknium1

@teknium1 teknium1 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates the Home Assistant gateway adapter out of gateway/platforms/homeassistant.py into plugins/platforms/homeassistant/ as a bundled plugin, matching the Mattermost (#31748), Discord, and Teams plugin migrations. Every HA consumer now resolves via PluginManager.discover_and_load()platform_registry.get("homeassistant") instead of a hardcoded Platform.HOMEASSISTANT import branch.

Salvage of #32500 by @kshitijk4poor onto current main (original branch was 1211 commits behind). Cherry-pick preserved authorship; cleanly re-verified.

Changes

  • gateway/platforms/homeassistant.pyplugins/platforms/homeassistant/adapter.py (git mv, 76% similarity) + appended register()
  • New plugins/platforms/homeassistant/__init__.py (exposes register) and plugin.yaml (declares HASS_TOKEN required, HASS_URL optional)
  • gateway/run.py: removed HOMEASSISTANT elif from build_adapter(); _send_to_platform falls through to the registry else branch
  • tools/send_message_tool.py: _send_homeassistant_standalone_send() in the plugin, reachable from cron via standalone_sender_fn
  • HASS_TOKEN/HASS_URL env→PlatformConfig seeding stays in gateway/config.py (same as bluebubbles/mattermost/discord)

Validation

Result
Plugin discovery homeassistant resolves as source: plugin, standalone_sender_fn set, required_env=['HASS_TOKEN'], emoji 🏠
Focused suite 64 passed (test_homeassistant.py 45, test_send_message_missing_platforms.py 19, test_ha_integration.py)

Original PR: #32500

Infographic

home-assistant-bundled-plugin

Move gateway/platforms/homeassistant.py into plugins/platforms/homeassistant/
following the same shape as the Mattermost and Discord migrations.

  - Adapter file is renamed via git mv (history is preserved).
  - register() exposes the platform via the plugin system instead of the
    hardcoded Platform.HOMEASSISTANT elif in gateway/run.py::build_adapter().
  - _standalone_send() replaces the legacy _send_homeassistant() helper in
    tools/send_message_tool.py.  Out-of-process cron delivery
    (deliver=homeassistant from a cron process not co-located with the
    gateway) now flows through the registry's standalone_sender_fn path
    instead of the hardcoded elif.
  - _is_connected() probes HASS_TOKEN via hermes_cli.gateway.get_env_value
    so existing connected-platform checks behave identically.

The HASS_TOKEN / HASS_URL env-to-PlatformConfig seeding in
gateway/config.py stays in core — same pattern bluebubbles, mattermost,
and discord migrations followed.  No setup_fn or apply_yaml_config_fn is
registered because Home Assistant has no _setup_homeassistant wizard in
hermes_cli/setup.py and no homeassistant: YAML block in config.yaml today;
setup runs through the existing hermes_cli/tools_config.py toolset wizard.

Test imports were rewritten across tests/gateway/test_homeassistant.py,
tests/integration/test_ha_integration.py, and
tests/tools/test_send_message_missing_platforms.py; the legacy
(token, extra, chat_id, message)-shaped _send_homeassistant call site is
preserved via a small SimpleNamespace shim in
test_send_message_missing_platforms.py (same approach used when
mattermost moved).

  - Focused HA suites (64 tests across the three rewritten files) pass.
  - Broader gateway/cron sweep produces 10 failures identical to main
    baseline (telegram approval/model-picker xdist isolation flakes,
    wecom_callback defusedxml issue, cron script_timeout fixture issue).
    Zero net new failures.
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-a028df52 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9963 on HEAD, 9962 on base (🆕 +1)

🆕 New issues (5):

Rule Count
unresolved-attribute 4
unresolved-import 1
First entries
plugins/platforms/homeassistant/adapter.py:70: [unresolved-attribute] unresolved-attribute: Attribute `ClientWebSocketResponse` is not defined on `None` in union `Unknown | None`
plugins/platforms/homeassistant/adapter.py:259: [unresolved-attribute] unresolved-attribute: Attribute `WSMsgType` is not defined on `None` in union `Unknown | None`
plugins/platforms/homeassistant/adapter.py:506: [unresolved-attribute] unresolved-attribute: Attribute `ClientSession` is not defined on `None` in union `Unknown | None`
plugins/platforms/homeassistant/adapter.py:507: [unresolved-attribute] unresolved-attribute: Attribute `ClientTimeout` is not defined on `None` in union `Unknown | None`
plugins/platforms/homeassistant/adapter.py:25: [unresolved-import] unresolved-import: Cannot resolve imported module `aiohttp`

✅ Fixed issues (5):

Rule Count
unresolved-attribute 4
unresolved-import 1
First entries
gateway/platforms/homeassistant.py:70: [unresolved-attribute] unresolved-attribute: Attribute `ClientWebSocketResponse` is not defined on `None` in union `Unknown | None`
gateway/platforms/homeassistant.py:422: [unresolved-attribute] unresolved-attribute: Attribute `ClientSession` is not defined on `None` in union `Unknown | None`
gateway/platforms/homeassistant.py:427: [unresolved-attribute] unresolved-attribute: Attribute `ClientTimeout` is not defined on `None` in union `Unknown | None`
gateway/platforms/homeassistant.py:25: [unresolved-import] unresolved-import: Cannot resolve imported module `aiohttp`
gateway/platforms/homeassistant.py:259: [unresolved-attribute] unresolved-attribute: Attribute `WSMsgType` is not defined on `None` in union `Unknown | None`

Unchanged: 5162 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1 teknium1 merged commit c37c6ea into main Jun 6, 2026
23 checks passed
@teknium1 teknium1 deleted the hermes/hermes-a028df52 branch June 6, 2026 18:46
@alt-glitch alt-glitch added type/refactor Code restructuring, no behavior change P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins labels Jun 6, 2026
teknium1 added a commit that referenced this pull request Jun 7, 2026
Home Assistant is a bundled plugin now (#40709) and declares
allow_update_command=True on its PlatformEntry. The registry fallback
in _handle_update_command already covers it, so the frozenset entry is
a redundant double-allow — same cleanup #40711 did for Discord and
Mattermost. Adds a registry-fallback test mirroring the existing
discord/mattermost cases.
teknium1 added a commit that referenced this pull request Jun 7, 2026
Move gateway/platforms/slack.py into plugins/platforms/slack/ following the
Discord (#24356) and Home Assistant (#40709) migrations. Advances #41112 /
hardcoded Platform.SLACK touchpoints in core.

  - Adapter file renamed via git mv (history preserved).
  - register() exposes the platform via ctx.register_platform() instead of the
    Platform.SLACK elif in gateway/run.py::_create_adapter().
  - _standalone_send() replaces the legacy _send_slack() helper in
    tools/send_message_tool.py; out-of-process cron delivery (deliver=slack)
    now flows through the registry's standalone_sender_fn. mrkdwn formatting
    moved into the plugin (was applied in _send_to_platform before chunking).
  - _apply_yaml_config() owns the config.yaml slack: -> SLACK_* env bridge
    (require_mention, strict_mention, allow_bots, free_response_channels,
    reactions, allowed_channels), replacing the hardcoded block in
    gateway/config.py.
  - interactive_setup() replaces hermes_cli/setup.py::_setup_slack +
    _write_slack_manifest_and_instruct and the static _PLATFORMS["slack"] dict
    in hermes_cli/gateway.py; setup metadata is discovered dynamically.
  - is_connected() probes SLACK_BOT_TOKEN via hermes_cli.gateway.get_env_value.
  - max_message_length=39000 on the PlatformEntry; the registry fallback in
    send_message_tool covers it (dropped the _MAX_LENGTHS entry).

The SLACK_BOT_TOKEN/SLACK_HOME_CHANNEL env->PlatformConfig seeding and the
_is_user_authorized allowlist maps stay in core (same as Discord/HA/Mattermost).

Bug fixed during migration: the registry-driven plugin-enable pass in
_apply_env_overrides re-enabled any plugin platform whose is_connected()
passed, ignoring an explicit enabled: false. Slack is the first plugin with an
enabled-false-wins test, so it exposed this latent bug (Discord had no such
test). Added an explicit-disable guard (_enabled_explicit + enabled=False ->
skip) and changed the slack env-block to read the flag instead of popping it so
the guard can see it; the flag is still cleared in the final per-platform
cleanup. Restores test_explicit_{top_level,platforms}_slack_enabled_false_wins.

Test imports rewritten across 11 files (gateway.platforms.slack ->
plugins.platforms.slack.adapter). The _setup_slack home-channel tests moved to
tests/gateway/test_slack_plugin_setup.py exercising interactive_setup. The
test_send_message_tool slack-formatting tests now patch the registry
standalone_sender_fn (via _patch_slack_standalone_sender) and assert the
mrkdwn-formatted text reaches the wire.

Validation: 706 targeted tests pass (slack/config/setup/registry/send/media
suites); 18/18 live E2E checks pass (real plugin discovery + registry resolves
SlackAdapter, env-only enable, standalone sender wired, YAML bridge, dynamic
setup discovery).
teknium1 added a commit that referenced this pull request Jun 7, 2026
Move gateway/platforms/slack.py into plugins/platforms/slack/ following the
Discord (#24356) and Home Assistant (#40709) migrations. Advances #41112 /
hardcoded Platform.SLACK touchpoints in core.

  - Adapter file renamed via git mv (history preserved).
  - register() exposes the platform via ctx.register_platform() instead of the
    Platform.SLACK elif in gateway/run.py::_create_adapter().
  - _standalone_send() replaces the legacy _send_slack() helper in
    tools/send_message_tool.py; out-of-process cron delivery (deliver=slack)
    now flows through the registry's standalone_sender_fn. mrkdwn formatting
    moved into the plugin (was applied in _send_to_platform before chunking).
  - _apply_yaml_config() owns the config.yaml slack: -> SLACK_* env bridge
    (require_mention, strict_mention, allow_bots, free_response_channels,
    reactions, allowed_channels), replacing the hardcoded block in
    gateway/config.py.
  - interactive_setup() replaces hermes_cli/setup.py::_setup_slack +
    _write_slack_manifest_and_instruct and the static _PLATFORMS["slack"] dict
    in hermes_cli/gateway.py; setup metadata is discovered dynamically.
  - is_connected() probes SLACK_BOT_TOKEN via hermes_cli.gateway.get_env_value.
  - max_message_length=39000 on the PlatformEntry; the registry fallback in
    send_message_tool covers it (dropped the _MAX_LENGTHS entry).

The SLACK_BOT_TOKEN/SLACK_HOME_CHANNEL env->PlatformConfig seeding and the
_is_user_authorized allowlist maps stay in core (same as Discord/HA/Mattermost).

Bug fixed during migration: the registry-driven plugin-enable pass in
_apply_env_overrides re-enabled any plugin platform whose is_connected()
passed, ignoring an explicit enabled: false. Slack is the first plugin with an
enabled-false-wins test, so it exposed this latent bug (Discord had no such
test). Added an explicit-disable guard (_enabled_explicit + enabled=False ->
skip) and changed the slack env-block to read the flag instead of popping it so
the guard can see it; the flag is still cleared in the final per-platform
cleanup. Restores test_explicit_{top_level,platforms}_slack_enabled_false_wins.

Test imports rewritten across 11 files (gateway.platforms.slack ->
plugins.platforms.slack.adapter). The _setup_slack home-channel tests moved to
tests/gateway/test_slack_plugin_setup.py exercising interactive_setup. The
test_send_message_tool slack-formatting tests now patch the registry
standalone_sender_fn (via _patch_slack_standalone_sender) and assert the
mrkdwn-formatted text reaches the wire.

Validation: 706 targeted tests pass (slack/config/setup/registry/send/media
suites); 18/18 live E2E checks pass (real plugin discovery + registry resolves
SlackAdapter, env-only enable, standalone sender wired, YAML bridge, dynamic
setup discovery).
changman pushed a commit to changman/hermes-agent that referenced this pull request Jun 10, 2026
…ch#40736)

Home Assistant is a bundled plugin now (NousResearch#40709) and declares
allow_update_command=True on its PlatformEntry. The registry fallback
in _handle_update_command already covers it, so the frozenset entry is
a redundant double-allow — same cleanup NousResearch#40711 did for Discord and
Mattermost. Adds a registry-fallback test mirroring the existing
discord/mattermost cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants