evcc
evcc copied to clipboard
Add planner for dynamic pricing
- [x] move
cheap
rate to site - [x] remove tariff
IsCheap
api - [x] add tests for planner
- [ ] integrate with existing planner
Ich glaub ich habs grob verstanden. Eine Pricetable generiert einen Plan über n nicht-kontinuierliche Slots und startet/stoppt bei jedem Planslot (außer dem letzten wo nicht mehr gestoppt wird). Der letzte müsste sich an der Zielzeit bemessen, nicht daran ob er der letzte des Plans ist?
Wenn das so grob stimmt dann könnten wir mal versuchen das zu zerlegen (und ich helfe gerne):
- Tarife generieren
PriceTables
- PriceTable ist sortierbar ;)
- Site verwurstet PriceTable mit einem
Planner
- Planner wird anstatt
isCheap
and Loadpoint übergeben und signalisiert oder zu laden ist (Active()
?)
Übel wirds wenn Lastmanagement kommt, aber das ist eine ganz andere Geschichte…
Ja, das hast du richtig verstanden.
Der letzte müsste sich an der Zielzeit bemessen, nicht daran ob er der letzte des Plans ist?
beides ist möglich. Es geht aber nicht um Zielzeit sondern um erwartet ladezeit. Der letzte Slot kann auch Stunden vor der Zielzeit liegen. Beim Testen muss man aufpassen, Awattar sperrt nach zu vielen Versuchen die API (vermutlich für die IP Adresse), daher hatte ich das fake_awattar drinnen.
@schenlap ich habe mal angefangen aufzuräumen. Die Logik gibts jetzt nur noch 1x in einer neuen Planner Komponente. Ein paar Todos hab ich oben ergänzt.
ich würde das Cheap komplett rauswerfen, spricht etwas dagegen? Ich sehe darin keinen Nutzen mehr.
ich würde das Cheap komplett rauswerfen, spricht etwas dagegen? Ich sehe darin keinen Nutzen mehr.
Wir brauchen ein Signal an den Ladepunkt ob geladen werden soll. was günstig ist weiß der Planner aufgrund der Zielzeit. Es mag aber auch im PV Modus Situationen geben wo man gerne zusätzlich aus dem Netz bezieht, z.B. bei negativen Preisen. Den zweiten Fall deckt heute „isCheap“ ab.
Der zweite Fall ist heute unter Tariff konfiguriert. Der gehört gem. Punk 1 oben nach Site als Parameter. Insofern bräuchte Tariff nur die Möglichkeit den aktuellen Preis auszugeben.
Ladeindikation für den Ladepunkt ist dann a) Planner sagt es muss geladen werden oder b) Site stellt fest, dass der Tarif gerade „billig“ ist- dann laden wir immer.
Bekommen wir Beides unter einen Hut?
Bekommen wir Beides unter einen Hut?
aktuell geht beides. Dann müssen die von dir genannten Änderungen noch umgesetzt werden. (cheap aus konfig übernehmen, cheap in site verschieben). Auch da würde ich Unterstützung brauchen.
Bekommen wir Beides unter einen Hut?
aktuell geht beides. Dann müssen die von dir genannten Änderungen noch umgesetzt werden. (cheap aus konfig übernehmen, cheap in site verschieben). Auch da würde ich Unterstützung brauchen.
Hab ich eingebaut- Du bist wieder dran ;)
@schenlap was fehlt denn noch um auf "Ready for review" zu kommen?
Ich hab nochmal aufgeräumt- die letzten Knackpunkte müsstest Du Dir bitte anschauen.
@schenlap der ganze letzte Block vereinfacht sich dann zu:
// is the current slot the last planned slot?
if curSlotNr == cntExpectedSlots && plannedDuration > requiredDuration {
curSlotNr = 0
}
Dann verstehe sogar ich den ;)
Dazu:
s/cntExpectedSlots/plannedSlots/
s/curSlotNr/currentSlot/
Jetzt produzieren alle Tests ein fail. Es wird nie eingeschaltet. Bei den aktuellen Strompreise wäre das vielleicht sogar sinnvoll :-) . Auf den ersten Blick sind es nur Umbenennungen aber es hat sich grundlegend an der Funktion was geändert, das muss ich zuerst finden bevor ich weitermachen kann.
Entschuldige. Ich schau auch nochmal rein. Wers kaputt macht muss auch reparieren.
Das wäre eine mögliche Lösung. Es wird zuvor auf t.clockNow gesetzt, da die Zeit noch nicht weitergeschritten ist, wird Before nicht erfüllt. Ev. muss man es auch anders lösen.
@@ -101,7 +104,7 @@ func (t *Planner) PlanActive(requiredDuration time.Duration, targetTime time.Tim
slot.Price, plannedDuration)
// current timeslot is a cheap one
- if slot.Start.Before(t.clock.Now()) && slot.End.After(t.clock.Now()) {
+ if !slot.Start.After(t.clock.Now()) && slot.End.After(t.clock.Now()) {
cheapSlotNow = true
curSlotNr = cntExpectedSlots
t.log.TRACE.Printf(" (now, slot number %v)", curSlotNr)
ok, Problem 1 sind die Test Cases. Durch umdrehen der before/after Logik gibts jetzt 1ns Differenz
Es bleibt noch:
planner_test.go:111: fixed tariff case 1: expected true, got false
planner_test.go:111: fixed tariff case 1: expected true, got false
FAIL
Mit
if !slot.Start.After(t.clock.Now())
bekomme ich keinen Fail. Mit dieser Änderung bekommst du
planner_test.go:111: fixed tariff case 1: expected true, got false
planner_test.go:111: fixed tariff case 1: expected true, got false
?
Das wäre dann vielleicht wirklich ein Fehler im Test selbst. Muss ich mir ansehen
Ich glaube ich habe noch eine große Unschönheit gefunden: Ist kein Tarif konfiguriert, dann wird mit Zielzeit über timer.go geladen. Konfiguriert man einen fixed-Tarif, z.B wegen der Ersparnisanzeige, dann wird mit planner.go geladen. Nur weil mein einen Preis einstellen will darf sich die Ladelogik nicht ändern. Auch wenn beide das selbe Verhalten erzeugen ist es nicht schön wenn unterschiedliche Code-Teile ausgeführt werden. Wie ich es in awattar implementiert habe, gab es das Problem nicht. War aber auch nicht sauber. Mit der Umstellung auf planner ist da jetzt der fixed Tarif aber mit rein gerutscht.
Wie ich es in awattar implementiert habe, gab es das Problem nicht. War aber auch nicht sauber. Mit der Umstellung auf planner ist da jetzt der fixed Tarif aber mit rein gerutscht.
Mhhm. Für's erste könnten wir den Typ prüfen und den ausschließen. Aber wovon hängt das ab? Ob der Tarif überhaupt nur 1 Zone hat? Was bedeutet das mit Bezug auf https://github.com/evcc-io/evcc/issues/2524?
Typ prüfen ist auch nicht schön. planner sollte timer.go eigentlich komplett ersetzen. Wobei das jetzt nicht so ist (nämlich dann wenn der SoC zum Zielzeitpunkt noch nicht erreicht wird, übernimmt derzeit noch der timer die restliche Ladung)
Mit
if !slot.Start.After(t.clock.Now())
bekomme ich keinen Fail. Mit dieser Änderung bekommst duplanner_test.go:111: fixed tariff case 1: expected true, got false planner_test.go:111: fixed tariff case 1: expected true, got false
?
Das wäre dann vielleicht wirklich ein Fehler im Test selbst. Muss ich mir ansehen
Der "Fehler" ist in den Tests und im Verständnis von before/after und der Frage ob die genau matchende Millisekunde dazu zählt. Da das in der Realität irrelevant ist sollten m.E. die Tests defensiv sein und die Timestamps um 1ns verschieben, im Code selbst sollten wir das nicht tun.
Typ prüfen ist auch nicht schön. planner sollte timer.go eigentlich komplett ersetzen. Wobei das jetzt nicht so ist (nämlich dann wenn der SoC zum Zielzeitpunkt noch nicht erreicht wird, übernimmt derzeit noch der timer die restliche Ladung)
Das eine ist doch eine Zeitsteuerung, das andere eine Preissteuerung. Preissteuerung macht nur Sinn wenn es unterschiedliche Preise gibt (>1 Zone, >1 Preis).
Alternativ: wir schmeissen die Zeitsteuerung weg und ersetzen sie- sollte es keinen Grid Tarif geben- durch einen "fake" Grid Tarif mit Preis 0.
Ich frag mich auch grad, warum eigentlich die site
bestimmen sollte, ob der Ladepunkt laden muss wg. seiner Planung. Auch nicht schön...
Bingo! Im Ladepunkt können wir die Prio der beiden Planner umdrehen -> Problem gelöst. Also erst Restzeit, dann nach Tarif.
Ich bau das nochmal komplett in Richtung Loadpoint; das wird allerdings umfangreicher...
Update: sorry für die Verzögerung. Der Komplettumbau erfordert einige Anpassungen am bestehenden Zielladen die etwas komplexer sind. Bin noch dran...
Depends on https://github.com/evcc-io/evcc/pull/2780 which will provide better abstraction for the time planner.
My Electricity provider publishes the daily tariff using an API in JSON format. Is HTTP as source type supported for tariff parsing?If not, it might be an option to allow type: custom and source: http for using the feedin and grid tariffs
Bitte neues Thema- wer ist Dein Anbieter?
Done in discussion. Topic #3333 Provider is Eneco