Charge Planner not following the plan
Describe the bug
This plan shows two slots seperated by 1 slot as the charging plan.
In reality this happens after the first slot
evcc-1 | [lp-1 ] DEBUG 2024/10/23 01:00:22 plan: charge 1h0m28s between 2024-10-23 02:00:00 +0200 CEST until 2024-10-23 05:30:00 +0200 CEST (power: 11040W, avg cost: 0.281)
evcc-1 | [lp-1 ] DEBUG 2024/10/23 01:00:22 plan: avoid re-start within 1h0m0s, continuing for remaining 59m37s
https://github.com/evcc-io/evcc/blob/fc377138d1debfea0d4bfc749911f1be580bf73b/core/loadpoint_plan.go#L175-L177
Here the value smallGapDuration which is not configurable is used to skip the slot inbetween that was planned.
Either the shown plan is wrong because at the beging of the plan we already know that the slot inbetween will be skipped anyway or the skip is wrong.
It's possible that the skipped slot is way more expensive than the planed slot.
Steps to reproduce
Set planner
Configuration details
none
Log details
evcc-1 | [lp-1 ] DEBUG 2024/10/23 01:00:22 plan: charge 1h0m28s between 2024-10-23 02:00:00 +0200 CEST until 2024-10-23 05:30:00 +0200 CEST (power: 11040W, avg cost: 0.281)
evcc-1 | [lp-1 ] DEBUG 2024/10/23 01:00:22 plan: avoid re-start within 1h0m0s, continuing for remaining 59m37s
What type of operating system are you running?
Linux
Nightly build
- [ ] I have verified that the issue is reproducible with the latest nightly build
Version
lateste master
Seems the planner should already take the smallStepDuration into account? I‘m not enitrely convinced this is worth doing though. The plan is a projection, not a committment. I‘d also assume that high 1-hour peaks in the price function are unlikely.
Let's say car comes home, you plan for it do be done at 20:00 and you get 2 to 3 slots because there are not that many left. So if you don't skip the big one at 18:00 you'll pay nearly 10ct more.
I think it could be a compatibility setting if a car has issues with starting and stopping to charge. Like VW where you have to do a wake-up call for example. But for cars where this works fine, why not just use the projected slots with the best price outcome?
That doesn‘t contrdict the argument. The less flexibility the planner has the higher the prices will be in any case.
Seems the planner should already take the smallStepDuration into account?
I don't think that's feasible. It would make the planner much more complicated as it would need to recursively plan. Current slot plus following might be more expensive than a later slot plus following. This would need to be taken into account.
Nothing the core team will be working on but happy to take a PR.
What about making the slot skipping optinal for people who have problems without that in their car? The setting would set smallGapDuration to 60 instead of the default 0 ? Setting per Loadpoint.
I could make that PR.
Wir wollen keine weiteren Settings. Was mir auffällt:
case requiredDuration < smallGapDuration:
lp.log.DEBUG.Printf("plan: continuing for remaining %v", requiredDuration.Round(time.Second))
return true
case lp.clock.Until(planStart) < smallGapDuration:
lp.log.DEBUG.Printf("plan: avoid re-start within %v, continuing for remaining %v", smallGapDuration, lp.clock.Until(planStart).Round(time.Second))
return true
Im ersten Fall könnte man überlegen ob das wirklich smallGapDuration sein soll oder nicht eher smallSlotDuration.
Im zweiten Fall (Deinem) könnte man überlegen ob smallGapDuration den richtigen Wert hat. Alternativ: 30min oder kürzer. Das stünde dann mit Konflikt mit https://github.com/evcc-io/evcc/pull/7419 (/cc @Hofyyy). Alternativ könnte man als zweite Bedingung einfügen, dass die requiredDuration zusätzlich >= smallSlotDuration sein muss oder kombiniert Beides. Logik: je länger der darauf folgende Slot, desto mehr lohnt es sich darauf zu warten und nochmal zu pausieren.
also im Grunde
Wir wollen keine weiteren Settings. Was mir auffällt:
case requiredDuration < smallGapDuration: lp.log.DEBUG.Printf("plan: continuing for remaining %v", requiredDuration.Round(time.Second)) return true case lp.clock.Until(planStart) < smallGapDuration: lp.log.DEBUG.Printf("plan: avoid re-start within %v, continuing for remaining %v", smallGapDuration, lp.clock.Until(planStart).Round(time.Second)) return trueIm ersten Fall könnte man überlegen ob das wirklich smallGapDuration sein soll oder nicht eher smallSlotDuration.
Bei smallSlotDuration(10 min aktuell) wenn die Restzeit unter 10 min ist dann geht das laden weiter. Denke das könnte schon auch 15 min sein (also 25% einer Stunde)
Im zweiten Fall (Deinem) könnte man überlegen ob smallGapDuration den richtigen Wert hat. Alternativ: 30min oder kürzer. Das stünde dann mit Konflikt mit #7419 (/cc @Hofyyy). Alternativ könnte man als zweite Bedingung einfügen, dass die requiredDuration zusätzlich >= smallSlotDuration sein muss oder kombiniert Beides. Logik: je länger der darauf folgende Slot, desto mehr lohnt es sich darauf zu warten und nochmal zu pausieren.
case requiredDuration > smallSlotDuration && lp.clock.Until(planStart) < smallGapDuration:
würde bedueten es wir noch länger als 15 min geladen (smallSlotDuration) und der nächste Slot startet in wengier als smallGapDuration 30 minuten.
In meinem fall waren noch 59 min restzeit übrig und und der start wäre in knapp und 60 gewesen und er würde kein true zurück liefern und erstmal wieder aufhören zu laden.
Ich bin heute zufaellig auch so einer smallSlotDuration hinterhergelaufen: Die Anfangszeit in der GUI schiebt sich dann immer weiter nach hinten, weil der Boiler fuer die veranschlagten 500Wh nur 9 Minuten brauchen wuerde. Bei 'nem Auto will man nauerlich nicht fuer 10 min die Schuetze klapppern lassen, bei Solid State Technik fuer small devices sind die 10 Minuten dann doch einschraenkend. Man kann es eben auch nicht allen Recht machen :-(