Repository navigation
fix(iog): take Ohme's Intelligent car slots from Octopus, and close the gaps Ohme was covering - #5685
Conversation
…he gaps Ohme was covering On Octopus Intelligent with an Ohme charger the planned car slots were read from Ohme's session slots, which carry no source - so a boost started from the Ohme app was priced as an off-peak dispatch and the house battery was grid-charged at the full rate while it ran. Octopus lists the same charge as type BOOST, which dispatch_billed_off_peak() excludes. Compared over 4 days on 6 IOG + Ohme instances, Ohme's slots are a relayed copy of Octopus's flexPlannedDispatches, minutes behind and at times stale or missing a slot. What Ohme did cover were three gaps on the Octopus side, which this closes for every charger: - octopus: read the device's currentState, already queried but unused. Planned dispatches are dropped while SMART_CONTROL_NOT_AVAILABLE or SMART_CONTROL_OFF (a plan was seen kept for 12 hours after unplug), and an empty planned list while SMART_CONTROL_IN_PROGRESS - Octopus re-planning a running session - keeps the last known plan for OCTOPUS_PLANNED_EMPTY_HOLD_MINUTES. current_state is published on the dispatch sensor. - octopus: hold the half hour a car charges in during a smart charge with no dispatch listed (the plug-in charge, which Octopus only lists once completed), through the #5520 held mechanism, so it prices the half hour in progress and adds no forward car slot. - ohme: with an Octopus dispatch sensor in octopus_intelligent_slot, leave the car slots, planned and completed, to it and wire only the ready time and charge limit to Ohme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
springfall2008
left a comment
There was a problem hiding this comment.
Automated comment from the triage bot.
Reviewed at head commit 483dba7 — 2 findings, 1 posted inline and 1 in this body. Both minor; the core behaviour checks out (details below).
apps/predbat/ohme.py:702 — automatic_config_octopus_intelligent() still documents the old wiring. Its docstring opens "Automatically set the predbat entities to take the Intelligent car slots from Ohme" and ends "...and Ohme supplies only the planned ones - see octopus_dispatch_sensor()". This PR inverts exactly that (planned and completed slots both come from Octopus's sensor where one is wired, and only the ready time and charge limit are Ohme's), and it updated the neighbouring docstrings and log text (reset() in predbat.py, charger_control_slots_claimed(), the update_slot_mode() log line, octopus_dispatch_sensor() and wire_planned_slots()) — but missed the docstring of the very function that calls wire_planned_slots(). After this PR a reader of that function gets the pre-#5685 behaviour presented as the truth, contradicting both the code directly below it and the new docs table in car-charging.md.
Checks that came back clean, for the record: the new charging-unlisted held source passes dispatch_billed_off_peak() (held=True, end at/behind now) so the plug-in half hour is priced off-peak, while BOOST/bump-charge/charger-schedule stay excluded — Test 24b exercises exactly that split; the held dispatch ends at now so load_octopus_slots() plans no forward car slot from it and "Hold for car" cannot keep it going; dispatch_slots_signature() excludes the drifting start of an in-progress slot, so the re-trimmed kept plan through an empty answer does not request a replan every 2-minute poll; slot confirmation cancelling the half hour wins over the unlisted hold (the strip_from gate precedes it, Test 17); the state-driven drop and the 10-minute empty-hold compose correctly with the failed-query keep from #5492, and the state machine covers CAPABLE/BOOSTING/IN_PROGRESS/NOT_AVAILABLE/empty as documented; devices[].status.currentState really is in intelligent_dispatches_query already, so no API change is being assumed beyond what the PR observed; the new tests restore my_predbat.args and state fields in finally blocks, and car-charging.md covers all three parts including the fallback's limits.
…s the Ohme Simpler than splitting the wiring: when the Ohme is a live Intelligent device, Ohme no longer claims the car slots at all, the same as when Octopus drives the car or another charger. The Octopus component wires its own dispatch sensor, ready time and charge limit, and the Ohme is used for the car's data, power and energy. ohme_control stands down whatever octopus_intelligent_charger_follows_car says, as Octopus is driving this very charger, and a charger Predbat was still holding is handed over to Octopus. This reverts the earlier wire_planned_slots()/clear_planned_slots() change: the #5443 split, Ohme's planned slots beside Octopus's completed ones, is again only for an Intelligent tariff with no device on the account or an explicit ohme_automatic_octopus_intelligent: true. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts own slot sensor octopus_charging_unlisted() read octopus_intelligent_slot at the car's index for every car charging, wired or not. A list too short for the car logged "Out of range index" every cycle and 15 second poll for the length of the charge, and a single entity resolved to car 0's dispatch sensor at any index, so a second car charging while car 0 was in a smart charge got a charging-unlisted half hour held (saved to storage, though never priced, as only a wired car's held half hours are read). It now returns False for a car fetch_sensor_data_cars() recorded no planned dispatches for, which is only ever a wired car. Test 18 covers both shapes: nothing held for car 1 and no warning logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Automated reply from the triage bot. On the review-body finding about The inline finding on |
Problem
On Octopus Intelligent with an Ohme charger, Predbat reads the planned car slots from Ohme's session slots (
octopus_intelligent_planned_slot→binary_sensor.predbat_ohme_slot_active). Those slots carry nosource, so a boost started from the Ohme app is priced as an off-peak dispatch. The plan's import column flips to the cheap rate and the house battery is charged from the grid at the full rate. The customer report showed the battery charging at ~3.4 kW from the grid at 35.99p while the car boosted, from this log line:Octopus itself listed the same charge as
type: BOOSTwith the devicecurrentState: BOOSTING, sodispatch_billed_off_peak()would already have excluded it. The Octopus sensor's planned list was simply not being read for an Ohme car.Is Ohme's schedule better than Octopus's?
We compared both sources over 4 days on 6 random production instances on IOG with an Ohme charger, with Ohme supplying the planned slots:
flexPlannedDispatches: the same half-hours, seconds to minutes later.flexPlannedDispatches: [], a valid empty list rather than an error, for 2–8 minutes, up to 12 times a night. The Octopus Intelligent: a failed planned-dispatch fetch (KT-CT-4340) is read as no dispatches and drops the car plan #5492 hold only covers a null/failed fetch, so the plan disappeared.SMART_CONTROL_NOT_AVAILABLEthroughout. None of it completed.currentStateis already queried but never read.Change
Three parts. Parts 1 and 2 are in the first commit. Part 3 is the second commit, which replaces the first commit's Ohme change with a simpler one:
SMART_CONTROL_NOT_AVAILABLE/SMART_CONTROL_OFF: planned dispatches are dropped.SMART_CONTROL_IN_PROGRESSwith an empty planned list: the last known plan is kept forOCTOPUS_PLANNED_EMPTY_HOLD_MINUTES(10, against the 2-minute poll) from the first empty answer, trimmed as in Octopus Intelligent: a failed planned-dispatch fetch (KT-CT-4340) is read as no dispatches and drops the car plan #5492. In any other state an empty list still clears the plan.current_stateis published on the dispatch sensor. A failed dispatch query keeps the last known state.octopus_held_update(), when the car is charging (car_charging_now), the Octopus dispatch sensor saysSMART_CONTROL_IN_PROGRESS, and no planned dispatch covers the half-hour, that half-hour is held like a running dispatch.car_charging_nowis what 8dc2471 removed, because it kept a Predbat-driven charger going.BOOSTINGnever qualifies. Slot confirmation (octopus_intelligent_dynamic) cancelling the half-hour still wins.current_state, so other slot sensors are unaffected.ohme_automatic_octopus_intelligentunset) now treats a live Ohme Intelligent device exactly like a live car or another charger: Ohme claims nothing. The Octopus component wires its own dispatch sensor, ready time and charge limit, and the Ohme supplies the car's data, power, energy andcar_charging_now.octopus_drives_ohmeflag:ohme_controlstands down whateveroctopus_intelligent_charger_follows_carsays, since Octopus is driving this very charger, and a charger Predbat was still holding is handed over to Octopus rather than released. The shared mixin'scharger_control_octopus_drives_charger()already reaches the same answer from the sensor'sis_charger.ohme_automatic_octopus_intelligent: true.wire_planned_slots()/clear_planned_slots()are unchanged from main.OctopusAPI.automatic_config()clears its wiring when no active device remains.Tests
octopus_intelligent_devices: newtest_planned_dispatches_follow_device_statecovers the not-dispatching states, the empty-list hold (kept at 2 and 8 min, cleared at 12, restarted after a planned list), the state kept through a failed query, and an empty list clearing outside a smart charge. Test 15's empty-list case now runs withSMART_CONTROL_CAPABLE.octopus_dispatch_hold: Tests 14–17 cover the plug-in half-hour being held and cheap with no forward car slot, and nothing held forNone/SMART_CONTROL_CAPABLE/BOOSTING/SMART_CONTROL_NOT_AVAILABLE, a car not charging, or a cancelled half-hour.rate_add_io_slots: Test 24b shows a boost in progress (source: BOOST, AT_HOME) at the day rate while the same slot with no source is off-peak. That is the mechanism of the bug.ohme:run_iog_device_ohmechecks that the slots, ready time and charge limit are left to Octopus, the charger data is still wired, and control stays off with and without follows-car../run_all --quick: all tests pass (4 slow tests skipped)Notes for review
SMART_CONTROL_OFFis included alongsideNOT_AVAILABLEbecause of a past case where a VW returned planned dispatches while smart control was off and they were priced cheap. Drop it if you'd rather keep this to the observed unplug case.🤖 Generated with Claude Code