Completion window for low-power charging to reduce rate oscillation #5536
Description
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:
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
springfall2008 commentedon Oct 13, 2026
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:
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 withinwindow.end − charge_low_power_marginminutes (apps/predbat/utils.py:2571, search loopapps/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.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 inadjust_charge_rate()(apps/predbat/inverter.py:2196,rate_tolerances()atapps/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.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:666applies low power only on thebest/best10/testpasses; 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.docs/customisation.md:494-495says the margin can change "between 0 and 30" butapps/predbat/config.py:1104allows up to 60 — worth tidying if this setting gets reworked.