Skip to content

Completion window for low-power charging to reduce rate oscillation #5536

Open

Description

@cjt105

Problem
When using low-power charging, Predbat can repeatedly adjust the requested charging rate as it attempts to achieve the planned completion time precisely. This can result in several small changes to the charging rate towards the end of a charging slot, even when the battery is on track to reach its target SoC and there is little practical benefit in further optimisation. Example from my overrnight charge:

Image

Existing completion margin
Predbat already has the predbat_charge_low_power_margin setting, which provides a margin intended to allow charging to finish ahead of the planned completion time.

I would like to explore whether this could be extended to address the issue above, rather than introducing a separate setting with overlapping semantics.

My understanding is that the existing margin shifts the desired completion time earlier, but Predbat can still repeatedly optimise towards that adjusted time. The desired behaviour is different: once the current rate is expected to complete charging within an acceptable window, there should be no need to keep adjusting it to hit a particular minute.

Proposed behaviour
Introduce configurable early and late completion tolerances for low-power charging, defining an acceptable completion window relative to the end of the planned charging slot.

For example:

  • Early tolerance: 30 minutes
  • Late tolerance: 0 minutes

For a charging slot ending at 06:00, any predicted completion between 05:30 and 06:00 would be acceptable.

While the existing charging rate is expected to achieve the required SoC within this window, Predbat should avoid adjusting the rate merely to bring the predicted completion closer to the nominal target time.

If the predicted completion moves outside the acceptable window, or a meaningful planning constraint changes, Predbat should recalculate the rate as necessary.

Relationship to the existing setting
Ideally, this feature would build on or clarify the semantics of predbat_charge_low_power_margin.

Possible approaches include:

  • Redefine the existing setting to represent an acceptable early-completion window, with a seperate, optional late tolerance.
  • Retaining the existing margin for backward compatibility while adding separate early and late tolerances.

The key requirement is that a completion tolerance should influence whether a new rate is needed, rather than simply shifting the target time while continuing to optimise towards it.

Expected benefits

  • Reduces unnecessary charging-rate adjustments and oscillation.
  • Reduces inverter writes.
  • Avoids optimising towards an unnecessarily precise completion time.
  • Preserves the ability to respond to changes in required SoC, charging availability and other genuine constraints.

My proposed default values would be a 30-minute early tolerance and zero late tolerance, treating the end of the charging slot as a hard deadline.

Activity

  1. springfall2008 commented on Oct 13, 2026

    @springfall2008
    Owner

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

    Classification: enhancement (a proposed behaviour change with new configurable settings, not a report that Predbat is misbehaving — no priority applied for feature requests).

    I verified the reporter's premise against current main (v9.3.7-26-g1faff228) by reading the low-power rate path:

    • While a charge window is active, find_charge_rate() re-runs every 5-minute execute cycle (apps/predbat/execute.py:446) and re-picks the lowest rate on a 100 W grid whose simulated charge reaches the target SoC within window.end − charge_low_power_margin minutes (apps/predbat/utils.py:2571, search loop apps/predbat/utils.py:2627-2649). So the existing margin shifts the completion target earlier, but the loop still re-optimises to that single point in time every cycle — "several small changes towards the end of the slot" (the ~850→1000→900→1000→700 W steps in the chart) is the designed just-in-time behaviour, not a defect.
    • The only rate stickiness today is (a) a narrow "stick with current rate" guard (apps/predbat/utils.py:2652), which fires only when the curve-capped achievable rate is below both the selected and the current rate, and (b) the ~5%-of-max write deadband in adjust_charge_rate() (apps/predbat/inverter.py:2196, rate_tolerances() at apps/predbat/inverter.py:2147-2164) — on a 2.6 kW inverter that is ~130 W, so the end-of-slot adjustments shown do cross it and write.
    • The proposed completion window (keep the current rate while its predicted completion lands inside the early-to-late tolerance, recompute only when it leaves the window or a constraint changes) is not provided by any existing setting, so this is not a duplicate — it is a genuine gap the margin does not cover.

    Related (same churn-reduction goal, different mechanisms): #2628 (deadband for fluctuating SoC readings), #2334 (write churn while holding SoC, closed), #5434 (car-conditional setpoint churn), #3540/#5083/#667 (charge/hold flip-flop family). Low-power-mode family: #4975, #4577, #4726, #5241.

    Design notes for review:

    • find_charge_rate() is shared by the published plan's prediction (apps/predbat/prediction.py:666 applies low power only on the best/best10/test passes; the optimiser itself costs windows at full rate) and by the live execute cycle. A "keep the current rate" completion window can only be execute-side — the prediction sim has no "current rate" — so the published prediction would assume the just-in-time rate while execution holds a possibly different one. Fine if intended, but a conscious choice.
    • The reporter's defaults (30 min early, 0 late) do overlap the existing margin's semantics (default 10 minutes, a shifted point deadline) as they say; resolving those into one setting family is the key design decision. Small aside: docs/customisation.md:494-495 says the margin can change "between 0 and 30" but apps/predbat/config.py:1104 allows up to 60 — worth tidying if this setting gets reworked.
    • With a 0-minute late tolerance, the last minutes can still legitimately move the rate upward if real SoC lags the simulation, and the 100 W search grid means the end-of-slot approach can still step — the window can reduce churn but not guarantee a single write per slot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BOT_TRIAGEDHas been through the triage botenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions