Skip to content

feat(ohme): take the car plan from Ohme's own schedule when Predbat is not controlling the charger - #5401

Merged
springfall2008 merged 2 commits into
mainfrom
feat/ohme-slots-as-car-plan-5399
Oct 5, 2026
Merged

springfall2008 merged 2 commits into
mainfrom
feat/ohme-slots-as-car-plan-5399

Conversation

@springfall2008

Copy link
Copy Markdown
Owner

Fixes #5399.

The problem

With ohme_automatic: true, ohme_control off and a tariff that is not Octopus Intelligent, Predbat built a car charging plan of its own and modelled it as load. Nothing carried that plan out: the charger kept following Ohme's schedule. The issue has an example of a 78 kWh, £21 car charge in the plan while the charger was drawing 0 W.

What this changes

When Predbat is not the one starting and stopping the charger, the car plan now comes from Ohme.

Setup (with ohme_automatic on) Who schedules the car The car plan in Predbat
Octopus Intelligent tariff, or ohme_automatic_octopus_intelligent: true Octopus, through Ohme Ohme's slots at the Intelligent off-peak rate (unchanged)
ohme_control: true Predbat Predbat's own plan (unchanged)
Neither Ohme New: Ohme's session slots, as car load only, at the normal tariff rate

This is the default for the third row, with no new option. In that row:

  • Ohme's slots go through the same path as Intelligent dispatches, so they become car_charging_slots.
  • Import rates are not changed and no minute is marked as a dispatch.
  • If Ohme has no slots, no car charge is planned.
  • A slot the car is not drawing power in is cancelled by the existing slot confirmation (octopus_intelligent_dynamic).
  • car_charging_battery_size, car_charging_limit and the Ohme battery percentage no longer size the charge, because the model limit is uncapped as it is for Intelligent slots.

How it works

  • A new dispatch source, charger-schedule. The Ohme component adds it to the dispatches it publishes on binary_sensor.predbat_ohme_slot_active when in the new mode. dispatch_billed_off_peak() rejects it. That one test is shared by rate_add_io_slots() and load_octopus_slots(), so both the rate overlay and the slot cost drop the cheap rate.
  • Slots are split where the rate changes. load_octopus_slots() priced a slot not billed off-peak at the import rate at its start. The prediction charges max(0, slot rate - import rate) on the car's energy, so an Ohme slot running from a dearer band into a cheaper one would have picked up a premium that does not exist. split_slot_by_rate() splits charger-schedule slots at each rate change. Other sources are not affected.
  • The mode is re-decided on every poll (120 seconds). It used to be decided once at start-up, which also meant a car moved off Intelligent kept its cheap slot rate until a restart. Now a move on or off an Intelligent tariff switches the wiring on the next poll. ohme_control follows the same change: it stands down and releases the charger when Intelligent takes over, and resumes when it leaves.

Protecting existing Intelligent setups

  • On Intelligent the dispatches are published exactly as before, with no source.
  • If octopus_intelligent_slot already points at another entity (the HA Octopus Energy integration, or the Octopus component's own dispatches), the Ohme component leaves it alone.
  • The new mode does not claim the car slots, so the Octopus component can still wire its own Intelligent device over it. Only the Intelligent wiring claims them, as before.
  • If the Octopus component has no tariff to report (between fetches, or while restarting), the last answer is kept, so a gap in the account data does not drop an Intelligent car's cheap rate.
  • An explicit ohme_automatic_octopus_intelligent: true or false is never overridden by the tariff.

Not covered

Testing

  • test_ohme.py: when the new mode applies, the wiring, the published source, the first run in each mode, and four tariff-change tests (onto Intelligent, off it, a gap in the tariff data, and with ohme_control).
  • test_octopus_slots.py: a charger-schedule slot is split by rate and costed at the tariff, changes no import rate and marks nothing as a dispatch; the same slot with no source is still stamped at the low rate.
  • test_multi_car_iog.py: drives the real fetch_sensor_data_cars() and fetch_sensor_data_car_planning() to check Ohme's schedule becomes the plan, plan_car_charging() is not called, and no schedule means no plan.
  • ./run_all --quick passes.

Docs: new section "Which car charging plan Predbat shows" in docs/car-charging.md, and the Ohme entry in docs/components.md.

🤖 Generated with Claude Code

…s not controlling the charger

With ohme_automatic on, ohme_control off and a tariff that is not Octopus
Intelligent, Predbat built a car charging plan of its own that nothing
carried out, while the charger followed Ohme's schedule (#5399).

Ohme's session slots are now the car plan in that case, through the same
path Intelligent dispatches take, but labelled as a charger schedule so
they earn no cheap rate: import rates are untouched and the car is costed
at the tariff, split wherever the rate changes.

The choice between Intelligent dispatches, a charger schedule and
Predbat-led control is re-made on every poll rather than once at start-up,
so a move on or off an Intelligent tariff is followed without a restart.

Fixes #5399

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 5, 2026 07:54

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.

Copilot review overview

🟡 Changes recommended

Mode transitions can lose wiring or charger-release retries, and schedule pricing can use stale rates.

Review effort: Balanced
Findings: 1 High severity · 2 Medium severity · 1 Low severity

Open (4)
What changed in this PR

Updates Ohme integration so Predbat models Ohme’s schedule when Predbat is not controlling the charger.

Changes:

  • Adds charger-schedule dispatch handling without off-peak pricing.
  • Dynamically switches between Ohme, Predbat, and Intelligent scheduling.
  • Adds tests and documentation for the new behavior.
File Description
docs/​components.md Documents Ohme schedule behavior.
docs/​car-charging.md Explains plan ownership and pricing.
apps/​predbat/​const.py Defines the charger-schedule source.
apps/​predbat/​ohme.py Adds dynamic schedule selection and wiring.
apps/​predbat/​octopus.py Prices charger schedules at tariff rates.
apps/​predbat/​tests/​test_ohme.py Tests Ohme modes and transitions.
apps/​predbat/​tests/​test_octopus_slots.py Tests tariff splitting and rate preservation.
apps/​predbat/​tests/​test_multi_car_iog.py Tests end-to-end car-plan selection.

💡 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 thread apps/predbat/ohme.py Outdated
Comment thread apps/predbat/ohme.py Outdated
Comment thread docs/car-charging.md Outdated
…y a lost wiring or hand-back

Review feedback on #5401:

- load_octopus_slots() runs before the cycle's rates are built, so a charger
  schedule was costed on the previous cycle's rate_import: empty after a
  restart, a day out after midnight. The slots are now marked and priced
  again by reprice_charger_schedule_slots() once the rates are known, in
  place so a cancellation made in between is kept.
- The slot wiring was only applied on the poll that changed the mode, so a
  session fetch or publish failing on that poll lost it for good. It is now
  compared against what was last wired, and made on the next poll.
- A charger hand-back that failed part way was not retried, as control had
  already stood down. It is now retried on every poll until the charger is
  released.
- docs: an explicit ohme_automatic_octopus_intelligent: false on an
  Intelligent tariff is not the first row of the table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@springfall2008
springfall2008 merged commit 4fb634b into main Oct 5, 2026
2 checks passed
@springfall2008
springfall2008 deleted the feat/ohme-slots-as-car-plan-5399 branch October 5, 2026 10:01
chalfontchubby added a commit that referenced this pull request Oct 6, 2026
…its own schedule stays the car plan otherwise

Main's #5401 now takes the car plan from Ohme's own schedule when Predbat is
not controlling the charger, so a default user already gets a plan that matches
what the charger will do. Turning control on by default would override that
schedule - one the user may have set up deliberately - with max charge, which
also overrides the Ohme app's target. ohme_control goes back to a false default;
GE and Zappi keep their automatic defaults. The size and limit guard for an
unset ohme_control goes, as there is no unset default any more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
springfall2008 added a commit that referenced this pull request Oct 8, 2026
…s); mark GH#5399/#5401, GH#5402/#5405, GH#5349/#5360 and GH#4832/#4956 fixed; add the debug-enable-engine-switch symptom row and the TEST_REGISTRY-name trap (#5416)

Co-authored-by: CI <ci@example.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
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.

Ohme: without ohme_control Predbat plans its own car charge that nothing carries out; take the plan from Ohme instead

2 participants