Skip to content

ESS battery still being planned to charge and charging at peak rate after implementing #5229 #5286

Closed

Description

@chrisgla

Describe the bug

I've implemented #5229 by:
a) Setting switch.predbat_octopus_intelligent_trust_slots to off
b) Ensuring car_charging_now is informed from sensor.<wallbox>_status_description

The house battery still repeatedly tries to Force Charge on each new Octopus Intelligent dispatch slot despite car_charging_now correctly showing the car is not charging.

PR #5229's intro states the goal is to stop Octopus Intelligent dispatch slots being treated as cheap for the house battery when the car isn't actually drawing power, by fixing the ordering so car-charging cancellation happens before rates are built. In testing, the car-side detection and slot cancellation works correctly (confirmed via log evidence below), but Predbat still issues a Force Charge on the house battery using that same dispatch slot's rate — and does this repeatedly, once per new slot, not just as a one-off.

The PR's own discussion notes, as a "known limitation": "Slots Predbat plans itself (Predbat-led charging) are no longer cancelled on low load" — which appears to be exactly this gap: once Predbat decides to Force Charge the house battery on a dispatch slot's rate, that decision is classified as "Predbat-led" and isn't re-evaluated against the same car-charging evidence that correctly cancelled the car's own kWh allocation for the identical slot.

Expected behaviour

Given car_charging_now correctly confirms the car isn't drawing power for a dispatch slot (and switch.predbat_octopus_intelligent_trust_slots is off, i.e. slots start untrusted until confirmed), the house battery should not Force Charge using that slot's cheap rate — per PR #5229's stated goal of fixing the ordering so cancellation happens before rates are built.

Actual behaviour

The car's own kWh allocation for each slot is correctly cancelled (kwh_cancelled shown in the plan), but the house battery is still Force Charged on the same slot's rate — and this recurs on every new slot, not just once. Four occurrences in a single afternoon, all with the car confirmed not charging throughout:

Time (Europe/London, BST) Dispatch slot charge_start_service call Car status at the time
14:30:09 14:30–15:00 → select.foxess_stepps_work_mode = Force Charge Not charging (kwh_cancelled: 10.017 logged 14:40)
15:00:18 15:00–16:00 → select.foxess_stepps_work_mode = Force Charge Not charging (kwh_cancelled: 6.678 logged 14:54)
15:30:09 15:30–16:00 → `select.foxess_stepps_work_mode = Force[

predbat.log

predbat_debug.yaml.txt
predbat.log

](url)

Activity

  1. springfall2008 commented on Sep 28, 2026

    @springfall2008
    Owner

    This is an automated first-pass triage; a maintainer will review before any action is taken.

    Classification: bug · Priority: medium

    I reconstructed the afternoon from your predbat.log and worked through the config in predbat_debug.yaml against current main (v9.3.1, the version you are running — confirmed in the log). Two separate things happened, and only one is a real bug — but it is one, and it is in Predbat.

    Why the 14:30 Force Charge happened (working as designed)

    • Predbat's own measurements show the car did charge between 14:05 and ~14:25: the car SoC sensor rose 74.7 → 77.4 kWh (+2.7 kWh) and Predbat's car energy meter counted ~2.5 kWh in that hour (13:30–14:30). Your status_description sensor mostly read non-"Charging" over that window, which is why the dashboard disagrees.
    • At 14:10:02 the trust check read "Charging" from that sensor while the car was inside the 14:00–15:00 dispatch, and PR feat(dynamic-load): cancel a car's slots and IOG cheap rate while it is not charging, with an option to distrust Intelligent slots until seen charging #5229 says a slot the car is seen charging in is trusted straight away — so the slots resumed line at 14:10 is the design working. The trust then persisted to 14:40 because the status sensor gave no definitive state in between (a read that resolves to "no evidence" keeps the saved state), and the slots were cancelled again at 14:40, also per design. The 14:30:09 Force Charge you logged fell inside that trusted window.

    The real bug: the 15:00 and 15:30 charges while the car slots were cancelled

    From 14:40 onward, every cycle cancelled the car's slots (kwh_cancelled 10.017 → 6.678 → 3.339) — and yet the house battery was still planned to charge 15:00–15:25 and 15:30–16:00 at 6.9p, inside the distrusted dispatch. Your 15:00:18 and 15:30:09 calls are Predbat's own Charge window will be: 15:00-15:25 / 15:30-16:00 writes, so the plan really did charge there with the car cancelled.

    Root cause, verified in your dump and reproduced with a probe test against current main: your rate feed (the Octopus integration's current_rate event) already prices the dispatch periods at the 6.9p off-peak rate but does not set is_intelligent_adjusted on them — your dump has rate_import_base and rate_import_no_io at 6.9p across the whole 14:00–16:00 dispatch while io_adjusted is empty. PR #5229's rate-side protection has two legs: withhold Predbat's own overlay (that works — rate_add_io_slots() skips a cancelled car's future dispatch minutes), and strip a discount the feed already delivered (dynamic_load_car_strip_feed_rates() in octopus.py). The strip only ever touches minutes the feed flagged, so with no flags it is a no-op — "removed the dispatch rate from N minutes" appears nowhere in your log and dynamic_load_car_stripped: 0 in the dump. The result: the car's kWh is cancelled correctly, but the feed's baked-in cheap rate survives and the house battery is planned into the slot anyway.

    The probe: with io_adjusted empty and the feed pricing the slot at 6.9p, a cancelled dispatch still carries 6.9p at minute 900 (15:00–15:25) after both protection legs run — while the existing suite passes, because its fixtures flag the minutes. This matches your log exactly.

    Two side notes:

    Possibly related: #4516, #5080, #5065, and closed #5110 (whose rates-only approach #5229 superseded).

    No further files needed — version, environment, log and debug yaml are all present.

  2. chalfontchubby commented on Sep 28, 2026

    @chalfontchubby
    Collaborator

    Posted by Claude (AI assistant) on behalf of @chalfontchubby, after a second look at the log and debug file.

    Thanks for the detailed report and the attachments. Having gone back through them, we think three separate things happened that afternoon. We can't yet say which of them is a Predbat bug, so the points below are observations and questions rather than a fix.

    In short

    1. Some of the afternoon charging may have been deliberate. You had an Axle VPP event from 18:00 to 19:00, which Predbat prices at 100p/kWh on top of your normal rate (128.99p). The house battery was at 90%, so topping it up earlier in the afternoon to avoid importing at 128.99p during the event can be the right decision, even at the full 28.99p rate.
    2. The 15:00 and 15:30 charges were planned at the cheap 6.9p rate, inside dispatch slots Predbat had already cancelled for your car. This is the part that looks wrong. Predbat's own record of the dispatches agrees: the car's slots were cancelled, but the house battery was still planned to charge in them. What we don't know yet is where that 6.9p price came from (details below). The answer probably depends on how the Octopus Energy integration (BottlecapDave's) is set up, so we have some questions about it.
    3. The car was actually charging between about 14:00 and 14:20, according to both the Wallbox energy counter and the car's own battery level. Predbat noticed the start late (about 14:10) and the stop late (14:40). The 14:30 Force Charge happened during that late window. We'd like to find out whether the Wallbox status sensor was slow to update or Predbat missed a change.

    There is also a known, separate issue that affects you on Axle days: #5050. It isn't the cause of this report, but it will affect what you see. While a VPP event is in the forecast, Predbat's automatic "low rate" threshold rises above your day rate (to 128.49p in your log), so the normal 28.99p day rate is treated as "low rate". A fix is open in PR #5163. Until it's merged, setting rate_low_threshold to about 0.9 works around it.

    Could you send us the following?

    1. Your Octopus Energy integration (BottlecapDave's): which version you're running, and what "Intelligent rate mode" is set to in its settings (planned and started dispatches, or started only).
    2. The rates the integration publishes during a dispatch. Next time a dispatch is planned outside the overnight window, go to Developer tools → States, open event.octopus_energy_electricity_22m0235307_1800051374513_current_day_rates, and copy the rates attribute. We want to see whether the dispatch periods show is_intelligent_adjusted: true.
    3. Wallbox history for 28 Sep, 14:00–14:45: the history of sensor.wallbox_pulsarplus_sn_249105_status_description and sensor.wallbox_pulsarplus_sn_249105_added_energy (a screenshot of the History page is fine). It would also help to know which status values your charger shows apart from "Charging", for example while paused or waiting for the car.
    4. Your Octopus bill for 14:30–16:00 that day: was the house import billed at 6.9p or at the full rate? The Octopus app shows half-hourly costs.
    5. The Axle event: did you know about the 18:00 session, and do you expect Predbat to prepare the battery for it?
    6. The restarts: Predbat restarted at 14:03, 14:53 and 15:44. Do you know why (config edits, add-on updates, crashes)? Each restart resets part of the car-charging detection.
    7. Next time it happens: could you save a debug file during the cancelled dispatch, while it's happening, rather than afterwards? Turn on switch.predbat_debug_enable and attach the predbat_debug_*.yaml, plus predbat.log. The file you sent was taken at 16:05, after the dispatches had finished, so it only shows them looking back.
    Technical detail

    The 6.9p price in cancelled dispatches. In the debug file, rate_import_base and rate_import_no_io are 6.9p across 14:00–16:00, but io_adjusted is empty and dynamic_load_car_stripped is 0. PR #5229's rate protection has two parts:

    • It holds back Predbat's own dispatch price in rate_add_io_slots(). That part worked.
    • It removes a discount the rate feed already applied, in dynamic_load_car_strip_feed_rates(). That only acts on minutes the feed has flagged is_intelligent_adjusted. With no flags, it did nothing.

    The open question is why nothing was flagged. BottlecapDave's adjust_intelligent_rates() sets is_intelligent_adjusted: True on every rate it lowers for a dispatch, and the rate events have carried that flag since BDC v9 (2023). A rate that is already at the off-peak price is passed through without the flag. So an unflagged 6.9p means the integration didn't do the lowering itself. Candidates:

    • an old BDC version
    • something in the local configuration
    • something on Predbat's side we haven't found yet

    The debug file was taken after the fact (16:05, after the 15:44 restart). It proves the plan charged at 15:00 and 15:30, as the Charge window will be log lines show, but not what the flags looked like at those moments.

    Predbat's own Octopus integration (octopus_api_key) builds its rates from the plain two-rate tariff and only adds the dispatch discount itself. PR #5229 already withholds that for a cancelled car, so we'd expect this gap not to occur there. We haven't tested that against your setup.

    Dispatch timeline (the Dispatch timeline car 0 log lines).

    • Octopus dispatched at 13:00 and continuously from 14:00 to 16:00. The 15:00 slot was added after 14:10.
    • The upper-case P/S markers at 15:00 and 15:30 confirm the house battery was planned to import inside blocks already cancelled for the car.
    • The GH#5080 notes (dispatch active but car not charging) fired for 14:30, 15:00 and 15:30.

    Car charging evidence.

    Time added_energy per 5 min Car SoC Predbat
    14:00–14:20 about 0.55–0.65 kWh (≈7 kW) 74.7 → 77.4 kWh cancelled at 14:04 (wrongly), resumed at 14:10
    14:20–14:40 about 0 77.4 still trusted, so the 14:30 Force Charge
    14:40 onwards about 0 77.4 cancelled (correctly)

    Predbat also re-reads the car status every 15 seconds between plan runs and logs any change, but no such line appears all afternoon. That fits the Wallbox status lagging behind the energy counter by 15–20 minutes. But it doesn't fit the late start: that change should have been logged too. The Wallbox history requested above should show which explanation is right.

    #5050 in this log: Rate thresholds (for charge/export) are import 128.49p, and the low-rate windows include every 28.99p day block. The optimiser itself still settled on 6.9p (Import threshold used for optimisation was 6.9p), so #5050 widens the set of candidate charge windows but didn't cause the 15:00 and 15:30 charges.

  3. chalfontchubby commented on Sep 28, 2026

    @chalfontchubby
    Collaborator

    Posted by Claude (AI assistant) on behalf of @chalfontchubby.

    Update: we've found the cause of the 15:00 and 15:30 charges. It's a Predbat bug, not your setup.

    The Octopus Energy integration (BottlecapDave's) is marking the dispatch periods correctly. Predbat reads those markers and then accidentally throws them away when it fetches your export rates straight afterwards. Without the markers, Predbat can't tell a dispatch's cheap price from the normal overnight rate. So when your car's slots were cancelled, the 6.9p stayed in the plan and the house battery was charged into it. We reproduced this on a second installation that uses the same integration.

    Because of that, you don't need to send items 1, 2 and 7 from our last comment: the integration version and settings, the rates attribute, and a debug file taken during a dispatch. Changing your setup won't help either; this needs a fix in Predbat.

    These would still help, but only if they're easy to get:

    • The Wallbox status history for 14:00–14:45 (item 3). It's for the separate question of why Predbat noticed the car stopping about 20 minutes late.
    • Your bill for 14:30–16:00 (item 4): was that import charged at 6.9p or the full rate?

    The Axle and #5050 points from our last comment still stand. Some charging before an 18:00 VPP event can be deliberate.

    Technical detail

    fetch_octopus_rates() (octopus.py) assigns self.io_adjusted from minute_data() on every call. fetch.py calls it first for import, with adjust_key="is_intelligent_adjusted", and then again for metric_octopus_gas and metric_octopus_export, without one. Those later calls reset io_adjusted to {}. Before PR #2826 (v8.27.0) the assignment only happened when adjust_key was set.

    On the second installation (BDC 19.1.0), HA's stored event state shows is_intelligent_adjusted: true on the dispatch periods of current_day_rates and next_day_rates. Predbat's io_adjusted was empty in every debug dump.

    This affects every BDC user with export or gas rates configured. Besides the #5229 strip (dynamic_load_car_strip_feed_rates()), io_adjusted also feeds:

    • rate_replicate()
    • the IOG handling in plan.py
    • the PV10 dispatch handling in the prediction
    • the kernel's io_flag

    A fix is not written yet.

  4. added a commit that references this issue on Sep 28, 2026
  5. chrisgla commented on Sep 28, 2026

    @chrisgla
    Author

    To explain why the car stopped charging I reduced the target in Tesla app to simulate termination of car_charging_now
    I'll check to see if billing is available yet, the problem with this is Octopus sometimes amend daytime charging after the immediate event,

    WRT saving energy for Axle later, the battery is ~29kWh useable with 3.68kW export limit. I think there was around 70% at the decision point in time with predbat plan showing 3 or 4 hours of discharge for this evening.

    Wallbox logs, I might actually be better using the <200W check for not charging as that's updated more regularly, I can't remember now if I asked Claude to include that.

    I don't have the pricing data for today yet but I've attached screenshots of EV/ home peak and off peak use from the most the most recent data octopus has made available on 26th.
    Predbat is fully in control with ~29kWh of battery, there is no reason I should be getting peak rate draw at times shown:

    3:00-13:30 28.99 p/kWh (peak) 1.133 kWh 32.84195p
    13:30-14:00 28.99 p/kWh 0 kWh 0p
    14:00-14:30 28.99 p/kWh (peak) 0.002 kWh 0.05797p
    14:30-15:00 28.99 p/kWh 0 kWh 0p
    15:00-15:30 28.99 p/kWh 0 kWh 0p
    15:30-16:00 28.99 p/kWh (peak) 0.497 kWh 14.4064p
    16:00-16:30 28.99 p/kWh 0 kWh 0p
    Image Image

    To hopefully complete the picture I've attached screenshot of the Bottlecap Dave dispatches for same time. These confirm pattern- dispatch comes in, car doesn't need charge but house battery does, Octopus detect this and in my case charge ~30p x 9kW blowing out most of the savings Predbat might have achieved. Note that I do have notifications set up so as soon as I notice charging I stop it manually and try to update plan manually, I did do this today as well

    Image

    I found one bit of config that I think needs change- watch list is commented out. This might cause the delayed detection of charging stop?

    Let me know if you need any more info :)

  6. chalfontchubby commented on Sep 28, 2026

    @chalfontchubby
    Collaborator

    Watch list is perhaps the delay - might take it from minutes to seconds.
    The "house charging but not car" is why we've been working on this for a while - there are many issues raised against it, but most find that the slots don't end up getting charged at the high rate.

    We have merged a patch that fixes some of the slot tagging that seems to be at the root of your problem - the way we handle the BCD ingest is different to the built in octopus support.

    FYIW - if you filter the predbat log for the word "timeline" you can a chart of IOG slots as they approach, disappear etc.

  7. chalfontchubby commented on Sep 28, 2026

    @chalfontchubby
    Collaborator

    I misspoke - I don't think watchlist will help since I think we already are checking every 15 seconds in this case.
    The wallbox status_description history would be quite helpful - the delay is bugging me.

  8. chrisgla commented on Sep 29, 2026

    @chrisgla
    Author

    Tested in V9.3.2, seems resolved, times approx:-
    12:00 - 12:30 dispatch received via bottlecap.
    12:02 re-plan and ESS charge started.
    12:15 manually set EV target_SoC via Tesla app to force car_charging_now off
    12:17 re-plan and stop ESS charge happened

    The delay I think is just caused by config Claude has missed, car_charging_now had not been defined.
    Now:-

    Image

    I've added this, not sure if needed:-

    Image

    Thanks for the fix! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions