Skip to content

Predbat set to manual Demand but planned (and executed) to force export #5423

Closed

Description

@gcoan

Describe the bug
To preserve the battery SoC between the Octopus Power Down and Axle events I put a manual force demand in at 19:00

Image

But Predbat seemingly partly ignored that and planned for demand followed by force export, and at 19:05 it was indeed exporting:

Image

Expected behaviour
Manual overrides are supposed to be respected regardless of what the plan optimisation gives so Predbat should have been in demand mode for the entire 30 minute slot from 19:00

Predbat version
9.3.6

Environment details
HAOS 2026.9.4
3x GivEnergy inverters via GivTCP REST

Screenshots
above

Log file

predbat.log

Predbat debug yaml file

predbat_debug.yaml.txt

Activity

  1. springfall2008 commented on Oct 6, 2026

    @springfall2008
    Owner

    Automated triage did not complete for this issue after 3 attempts - see the triage bot's logs for details. It has been skipped so that it does not hold up triage of later issues. Remove BOT_FAILED and add BOT_REVIEW to have the bot look at it again - nothing removes BOT_FAILED on its own, so leaving it on would outlive a later successful run.

  2. added
    BOT_FAILEDAn automated triage/PR attempt failed; see the comment for details
    on Oct 6, 2026
  3. added
    BOT_REVIEWTrigger an autotriage
    and removed
    BOT_FAILEDAn automated triage/PR attempt failed; see the comment for details
    on Oct 6, 2026
  4. springfall2008 commented on Oct 6, 2026

    @springfall2008
    Owner

    Automated triage failed to complete for this issue - see the triage bot's logs for details. Not retrying automatically; remove BOT_FAILED and re-add BOT_REVIEW to try again.

  5. added
    BOT_FAILEDAn automated triage/PR attempt failed; see the comment for details
    and removed
    BOT_REVIEWTrigger an autotriage
    on Oct 6, 2026
  6. chalfontchubby commented on Oct 7, 2026

    @chalfontchubby
    Collaborator

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

    Classification: bug
    Priority: priority_low (cosmetic plan-display issue, no confirmed execution or cost impact in this report)

    What I checked: the attached predbat_debug.yaml and predbat.log, replayed against current main (v9.3.6 is what the reporter ran; confirmed from the log).

    What actually happened at execution: the log shows Predbat did correctly apply the manual demand override at the inverter level — by 19:00:09 all three inverters were switched to Eco/Demand with Freeze Export on ("Info: Completed run status Demand [Freeze exporting]"), and live telemetry at 19:05:00 shows ~0 W battery/grid power on all three inverters. So contrary to the plan screenshot's apparent implication, the hardware was not exporting at 19:05 in this log.

    What is actually wrong: the plan table. The attached plan screenshot shows the 19:05 row rendering as a split "demand → export" row, with a genuine export window starting at 19:30 (the Axle VPP paid-export session, confirmed in the log: Setting Axle VPP session in range 10-06 19:30:00 - 10-06 20:30:00 export True pence_per_kwh 100). The root cause: select.predbat_manual_demand was set for the single 19:00 slot, and optimise_charge_windows_manual() (apps/predbat/plan.py:4965) only clears an export window when that window's own start exactly equals a manual-override minute. The rate-window scanner (find_charge_window, apps/predbat/fetch.py) produced the next export candidate window starting at 19:10, not 19:00 or 19:30 — a window that falls inside the nominal 19:00–19:30 override slot but has a different start minute, so the override never attaches to it. Plan rendering reflects this mismatched window; live execution does not appear to have been affected in this instance.

    Cost impact — replayed both ways: I replayed the debug yaml against main with --redo, once as captured (override set) and once with manual_demand cleared. Both runs converge on the identical plan metric (-1562.2784) to four decimal places — the override made no difference to the optimizer's chosen plan. The real profitable opportunity in this period was always the Axle export starting at 19:30, outside the 19:00 override's 30-minute slot, so in this specific case the manual override was a no-op rather than something the optimiser overrode.

    Related (not duplicates): #4257 (odd charging choices around Axle events) and #4592 (Axle VPP conflict check gap) touch the same Axle-event planning area and may be worth a maintainer's cross-check, though the mechanism here looks distinct.

    No further information needed — both required files were attached.

  7. added
    BOT_TRIAGEDHas been through the triage bot
    and removed
    BOT_FAILEDAn automated triage/PR attempt failed; see the comment for details
    on Oct 7, 2026
  8. chalfontchubby commented on Oct 7, 2026

    @chalfontchubby
    Collaborator

    🤖 Correction to my earlier triage comment (automated, maintainer review still pending).

    I need to walk back one claim: "the hardware was not exporting at 19:05... contrary to the plan screenshot's apparent implication." That was wrong, and the screenshot does not contradict the log.

    Re-reading the log more carefully for the full 19:00-19:30 slot (not just the 19:05 snapshot):

    • At the 19:05 planning cycle, Predbat scheduled a fresh export window: Next export window will be: 2026-10-06 19:10:00+01:00 - 2026-10-06 20:31:00+01:00.
    • At the following 19:10 cycle, the readback from the inverter showed a force-export window already configured as 19:05:00 - 20:01:00 before Predbat cleared it back to None/None and returned the inverter to Demand.

    So the plan genuinely did schedule, and the inverter genuinely did have configured, an export window starting inside the user's 19:00-19:30 manual demand slot — exactly what the screenshot's split "demand → export" row for that slot shows. My "hardware was not exporting" framing conflated "no sustained power flow" (battery/grid power readings were ~0W at both the 19:05 and 19:10 polls) with "no export was scheduled or configured," which are different claims. The telemetry only supports the former.

    This doesn't change the root-cause direction from my first comment - the manual override not fully covering the slot it's meant to protect is still the mechanism - but it does mean the practical impact is real, not just a display artifact: Predbat configured export inside the protected window, and only the brief polling cadence (and no PV/favourable conditions to drive real power at that moment) likely kept it from mattering here. On a more active system this could mean real export during a slot the user explicitly asked to hold.

    I attempted a fix (new branch, not yet pushed) at the window-construction level (find_charge_window in apps/predbat/fetch.py), with a passing regression test, but replaying the reporter's own debug yaml against current main (with --redo) did not reproduce the 19:10 boundary either with or without that fix - the replay's regenerated rate/session data diverges from what the live system actually had at the time, so I can't yet confirm that fix addresses the mechanism that produced this specific 19:10 scheduling. Flagging this as something a maintainer may want to dig into further, ideally with the live Octopus/Axle session timing rather than a --redo replay.

  9. gcoan commented on Oct 7, 2026

    @gcoan
    CollaboratorAuthor

    On a more active system this could mean real export during a slot the user explicitly asked to hold.

    As I said at the beginning, the background to this was that I had told Predbat to keep the inverters in demand mode for the 19:00-19:30 slot, and this was to preserve the battery SoC for the forthcoming Axle event at 19:30.
    [Reason for having to do this goes back to the #4856 multi inverter handling issues, Predbat doesn't take account of differing SoCs and corresponding charge/discharge rates in constructing the plan, and I could see that inverter I would run out of SoC half way through the Axle session if I let Predbat execute its plan]

    Predbat did actually start exporting, this is clear from the predbat status (19:06)

    Image

    and grid power:

    Image

    interestingly I managed (accidentally) to get predbat to stop the export. I had a couple of Manual API overrides, setting the inverter limit discharge to maximum on inverters 1 and 2 (which should have been a no-op as the override was for max rate]. When I removed these overrides at 19:08, Predbat re-planned and removed the export

    Image

    [the need to manually configure charge and discharge rates comes from another side effect of the multi-inverter control issues, but we know about that ....
    I have it all nicely controlled with inverter override buttons on the dashboard now 😜

    Image
  10. chalfontchubby commented on Oct 7, 2026

    @chalfontchubby
    Collaborator

    (This comment was posted in error due to a tooling mistake - see the corrected version below.)

  11. chalfontchubby commented on Oct 7, 2026

    @chalfontchubby
    Collaborator

    🤖 Automated follow-up (maintainer review still pending).

    I tried a forward log replay (./run_all --debug_file ... --replay_log ..., from tools/debug-replay-forward) to actually reproduce the 19:10 export scheduling against the attached files, rather than --redo's fresh regeneration. It couldn't produce anything to compare, for a structural reason rather than a tooling bug:

    • The attached predbat_debug.yaml was captured at 19:05:00 - right at/after the moment things had already gone wrong. The replay only steps forward from a yaml's own timestamp, so it can't look back at how the 16:30-19:00 export window formed or why the 19:00 cycle scheduled export to resume at 19:10 inside the manual demand slot - by the time this yaml was taken, per the log, the plan had already committed to exporting at 19:10.
    • The attached predbat.log ends at 19:13, only 8 minutes past the yaml. That leaves at most one candidate re-plan cycle (19:10) for the replay to work with, and the log cuts off before that cycle even logs its "Export windows filtered" line - so there's nothing left to compare against.

    To actually pin down why the window boundary landed at 19:10 instead of 19:00 or 19:30, we'd need:

    • A debug yaml from before 16:30 on 2026-10-06 - ideally the nearest rolling snapshot Home Assistant kept (Predbat's web interface → Plan → History → Debug column, next to a time slot before 16:30), so the replay can watch the export window form from scratch.
    • A predbat.log spanning at least 16:00 through 20:00 that same day, so the replay has every re-plan cycle through the full incident, including the 19:30 Axle transition.

    @gcoan - if you still have either of these (the debug history panel keeps roughly the last 48 hours by default, so it may already be gone, but worth checking), that would let us actually confirm or rule out the fix we're looking at rather than guessing from the aftermath. Docs on where to find the debug history snapshots: https://springfall2008.github.io/batpred/customisation/#debug-history

    (Replaces my previous comment, which a copy-paste slip broke - apologies for the noise.)

  12. chalfontchubby commented on Oct 7, 2026

    @chalfontchubby
    Collaborator

    (I'm trying to test a new flow that lets me play a log - rather than just reading it - and check whether whatever changes we make "do the right thing". Debug.yaml just let us run one plan, but the new flow lets us run the entire process - if it works. But it needs an earlier debug.yaml to anchor the start and the logs following from then as a script to follow.)

  13. chalfontchubby commented on Oct 7, 2026

    @chalfontchubby
    Collaborator

    🤖 Second correction, this time to my earlier "the manual override made no difference to the plan" claim (automated, maintainer review still pending).

    Rik pushed back on that result as suspicious, and he was right to. My original "no override" comparison run had a bug: read_debug_yaml restores manual_demand_times/manual_all_times as raw top-level attributes copied straight from the yaml (apps/predbat/userinterface.py:797-801), completely independently of the select.predbat_manual_demand config item's displayed value (restored separately at line 831-836, for UI display only). I had only edited the config item's value to simulate "no override" - the actual attributes the optimiser reads were untouched, so both my "with" and "without" runs still had the override active. That's why they came out identical; it wasn't evidence of anything.

    Redone properly (clearing both manual_demand_times: [] and manual_all_times: [] in the comparison yaml), the two plans do diverge:

    • 19:00-19:30 (the override slot): with the override, export target is 30% (26.0p); without it, the optimiser's own unconstrained choice is 18% (13.0p) - a smaller export, not full demand, but a real difference.
    • 19:30-20:00: same divergence carries forward (30% (26.0p) vs 18% (13.0p)).
    • 21:30: the two plans have fully diverged in shape by this point - one is in Demand, the other in a split Exp state.
    • Aggregate plan cost: -15.62 vs -15.63 (about 1p difference over the 48h plan) - small in total, but the per-slot divergence is real, not noise.

    So the override does change the plan, as expected, and I retract the "made no difference" framing entirely. What I still can't determine (same reproduction gap as the last two comments) is whether this divergence explains the specific 19:10 scheduling anomaly from the live log, since --redo regenerates rates/sessions fresh rather than following what actually happened - the ask for an earlier debug yaml plus a longer log still stands for that part.

    Apologies for the repeated corrections on this thread - will slow down and sanity-check comparisons like this against the raw restored state next time, not just the top-level config display.

  14. gcoan commented on Oct 7, 2026

    @gcoan
    CollaboratorAuthor

    Something weird with github its not letting me upload either the logfile or the debug file

  15. gcoan commented on Oct 7, 2026

    @gcoan
    CollaboratorAuthor

    predbat.02.log

    predbat_debug_20261006-170000.yaml.txt

    here you go, most recent debug from before the event

  16. chalfontchubby commented on Oct 7, 2026

    @chalfontchubby
    Collaborator
  17. chalfontchubby commented on Oct 8, 2026

    @chalfontchubby
    Collaborator

    (Posted by Claude on Rik's behalf.)

    Summary: thanks for the 17:00 debug file and the longer log - they found it. This was a real Predbat bug, not your configuration. A step in the export planning could stretch an export window backwards into the slot before it, and that went straight through your 19:00-19:30 manual demand override. The fix is in PR #5447.

    What happened. Predbat set up the 19:00-19:30 demand slot correctly. Each time it re-planned after that, though, a later stage of the optimiser stretched the 19:30 export window backwards: to 19:15 at 18:35, and to 19:10 at 18:45. The 18:45 plan was kept and still in use at 19:00. The 19:05 re-plan stretched it to 19:05, which is why the inverter went into timed export at 19:05 until the 19:08 re-plan moved it back to 19:30. The override check only looks at the minute each window starts, so a window that started at 19:30 and was then stretched back wasn't caught.

    A correction to my earlier comments. I said the 19:05 window start was just a side effect of replaying from a file captured at 19:05. That was wrong: your log shows the live system did exactly the same thing. The earlier change we mentioned looking at (in find_charge_window) was also the wrong place - that part handled your slot correctly.

    Technical detail

    The bug is in optimise_swap_export's "partial combine" step in apps/predbat/plan.py, which moves export time from an earlier window into a later, trimmed one. It lengthened the earlier window by the time moved instead of shortening it, and nothing stopped it at the previous window's end. #5447 makes the earlier window shrink by what the later one gains, and adds a regression test with a manual slot just before the window being combined.

  18. gcoan commented on Oct 8, 2026

    @gcoan
    CollaboratorAuthor

    Thanks Rik, still finding these little things in Predbat. Glad I was able to help identify it.

    The auto captured debug logs make such a difference to things like this, definitely a great improvement of yours

    Cheers

  19. chalfontchubby commented on Oct 9, 2026

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

Metadata

Metadata

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions