Skip to content

fix(ha): send target as a sibling of service_data in websocket call_service - #4669

Merged
springfall2008 merged 2 commits into
mainfrom
fix/input-boolean-service-target-4662
Aug 23, 2026
Merged

fix(ha): send target as a sibling of service_data in websocket call_service#4669
springfall2008 merged 2 commits into
mainfrom
fix/input-boolean-service-target-4662

Conversation

@springfall2008

Copy link
Copy Markdown
Owner

Summary

  • HA's call_service websocket command expects target (entity_id/device_id/area_id addressing) as a sibling of service_data, not nested inside it. Predbat's call_service_template()/call_service() forwarded any key from a dict-style service definition verbatim into service_data, so a service configured with the common target: entity_id: ... syntax (matching standard HA action/automation syntax, and easy to reach for when wiring up a generic input_boolean Service API bridge) ended up with target nested inside service_data.
  • HA rejects that shape with invalid_format: extra keys not allowed @ data['target'], which silently blocked every charge/discharge/freeze service call while Predbat's internal plan and predbat.status kept reporting normally, since the failure is only logged as a Warn: and never propagated back to plan/status state.
  • Fix: in socketLoop()'s queued-command send, pop target out of service_data and send it as its own top-level field in the outgoing call_service frame.
  • Documented the target: addressing form as supported alongside the existing flat entity_id: form in has_service_api.

Fixes #4662

Test plan

  • New regression test test_hainterface_socketloop_call_service_target_field drives a real socketLoop() iteration with a queued input_boolean.turn_on command carrying a nested target, and asserts the frame sent over the websocket has target as a sibling field, not nested inside service_data. Confirmed RED before the fix, GREEN after.
  • ./run_all --test hainterface_service --test hainterface_websocket --test hainterface_lifecycle --test hainterface_state --test hainterface_api --test inverter — all pass.
  • ./run_all --quick — 155 passed, identical to a clean main checkout (one pre-existing, order-dependent test_web_functions.py crash reproduces identically on unmodified main, unrelated to this change).
  • pre-commit run --all-files (ruff, black, cspell, markdownlint, etc.) — all pass.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 23, 2026 16:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The current implementation mutates the queued service_data dict in-place (affecting cross-thread state/logging), and the new documentation sentence is misleading about how non-target fields are sent.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes Home Assistant WebSocket call_service framing so that target (entity/device/area addressing) is sent as a top-level sibling of service_data, matching HA’s expected schema and unblocking service-based inverter control flows (notably input_boolean bridge automations).

Changes:

  • Update HAInterface.socketLoop() to extract target from queued service_data and send it as the top-level target field in the outgoing call_service frame.
  • Add a regression test asserting the outbound WebSocket frame shape for queued commands containing a nested target.
  • Document the supported target: form in the service API configuration docs.
File summaries
File Description
apps/predbat/ha.py Adjusts WebSocket call_service payload construction to place target at the correct top-level location.
apps/predbat/tests/test_hainterface_websocket.py Adds a regression test to verify correct target framing in queued WebSocket service calls.
docs/inverter-setup.md Documents target: syntax support for service API definitions.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/predbat/ha.py Outdated
Comment thread docs/inverter-setup.md Outdated
springfall2008 added a commit that referenced this pull request Aug 23, 2026
- Pop target from a copy of service_data, not the dict itself - it's
  the same object async_call_service_websocket_command() logs on
  failure, so popping in place was silently dropping target from that
  diagnostic.
- Reword the docs note: only target is elevated to a top-level sibling
  field, other keys (entity_id, device_id, option, etc.) still travel
  inside service_data as before.
- Extend the regression test to assert the original queued
  service_data is left unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
springfall2008 and others added 2 commits August 23, 2026 18:44
…ervice

HA's call_service websocket command expects target (entity_id/device_id/
area_id addressing) as a sibling of service_data, not nested inside it. A
service configured with the common target: entity_id: ... syntax (e.g.
input_boolean.turn_on/turn_off for a generic Service API bridge) landed
with target nested inside service_data, which HA rejects with
invalid_format: extra keys not allowed @ data['target'] - silently
blocking every charge/discharge service call while Predbat's plan and
status kept reporting normally.

Fixes #4662
- Pop target from a copy of service_data, not the dict itself - it's
  the same object async_call_service_websocket_command() logs on
  failure, so popping in place was silently dropping target from that
  diagnostic.
- Reword the docs note: only target is elevated to a top-level sibling
  field, other keys (entity_id, device_id, option, etc.) still travel
  inside service_data as before.
- Extend the regression test to assert the original queued
  service_data is left unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@springfall2008
springfall2008 force-pushed the fix/input-boolean-service-target-4662 branch from 510db38 to 1bf1d9e Compare August 23, 2026 17:44
@springfall2008
springfall2008 merged commit 413d0db into main Aug 23, 2026
2 checks passed
@springfall2008
springfall2008 deleted the fix/input-boolean-service-target-4662 branch August 23, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

input_boolean service calls fail with invalid_format: extra keys not allowed @ data['target'] — charge/discharge start booleans never get set

2 participants