Skip to content

feat(octopus): Power Up/Power Down migration - join service and entity regex (points 2 & 3 of #4548) - #4601

Open
chalfontchubby wants to merge 3 commits into
mainfrom
feat/octopus-power-down-migration
Open

feat(octopus): Power Up/Power Down migration - join service and entity regex (points 2 & 3 of #4548)#4601
chalfontchubby wants to merge 3 commits into
mainfrom
feat/octopus-power-down-migration

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

Summary

Octopus Energy integration v19.0.0 renamed saving sessions/free electricity events to Power Down/Power Up (see #4548, points 2 & 3). This covers the two mechanical renames:

  • Point 3 - the join service Predbat calls now tries octopus_energy/join_octoplus_power_down_session_event first, falling back to the deprecated join_octoplus_saving_session_event if the current one isn't available (e.g. an older integration version).
  • Point 2 - the octopus_saving_session/octopus_free_session re: entity-matching patterns in apps.yaml (default template, coverage test fixture, and all 23 inverter templates that set these keys) are broadened to match both the deprecated and new (Power Down/Power Up) entity names in one alternation. This is a one-off startup regex resolution (resolve_arg_re()), not a per-cycle call, so no runtime try-new-then-old logic is needed here (unlike point 3's service call).

Both old and new entities/services remain registered by BottleCapDave's integration until January 2027 (ADR-0004), so this is non-breaking for anyone still on the old names.

docs/energy-rates.md updated to mention the rename, the January 2027 cutoff, and that the new entities ship entity_registry_enabled_default: False (so users need to enable them in the HA UI, or Predbat's regex will just keep quietly matching the old entity with no visible sign anything changed).

Filed #4599 as the tracking issue for removing the point-3 fallback once the old service name is retired upstream (not before Jan 2027); the point-3 fallback in octopus.py carries a TODO(#4599) comment.

Test plan

  • ./run_all --quick passes
  • New test test_saving_session_join_service_fallback (point 3): current service succeeds → no fallback; current service fails → falls back to deprecated service
  • New test test_saving_session_entity_regex_power_rename (point 2): resolve_arg_re() matches deprecated entity names, matches new Power Down/Power Up entity names, and does not cross-match Power Up against the saving-session pattern or vice versa
  • ./run_pre_commit passes (ruff, black, cspell, markdownlint, etc.)
  • New/updated Power Down/Power Up entity names and old deprecated names cross-checked against BottleCapDave's published entity docs

🤖 Generated with Claude Code

chalfontchubby and others added 2 commits August 19, 2026 19:42
…ed one

Bottle Cap Dave's Octopus Energy integration renamed
join_octoplus_saving_session_event to
join_octoplus_power_down_session_event (ADR-0004). Both remain
registered until the old one is retired in January 2027, so try
the new name first and fall back to the old one on failure.

Part of #4548 (point 3). TODO(#4599) tracks removing the fallback
once the old service name is gone upstream.
…r Down/Up rename

Octopus Energy integration v19.0.0 renamed the saving-session and
free-electricity event entities to Power Down and Power Up
(event.octopus_energy_ACCOUNT_ID_octoplus_power_down_events /
..._power_up_events). Both old and new entities remain registered
until January 2027 (ADR-0004), and resolve_arg_re()'s regex
matching against HA state keys is a one-off startup resolution, so
broadening the 're:' alternation to match either name is enough -
no runtime try-new-then-old logic is needed here, unlike the join
service rename in point 3.

Updates the octopus_saving_session/octopus_free_session regex in
apps.yaml (default, coverage test fixture, and all 23 inverter
templates that configure them), plus docs/energy-rates.md to
mention the rename, the entity_registry_enabled_default=False
gotcha, and the January 2027 cutoff.

Part of #4548 (point 2).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

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.

Pull request overview

This PR updates Predbat’s Octopus Energy integration handling to support the Octopus v19.0.0 rename of Saving Sessions/Free Electricity sessions to Power Down/Power Up, while maintaining compatibility with the deprecated names through the upstream deprecation window (until Jan 2027).

Changes:

  • Updates the saving-session auto-join service call to try octopus_energy/join_octoplus_power_down_session_event first, with fallback to the deprecated .../join_octoplus_saving_session_event.
  • Broadens apps.yaml-style re: entity match patterns across templates and fixtures to match both legacy and new Power Down/Up event entity names.
  • Updates documentation and adds/extends unit tests covering the service fallback and regex matching.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
templates/teslemetry.yaml Broaden saving-session entity re: pattern to include Power Down.
templates/tesla_powerwall.yaml Broaden saving-session entity re: pattern to include Power Down.
templates/sunsynk.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/solis_cloud.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/solax_sx4.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/solax_cloud.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/solaredge.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/solar_assistant_growatt_sph.yaml Broaden saving-session entity re: pattern to include Power Down.
templates/solar_assistant_growatt_spa.yaml Broaden saving-session entity re: pattern to include Power Down.
templates/sofar.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/sofar_modbus.yaml Broaden saving-session entity re: pattern to include Power Down.
templates/sigenergy_sigenstor.yaml Broaden saving-session entity re: pattern to include Power Down.
templates/sigenergy_cloud.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/luxpower.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/kostal.yaml Update commented free-session entity re: pattern to include Power Up.
templates/givenergy_givtcp.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/givenergy_ems.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/givenergy_cloud.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/ginlong_solis.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/fronius.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/fox.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/fox_cloud.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
templates/enphase_cloud.yaml Broaden saving/free session entity re: patterns to include Power Down/Up.
docs/energy-rates.md Document the Power Down/Up rename, Jan 2027 cutoff, and entity-default-disabled nuance; update example regexes.
coverage/apps.yaml Update fixture commented free-session entity re: pattern to include Power Up.
apps/predbat/config/apps.yaml Update default template regexes to match legacy + Power Down/Up entities.
apps/predbat/octopus.py Implement join-service “try new then fallback to old” logic.
apps/predbat/tests/test_infra.py Add service_store_fail + change stored service-call return behavior for tests.
apps/predbat/tests/test_saving_session.py Update expected join service + add tests for service fallback and entity-regex rename coverage.
apps/predbat/unit_test.py Register the new saving-session tests.

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

Comment thread apps/predbat/octopus.py
Comment on lines +2994 to +3004
# Join via Bottle Cap Dave's Octopus Energy HA integration. Try the current
# service name first (join_octoplus_power_down_session_event, which
# superseded join_octoplus_saving_session_event - see the integration's
# ADR-0004), falling back to the old name for anyone on an integration
# version that predates the rename. Both remain registered until the old
# one is removed in January 2027, so this fallback is a temporary bridge,
# not a permanent branch.
# TODO(#4599): remove this fallback once the old service name is retired upstream.
if not self.call_service_wrapper("octopus_energy/join_octoplus_power_down_session_event", event_code=code, entity_id=entity_id):
self.log("Note: octopus_energy/join_octoplus_power_down_session_event not available, falling back to the deprecated join_octoplus_saving_session_event service")
self.call_service_wrapper("octopus_energy/join_octoplus_saving_session_event", event_code=code, entity_id=entity_id)
Comment on lines 189 to +196
def call_service(self, service, **kwargs):
print("Calling service: {} {}".format(service, kwargs))
if self.service_store_enable:
self.service_store.append([service, kwargs])
return None
# Services in service_store_fail simulate a service that doesn't exist (e.g. testing a
# try-new-service-then-fall-back-to-old caller) - everything else succeeds, matching real
# HA behaviour for a registered service call.
return None if service in self.service_store_fail else True
async_call_service_websocket_command() returned None for a successful
call when return_response wasn't requested - identical to its failure
return value. This made the point-3 join-service fallback in
octopus.py (dabf8a7) fire on every call regardless of whether the new
service actually succeeded, double-calling the deprecated service on
top of a working one. TestHAInterface's call_service() already
returned True on success, masking the bug in tests.

Checked every other call_service/call_service_wrapper call site
(inverter.py, userinterface.py, web.py, predbat.py) - none branch on
the return value's truthiness, so this is safe.

Copilot review finding on PR #4601.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@springfall2008

Copy link
Copy Markdown
Owner

Review: call_service_wrapper None→True change

Ran a multi-angle review focused on the safety of the NoneTrue change in async_call_service_websocket_command (ha.py) and its new use in the Octopus join fallback logic. Verdict: the one-line change is correct in isolation, but the fix is incomplete — the new fallback logic in octopus.py depends on a "truthy = success" contract that only holds on one of call_service()'s two branches.

Blocking

  1. Loopback branch was never fixed (ha.py call_service() + userinterface.py trigger_callback())
    call_service() has two branches: the websocket branch (now correctly True/None) and a loopback branch — used whenever websocket_active is False (standalone/Predbat.com/Docker mode with no linked HA, or transiently during an auth_invalid reconnect / after repeated websocket failures) — which delegates to trigger_callback(). That function has no return statement anywhere, so it always implicitly returns None, success or not.
    Effect: on any install running in loopback mode, octopus.py's new if not self.call_service_wrapper("octopus_energy/join_octoplus_power_down_session_event", ...) is always truthy-negated. Predbat will always log "not available, falling back to the deprecated ... service" and always fire the deprecated join_octoplus_saving_session_event call too, on every single join, indefinitely — not just as an availability fallback.

  2. Even the fixed branch can't distinguish failure reasons
    In async_call_service_websocket_command, every failure mode (unregistered service, timeout, HA-reported success: False) collapses to the same falsy None. A slow-but-successful call that times out on the wait would return None and trigger the fallback path to fire a second, real join request against Octopus's backend for a join that already went through.

  3. Test coverage can't catch either of the above
    TestHAInterface.call_service in test_infra.py is an independent reimplementation of the True/None contract (return None if service in self.service_store_fail else True) — it never delegates to the real ha.py/userinterface.py code. So test_saving_session_join_service_fallback passes regardless of whether the production loopback path is broken.

Non-blocking

  1. web.py's /api/service REST endpoint response body silently changes from null to true on success — a documented public API contract change not mentioned in the PR.
  2. No caching of which service name actually works — every install still on the old integration re-probes the new service and falls back on every attempt (every 2 hours per the existing throttle), forever, instead of detecting once (cf. inverter.py's rest_v3 pattern).
  3. templates/kostal.yaml's octopus_saving_session pattern wasn't updated to include power_down_events like the other 22 templates, and was already diverged pre-PR.
  4. The service_store_fail/service_store_enable reset in the new test isn't wrapped in try/finally; since TestHAInterface is a shared singleton across the whole test run, an exception mid-test could leak that state into unrelated later tests.

Suggested fix for the blocking issue: make trigger_callback() return a real success/failure signal (e.g. True if a matching EVENT_LISTEN_LIST entry ran, False/None otherwise) so call_service()'s truthy contract holds on both branches, not just the websocket one.

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.

3 participants