Laden bei uns — die beiden Masken

Plan, Preis, Schutz des Hausakkus und Abrechnung · Nachbarmaske und Hausmaske · Stand 23.10.2026

Warum es diese Seite gibt. Der Nachbar lädt bei uns mit und zahlt 0,35 €/kWh. Damit das kein Verlustgeschäft wird, muss die Ladung in die billigen Viertelstunden gelegt werden, und der Hausakku darf sie nicht bezahlen. Beides macht keine fertige Software — EVCC kennt nur eine feste Preisschwelle, und für EVCC ist der Hausakku nicht von Netz zu unterscheiden, weil alle Lasten am AC-Ausgang hängen. Diese Seite beschreibt, was wir deshalb selbst gebaut haben.
Neu seit dem 16.10.2026: dieselbe Maske gibt es jetzt auch für uns. Was für Dietrich gebaut wurde — Menge rechnen, billigstes Fenster suchen, Fertig-bis — ist für das eigene Auto genauso nützlich. Darum läuft ein Programm in zwei Betriebsarten: --modus nachbar auf Port 8089 und --modus haus auf Port 8091. Gemeinsamer Code, getrennte Zustandsdateien, getrennte Adressen, getrennte Anmeldegruppen. Im Haus gibt es keine Abrechnung und keinen Festpreis — dort stehen die echten Viertelstundenpreise.

Die Adressen

Alle drei sind live gemessen am 16.10.2026: beide Masken antworten von außen mit 302 auf die Anmeldeseite, die Anmeldeseite selbst mit 200.

WerAdresseInternAnmeldung
Nachbar (Dietrich)https://laden.askai.de nginx 8090 → 127.0.0.1:8089Gruppe nachbarn
Haus (Jürgen, Kristina)https://charge.askai.de nginx 8092 → 127.0.0.1:8091Gruppe haus
Anmeldeseite für beidehttps://auth.askai.de 7072 → Authelia 9091—

Der Weg von außen nach innen, für beide gleich aufgebaut:

Browser → Apache auf der .55 (Zertifikat)
        → nginx auf dem apps-server (Authelia-Prüfung)   8090 bzw. 8092
        → nachbar_laden.py                          127.0.0.1:8089 bzw. :8091
Der Proxy muss auf den nginx-Port, nicht auf den Dienst. 8090/8092 sind die nginx-Blöcke mit der Authelia-Prüfung; 8089/8091 wären die Dienste ungeschützt. Wer den Proxy direkt auf 8089 oder 8091 richtet, stellt die Seite ohne Anmeldung ins Netz — und sie schreibt in den Ladeplan und zeigt Verbrauchsdaten.
Was dazugehört: nginx-Blöcke /etc/nginx/sites-available/laden-askai und charge-askai, die Authelia-Regeln für beide Namen, die Vhosts auf der .55 (apache_askai… / apache_charge…) und ein Zertifikat über alle drei Namen, geholt mit certbot --expand, gültig bis 13.01.2027. Die Konfigurationen liegen als Vorlage in ~/energie/, jeweils mit den Einsetzbefehlen im Kopf; eingesetzt werden sie über die umstellung_*.sh-Skripte. Alles davon braucht root, das gibt Jürgen selbst ein.

Zwei Masken, ein Programm

 Nachbar (8089)Haus (8091)
Preisfest 0,35 €/kWhdie echten Viertelstundenpreise
Fenstersuchenachbar_fenster.py — mit Budget 0,34 und Deckel haus_fenster.py — kein Budget, kein Aufschlag, nur billigster Block
AbrechnungScheiben, Monatsabschluss, Rechnungkeine
Fahrzeugfest, über seine KarteAuswahl, vom Chip überstimmt
EilfallSchieber mit AufschlagKnopf „Jetzt laden“, ohne Aufschlag
Sperrefrei, solange seine Karte steckt frei, solange seine Karte nicht steckt

Der Ablauf am Ladepunkt (Nachbar)

SchrittWas passiert
1. Chip erkanntKarte Dietrich → EVCC meldet das Fahrzeug „Leaf E 75kWh“. Phasen 1, Modus smart, Dauerhaft laden an → 6 A, rund 1,4 kW. Zweck ist nicht die Energie, sondern dass er sieht, dass sein Auto lädt.
2. Er trägt einAuf seiner Seite: aktueller Stand, Ziel, fertig bis. Die Felder stehen in dieser Reihenfolge, und ein Ziel unter dem Stand wird abgewiesen.
3. FenstersucheRechnet die benötigte Menge, sucht die passenden Viertelstunden, setzt die Preisgrenze — und schaltet die Grundladung ab, damit die Wartezeit nicht aus dem Hausakku bezahlt wird.
4. Im FensterEVCC lädt dreiphasig mit 11 kW. Der Hausakku wird gesperrt, die Leistung kommt aus dem Netz.
5. Ziel erreichtGrundladung aus, Ladepunkt gesperrt, Eilfreigabe gelöscht.

Die Preisregel des Nachbarn ist ein Durchschnittsbudget

Jürgens Entscheidung vom 29.09.2026, wörtlich: „es sollte über den Verlauf nicht über 0,34 kommen mit echtem Durchschnitt. Diese Fenster zusammen ergeben 0,34 max.“

RegelWertBegründung
Budget0,34 €/kWhMit der Energie gewichtetes Mittel über die ganze Ladung — nicht jede einzelne Viertelstunde
Obergrenze0,45 €/kWhKeine Viertelstunde darüber, damit ein Preisausreißer nicht durchrutscht
Startwert der Grenze0,38 €/kWhNur damit genug Stunden zur Auswahl stehen, bis die Suche gerechnet hat
Angesparter Spielraumlaufend gerechnetSeit 17.10.2026 gebaut. Wurde bisher billiger geladen als 0,34, darf der Rest teurer sein — so weit, dass der Schnitt am Ende trotzdem 0,34 trifft
Aufschlag im Ausnahmefall+0,03 €/kWhWenn er ein zu teures Fenster erzwingt oder sofort lädt

Im Haus gilt davon nichts. Dort gibt es nichts zu verdienen und nichts zu deckeln — die Suche nimmt einfach den billigsten zusammenhängenden Block und zeigt, was er kostet.

Der angesparte Spielraum — gebaut am 17.10.2026

Jürgens Regel vom 29.09. hatte einen zweiten Teil, der vier Wochen offen stand: „Ist aber nur noch eine kurze Zeit und das Auto wäre voll, kann es auch mal höher sein.“ Am 17.10. forderte er ihn ein — „ich will pro Ladung am ende einen schnitt von Max 0,34“ — und so wird er gerechnet:

Spielraum        = 0,34 × bisher geladene kWh − bisherige Einkaufskosten
Budget für Rest  = 0,34 + Spielraum ÷ offene kWh       (gedeckelt auf 0,45)

Alles prüft gegen diesen Wert, nicht mehr gegen die feste Zahl: die Auswahl der Viertelstunden, das Tor und der Preis, den er sieht. Je weniger offen ist, desto mehr darf die letzte Viertelstunde kosten — genau Jürgens Zusatz, nur ausgerechnet statt geschätzt.

Warum es erst jetzt geht. Die Rechnung braucht Preis × kWh je Viertelstunde des bisherigen Verlaufs. EVCCs sessionPricePerKWh taugt dafür nicht — es rechnet den Sonnenanteil mit 0 ct und stand am 12.10. bei 99,5 % Sonne auf 0,0024 €/kWh. Seit dem Umbau der Abrechnung auf den Chipzähler am 16.10. stehen die Zahlen in der Datenbank, also war die Regel baubar.
Was es im Betrieb ausmacht. Am 17.10. um 15:30 brach Dietrichs Ladung ab, weil die laufende Viertelstunde 0,3404 kostete — vier Zehntausendstel über 0,34. Die Frist 15:45 verfiel, es wurde auf den nächsten Morgen umgeplant, von 4,2 kWh waren 1,35 geflossen. Mit dem Spielraum (bisher 7,397 kWh zu 0,3089) hätte das Budget bei 0,4192 gelegen; die Ladung wäre durchgelaufen. Und er zahlt dabei weiter seine 0,35, während der Einkauf darunter bleibt.

Durchgehender Block vor billigster Auswahl

Die Suche nimmt zuerst den billigsten zusammenhängenden Lauf, solange sein Schnitt unter 0,34 bleibt. Die Ränder dürfen teurer sein. Gemessen am 12.10.: herausgepickt 0,3147 gegen durchgehend 0,3171 — 0,24 ct/kWh, auf 66 kWh 16 Cent. Dafür fallen alle Pausen weg, aus vier Blöcken wurde einer.

Wofür der Pausen-Beleg gilt — und wofür nicht. Aus einem systematischen Test (Cupra Tavascan, Enyaq RS): „Nach etwa 3 Minuten Pause schläft ein MEB ein und nimmt die Ladung nicht wieder auf.“ Das sind MEB-Autos, also Jürgens ID.3 — für Dietrichs Leaf ist es nicht geprüft, und am 17.10. hat der Leaf eine Viertelstunde Pause sogar überlebt. Der Grund für durchgehende Blöcke ist Jürgens Regel, nicht ein fremdes Fahrzeug.

Eine laufende Ladung wird nicht unterbrochen

Gebaut am 17.10.2026. Die Blockregel oben greift nur innerhalb eines Laufs, mit der jeweils noch offenen Menge. Sinkt die auf eine Viertelstunde, ist jede für sich ein „durchgehender Block“ — dann gewinnt die billigste, auch wenn sie nicht anschließt. Genau so entstand am 17.10. eine Pause um 14:00: die laufende Viertelstunde kostete 0,3084, die nächste 0,3074, ein Zehntel Cent Unterschied.

Jürgen dazu: „Klar möglichst billig aber dann durch.“ Läuft eine Ladung und gehört die laufende Viertelstunde nicht zur Auswahl, wird sie aufgenommen und die teuerste spätere gestrichen — gleiche Menge, nur die Pause fällt weg. Reißt der Schnitt dabei das Budget, passiert nichts: das Budget hat Vorrang.

Ein noch nicht angelaufener Plan wandert nur für echtes Geld

Gebaut am 23.10.2026, für beide Masken. Bis dahin stand in der Suche ausdrücklich: „solange nichts angelaufen ist, kostet ein Wechsel niemanden etwas.“ Das stimmt für das Laden, aber nicht für den, der auf den Wagen wartet: die angezeigte Startzeit wanderte bei jedem Lauf, auch für ein Zehntel Cent. Jürgens Entwurf:

„Kommen neue Daten und es ergibt sich ein neues Fenster kann es innerhalb der 48h geschoben werden. Außer der User hat eine frühe Zeit angegeben.“

Jetzt gilt: vor dem Anlaufen darf das Fenster wandern, aber nur bei mindestens 1 ct/kWh Vorteil im Schnitt — sonst bleibt der Plan stehen und sagt warum. Ab dem Anlaufen gar nicht mehr. Neue Preise kommen ohnehin nur einmal am Tag; ein echter Vorteil entsteht also höchstens einmal täglich, Rauschen dagegen bei jedem Lauf.

Die drei Schwellen — alle von Jürgen am 23.10.2026

ZahlWofürWo
0,4 ctRauschen beim Vergleich zweier Fenster in der Maske — „0,4 Cent sind okay für das rauschen.“ nachbar_laden.RAUSCHEN_EUR
1 ctein noch nicht angelaufener Plan wird verschoben — „Beim Verschieben nimm 1 cent oder?“ Bei 75 kWh sind das 75 Cent. nachbar_fenster.VERSCHIEBEN_EUR
2 ctDietrich wartet über die 24 h hinaus — „Wenn ich bei Dietrich fast am Minus liege würde es auch nutzen ab 2 Cent.“ noch nicht gebaut
Warum es überhaupt eine Schwelle braucht, obwohl ein nicht angelaufener Plan niemanden unterbricht: Jürgens Begründung war „nicht wegen 1 cent ständiges on off. Man bekommt dann auch immer eine push Nachricht vom auto.“ Vor dem Anlaufen gibt es kein On/Off — die Schwelle schützt nicht das Laden, sondern die angezeigte Zeit.

„Spätestens fertig bis“ — 48 Stunden, bei Dietrich eine Obergrenze

Wer ein Ziel speichert, ohne eine Zeit zu nennen, bekommt die Frist seit dem 23.10. automatisch: 48 Stunden. Eine noch gültige Frist wird dabei nicht überschrieben. Jürgen: „spätestens fertig würde ich auf 48h automatisch setzen. aber man muss es ändern können.“

Der Unterschied zwischen den Masken: Bei Dietrich sind die 48 Stunden eine Obergrenze — „damit er unseren Ladeplatz wieder frei macht“ — seine Eingabe wird darauf gekappt. Im Haus sind sie nur eine Vorbelegung und dürfen länger sein: „Das Auto steht da sowieso.“

48 Stunden sind nie ganz sichtbar, und das gehört in jede Erklärung. Zwölf Tage preise_ankunft.log: die Preise für morgen kommen zwischen 13:20 und 13:50, die Vorschau endet immer mit dem Folgetag. Kurz vor der Ankunft reicht der Blick ~10,5 Stunden, kurz danach ~34. Die Frist ist ein Termin, kein Versprechen auf das beste Fenster von 48 Stunden — der Plan zieht nachträglich um, wenn neue Preise ein um mindestens 1 ct billigeres Fenster zeigen. Genau das ersetzt die E-Mail, über die am 23.10. nachgedacht wurde: „Dann kann das system entscheiden udn wir brauchen kein email.“

Die Hausfenstersuche hielt ihren Plan bis dahin überhaupt nicht fest

Nebenbefund vom 23.10. Die Festhalte-Regel vom 17.10. lebte in nachbar_fenster.py und galt nur für Dietrich; haus_fenster.py wählte jede Viertelstunde frei neu. Jetzt benutzen beide dieselbe Funktion.

Zwei Fallen, die dabei auffielen. (1) Ohne Angabe liest die Festhalte-Funktion den Plan aus der Abrechnungsdatenbank — das ist Dietrichs Plan, nur seine Suche schreibt dort hinein. Die Hausuche muss ihren eigenen übergeben, sonst würde das Hausauto an seinen Zeiten festgehalten; sie legt ihn darum in haus_fenster_stand.json ab. (2) Der Schlüssel „derselbe Plan“ darf nicht das Ende der Preisvorschau sein. Ohne gesetzte Frist plant die Suche bis dorthin, und dieses Ende wandert, sobald mittags die neuen Preise kommen — als Schlüssel wäre es jeden Tag neu, die Regel stiege mit „andere Frist = echte Änderung“ aus, und die 1-ct-Schwelle wäre genau in dem Moment umgangen, für den sie gebaut ist. Schlüssel ist darum der echte Fristzeitpunkt oder das Wort ohne.

Jede Maske hat ihre eigene Fristzeile

Gebaut am 23.10.2026, nachdem beim Gegenlesen auffiel, dass die 48-Stunden-Regel umgehbar war. Es gab genau eine Fristzeile in der Abrechnungsdatenbank — per CHECK-Bedingung erzwungen — und die Spalte „woher“ wurde beim Lesen nicht ausgewertet. Beide Masken schrieben in dasselbe Feld.

Das Szenario, in dem es schiefgeht: Jürgen trägt im Haus „fertig bis 28.10.“ ein — bei ihm absichtlich nicht gekappt. Danach steckt Dietrich seinen Leaf an, ohne selbst etwas einzutragen. Seine Suche liest die Frist und plant über fünf Tage statt über 48 Stunden; sein Wagen blockiert den einzigen Ladepunkt entsprechend länger. Die Kartensperre deckt das nicht: sie verhindert gleichzeitiges Schreiben, nicht das Weiterwirken eines vorher geschriebenen Werts.

Jetzt hat jede Maske ihre Zeile in der Tabelle frist_maske. Die Übernahme der alten Zeile ist absichtlich eng: sie wandert nur dorthin, wo ihre Herkunft hinpasst (maske-haus / maske-nachbar). Eine Zeile fremder oder unbekannter Herkunft wird nicht übernommen — sonst bekäme die eine Maske die Frist der anderen, also genau der Fehler, den die Trennung behebt. Die vorhandene stammte von Dietrichs Maske und ist nur ihm zugeordnet worden.

Dazu wird Dietrichs Frist jetzt beim LESEN gekappt, nicht nur bei der Eingabe. Die getrennten Zeilen schließen den Weg oben schon; die Kappung beim Lesen bleibt als zweite Schranke für alles, was die Zeile sonst noch beschreibt — die HA-Felder, ein Handeingriff in der Datenbank, eine künftige dritte Maske. Das Haus wird nicht gekappt.

Auch die Pläne liegen je Maske in der Datenbank

Jürgen am 23.10.2026: „Sind die Fenster jetzt je app extra in der Datenbank dann kann da nichts vertsellt werden“ — zu dem Zeitpunkt noch nicht: die Fristen ja, die Pläne nein. Dietrichs Pläne lagen in der Datenbank ohne Spalte für die Maske, der Hausplan in einer Datei. Jetzt hat die Tabelle plan die Spalten maske und auto, und die Hausuche schreibt dorthin.

Zwei Dinge, die dabei wichtig waren. (1) Die Spalten mussten nachgerüstet werden, nicht im CREATE TABLE geändert: bei einer vorhandenen Datei tut SQLite dort schlicht nichts, und der Fehler fällt erst auf, wenn eine Spalte fehlt. (2) Alle vorhandenen Zeilen bekommen nachbar — das ist nicht geraten, sondern belegt: nur seine Suche hat je in diese Tabelle geschrieben. Gemessen danach: 46 Zeilen nachbar, 1 Zeile haus.

Die Stand-Datei des Hauses ist damit wieder reine Anzeige. Der Dateiweg war belegt fehleranfällig: sie wird vollständig ersetzt, und jeder frühe Ausstieg der Suche löschte den gemerkten Plan. In der Datenbank mit Nummer, Zeitstempel und Unterschrift kann das nicht passieren.

Ein anderes Auto verwirft den Plan

Jürgens Frage vom 23.10.2026: „was ist wenn die Autos geändert werden? wird die dann zurückgesetzt?“ — vorher nein, und das war eine Lücke. Frist und Plan gehören der Maske, nicht dem Wagen: Jürgens pro (59 kWh) und Kristinas pro S (79 kWh) teilen sie sich. Ein festgehaltener Plan hätte die Menge des einen Autos für das andere gelten lassen.

Der Plan merkt sich jetzt in plan_auto, für welches Fahrzeug er gemacht wurde. Weicht das erkannte Auto ab, ist das eine echte Änderung und es wird neu geplant — keine Preisfrage, also greift auch die 1-ct-Schwelle nicht.

Geprüft wird nur, wenn beide Namen bekannt sind. Steckt nichts am Kabel, meldet EVCC keinen Fahrzeugtitel — daraus darf kein Fahrzeugwechsel werden.

Der Knopf „Plan zurücksetzen“

Jürgen am 23.10.2026: „eventl. machen wir einen Klick zurücksetzen. wenn man falsch geplant hat. nur bei der Eingabe“. Gebraucht wird er, weil die Regeln von diesem Tag absichtlich klebrig sind: ein angelaufener Plan wird festgehalten, ein noch nicht angelaufener erst ab 1 ct verschoben. Das ist richtig für den Betrieb, ließ aber keinen Ausweg außer Ziel oder Zeit zu ändern.

Er steht in beiden Masken direkt unter „Speichern“, als zweitrangiger Knopf mit Rückfrage, und erscheint nur, wenn die eigene Eingabe freigegeben ist — dieselbe Richtungssperre wie beim Speichern, sonst könnte eine Seite den Plan der anderen verwerfen. Zwei Schritte, nur für die eigene Maske:

SchrittWas genau
Fristgeht auf die Vorbelegung, also 48 Stunden ab jetzt — nicht gelöscht. Eine gelöschte Zeile würde beim nächsten Lesen wieder aus den drei HA-Feldern gefüllt, und dann stünde prompt die alte Zeit da. Zurücksetzen heißt zurück auf den Grundzustand, nicht „leer“.
Planwird gestempelt: alles, was vorher geplant wurde, gilt als verworfen. Im Protokoll wird nichts gelöscht — die Tabellen plan/plan_fenster sind ein Nachweis mit Nummer, Zeitstempel und Unterschrift. Danach läuft die eigene Suche sofort.
Gemessen beim ersten Knopfdruck (Hausmaske, 23.10. 13:51): Hausfrist von 24.10. 23:59 auf 25.10. 13:51, Stempel gesetzt, neuer Plan sofort da — und Dietrichs Zeile unberührt.
Ein Fehler, den genau dieser erste Knopfdruck aufgedeckt hat. Der Stempel wurde mit Sekunden geschrieben, verglichen wird aber mit den Zeitstempeln der Pläne — und die sind minutengenau. Im Zeichenvergleich ist "2026-10-23T13:51" kleiner als "2026-10-23T13:51:41", weil die kürzere Zeichenkette zuerst endet: der unmittelbar nach dem Zurücksetzen neu geschriebene Plan las sich damit selbst als verworfen. Behoben am selben Tag — Stempel minutengenau, Vergleich mit < statt <=, damit ein Plan aus derselben Minute überlebt. Regel: Zeitstempel verschiedener Genauigkeit nie als Zeichenketten vergleichen.

Zwei Funde aus dem Gegenlesen vom 23.10.2026

Das Plangedächtnis wurde von jedem frühen Ausstieg gelöscht. Die Stand-Datei wird vollständig ersetzt, und die früh zurückkehrenden Zweige („Der Nachbar lädt gerade“, „Ziel erreicht“, „Sofortladen läuft“, „Frist zu knapp“) gaben keinen Plan mit. Szenario: der Hausplan steht für 22:45, um 18:00 steckt Dietrich an, um 20:00 ist er wieder ab — und das Haus beginnt bei null. Jetzt bleibt der gemerkte Plan stehen, wenn keiner mitgegeben wird.

Die Maske warf den Satz des Servers weg. Im Erfolgsfall zeigte sie hart „Gespeichert.“ Damit waren das Fenster, der Einkaufsschnitt und der Hinweis auf eine gekappte Frist unsichtbar, obwohl der Server diese Sätze ausdrücklich für den Nutzer baut.

Was Dietrich sieht, wenn sein Auto nicht steckt

Gebaut am 23.10.2026 — zwei Fragen, die vorher unbeantwortet blieben

Der Befund, der das ausgelöst hat. Jürgen am 23.10.: „bei Dietrich ist ja die Maske gesperrt. kann er trotzdem wenn ein reingeht sehen wann für ihn ein günstiges Fenster wäre?“ — und auf die Antwort: „leider nur alte sachen.“ Gemessen um 14:10: nachbar_fenster_stand.json war vom 19.10. 12:45, also vier Tage alt, und auf seiner Seite stand trotzdem „Geladen wird heute 13:45-14:00“. Ursache: seine Suche steigt aus, sobald der Leaf nicht am Ladepunkt hängt, und rührt die Datei dann nicht an — das Wort „heute“ war am 19.10. in den Text geschrieben worden und wandert nicht mit. Genau daraus entsteht der Schaden, den Jürgen beschreibt: „So wie es jetzt parken wie die autos um und es dann teuer für ihn.“

Erstens: nichts Altes mehr als aktuell ausgeben

Eine Stand-Datei, die älter als 45 Minuten ist, liefert nie mehr die alten Zeiten, sondern einen ehrlichen Satz: „Gerade ist kein Ladeplan aktiv — der letzte ist vom 19.10. 12:45. Halte deinen Chip an die Wallbox, dann wird neu geplant.“ Gilt für beide Masken. 45 Minuten, weil beide Suchen alle 15 Minuten laufen — zwei ausgefallene Läufe sind damit gedeckt.

Warum eine Zeitangabe veralten kann, ohne sich zu ändern: die Fenster stehen als fertiger Text in der Datei („heute 13:45-14:00“), nicht als Zeitpunkt. Der Text bleibt gleich und wird mit jedem Tag falscher.

Zweitens: die Vorschau mit zwei Mengen

Jürgens Vorschlag: „eventl. 2 Angaben 1x 50% und 80% akku voll laden“. Genau das steht jetzt auf seiner Seite, solange sein Auto nicht steckt:

BisMengeFensterDauerSein Preis
50 %25,2 kWhmorgen 12:45–15:15 2,5 h0,35 €/kWh
80 %50,4 kWhmorgen 11:30–16:15 4,8 h0,35 €/kWh

Gemessen am 23.10.2026 um 15:00. Beides im Budget, weil morgen Mittag die 16-Cent-Stunden liegen.

Gerechnet wird mit derselben Auswahl wie im Echtbetrieb — billigster durchgehender Block, gleiche Obergrenze, gleiche Budgetfrage. Geschrieben wird nichts: kein Tor, keine Preisgrenze, kein Modus, kein Plan, keine Frist. Eine eigene Funktion war nötig, weil die echte Suche als erstes aussteigt, wenn der Leaf nicht am Kabel hängt.

Die Annahme, ohne die es nicht geht — und sie steht auf der Seite. Steckt sein Auto nicht, kennt niemand seinen Ladestand: EVCC meldet vehicleSoc nur am Kabel, und in der Abrechnung steht die geladene Menge, nicht der Stand. Die Vorschau rechnet darum ab 20 % — derselbe Wert, den sein Eingabefeld zeigt, wenn seine Karte nicht steckt. Die Seite sagt es ihm wörtlich: „Angenommen, du kommst mit rund 20 % — Preise sind bis 25.10. 00:00 Uhr bekannt.“ Ladeleistung ohne Auto: 11 kW (Leaf MY26, AC).
Er sieht nur seinen Preis, nach derselben Formel wie im Echtbetrieb: die festen 0,35 €/kWh, wenn der Einkaufsschnitt ins Budget passt, sonst Einkaufsschnitt plus Aufschlag — fertig addiert. Der Einkaufsschnitt selbst wird nicht zurückgegeben; gegengeprüft, in der Antwort steht keine Einkaufszahl. Jürgens Vorgabe vom 12.10.2026 gilt unverändert.

Nur in seiner Maske und nur ohne sein Auto. Steckt es, rechnet seine echte Suche und die Seite zeigt den Plan — ein zweiter Vorschlag daneben wäre verwirrend. Die Hausmaske bekommt das Feld leer.

Was zwei Prüfungen an einem Tag gebracht haben

23.10.2026 · 29 Funde, davon 27 behoben

Beide Masken wurden am 23.10. zweimal von einem beauftragten Prüfer gegengelesen, der nur lesen und messen durfte. Die Funde waren fast alle meine — hier stehen die vier, aus denen etwas zu lernen ist.

Zeitstempel als Zeichenketten vergleichen: dreimal derselbe Fehler

An einem einzigen Tag dreimal: "…T13:51" ist lexikalisch kleiner als "…T13:51:41", weil die kürzere Kette zuerst endet — ein gerade geschriebener Plan las sich damit selbst als verworfen. Dann dasselbe mit verschiedenem Trennzeichen: " " ist kleiner als "T", womit jeder Plan aus der Datenbank als verworfen galt. Und schließlich, vom Prüfer gerechnet, die doppelte Stunde am 25.10.: Zurücksetzen um 02:50 CEST, Plan um 02:15 CET — also 25 Minuten später — hätte sich als 02:15 < 02:50 gelesen.
Gelöst, nicht geflickt: verworfen wird jetzt über die Plannummer. plan.id ist monoton, hat kein Format, keine Zeitzone und keine doppelte Stunde. Regel: Zeitstempel nie als Zeichenketten vergleichen — und wo es ohne Zeit geht, ohne Zeit rechnen.

Eine Sperre im Browser ist keine Sperre

POST /api/zuruecksetzen prüfte die Chipkarte nicht — verborgen wurde der Knopf allein in der Vorlage, und deren Zustand ist bis zu zehn Sekunden alt. Wer die Seite offen hatte, konnte damit den Plan der anderen Maske verwerfen und die gemeinsamen ev_ladeplan_deadline_*-Helfer überschreiben — genau die Felder, über die die Richtungssperre wacht. Jetzt prüft der Server, wie beim Speichern.

Ein Rückfall, der eine Behauptung aufstellt

Fiel ein Cronlauf aus, zeigte die Hausmaske „nach PV-Überschuss (EVCC Smart-Modus)“ — und verdeckte damit genau die Meldung, dass gar kein Plan bekannt ist. Dazu erreichte der Veraltungssatz die Hausmaske nie, weil die Vorlage dort hinweis grundsätzlich nicht zeigt. Ein Rückfalltext darf sagen „ich weiß es nicht“, aber nichts behaupten.

Eine Vorschau muss rechnen wie der Echtbetrieb

Dietrichs Vorschau bewertete den Schnitt des durchgehenden Blocks gegen die feste Budget-Konstante. Die echte Suche bewertet die billigsten Viertelstunden gegen das Budget samt angespartem Spielraum. Beide Abweichungen gingen in dieselbe Richtung: die Vorschau hätte „teuer“ gemeldet, wo die Ladung ihn 0,35 kostet — er wäre nicht umgeparkt, obwohl es sich lohnt. Dasselbe beim Aufrunden: ceil statt round ergab eine Viertelstunde mehr als der spätere echte Plan. Wer einen Vorschlag macht, muss ihn mit demselben Code rechnen, der ihn später ausführt.

Und ein Weg, der ganz wegfiel

Fehlte einer Maske ihre eigene Fristzeile, fiel sie auf die drei gemeinsamen HA-Felder zurück — also auf die Zeit der anderen Maske. Statt das abzusichern, ist der Rückfall weg: keine eigene Zeile heißt keine Frist, dann wird im ganzen Preisblick geplant und das gesagt. Beim ersten Speichern legt die Maske die Zeile mit der 48-Stunden-Vorbelegung an. Damit entfiel auch das Beschreiben der Kachel-Felder beim Zurücksetzen, wo „morgen“ bei 48 Stunden ausnahmslos einen Tag zu früh stand.

Was die Prüfungen ausdrücklich bestätigt haben: In Dietrichs Antwort steht keine einzige Einkaufszahl — feldweise gelesen, auch in der neuen Vorschau; die Plan- und Fristtrennung hält; die Kappung greift auf allen Lesewegen; es entstehen keine doppelten Planzeilen. Eines bleibt offen: eine beschädigte Stand-Datei schweigt ganz statt zu melden — der sichere Rückfall, aber kein schöner.

Die Preisgrenze ist ein Tor, kein Horizont

Der wichtigste Fund des 12.10. smartCostLimit ist ein Schwellenwert, kein Fahrplan. EVCC lädt in jeder Viertelstunde darunter — und zwar chronologisch, nicht in den billigsten. Stand die Grenze auf dem Preis der teuersten gewählten Viertelstunde, nahm EVCC die frühen teuren zuerst und erreichte die billigen des nächsten Tages nie: geplanter Schnitt 0,318, tatsächlicher 0,344.

Darum gibt jeder Lauf nur die laufende Viertelstunde frei. Der Cron läuft */15, also synchron zum Viertelstundenwechsel:

gehört die jetzige Viertelstunde zum Plan → Grenze = ihr Preis + 0,002
sonst                                      → Grenze = 0, Modus off

Fällt der Cron aus, steht die Grenze auf 0 und es wird nicht geladen — die sichere Richtung. Ein hängender hoher Wert würde teuer weiterladen.

Der Fehler, der das Laden über den Plan ganz verhindert hat (17.10.2026). Der Filter verlangte, dass eine Viertelstunde noch nicht begonnen hat. Der Cron startet um :30:00, die Abfrage an EVCC braucht einen Moment — damit ist es :30:0x, und 13:30 galt bereits als Vergangenheit. Nachgemessen: bei :30:00 ist 13:30 wählbar, bei :30:01 nicht mehr. Folge: der Plan begann immer erst mit der nächsten Viertelstunde, die laufende war nie besetzt, die Grenze blieb auf 0,000, der Modus auf off — und alle 15 Minuten rückte der Plan eine Viertelstunde weiter. Die Zeile „Grenze 0.000 — gehört nicht zum Plan“ stand tagelang in jedem Lauf und sah wie ein normaler Wartezustand aus. Richtig ist: eine Viertelstunde zählt, solange sie noch nicht zu Ende ist. Erster echter Ladestart danach: 13:42, 7.050 W.
Die Grenze allein sperrt nicht. Am 12.10. live gemessen: Grenze 0, „Dauerhaft laden“ aus — und der Ladepunkt zog weiter 1,42 kW, PV 0 W, Netz 5 W, also vollständig aus dem Hausakku. Grund: der Akku stand bei 81 % und damit über bufferSoc 80, also sah EVCC Überschuss. Dazu der Code-Fund in core/loadpoint.go: eine einmal akku-gestützt gestartete Sitzung setzt mayDisable = false und hält den Mindeststrom, ohne den SoC neu zu prüfen. Der einzige sofortige Stopp ist Modus off — bei pv läuft EVCCs disable_delay noch 180 Sekunden nach.

Der Schieber für „es ist eilig“ (nur Nachbar)

Auf seiner Seite ein Regler, kein Knopf: er muss ganz nach rechts gezogen und losgelassen werden. Der Text nennt die Folge vorher („Kostet dann 0,48 €/kWh statt 0,35“). Dann Modus now, Phasen frei, Grundladung aus, Budget außer Kraft.

Sein Preis ist dabei Einkauf in dieser Dreiminuten-Scheibe + 3 ct, alle drei Minuten neu. Fällt der Preis während des Eilladens, zahlt er automatisch weniger — und die Marge steht unabhängig vom Nachtpreis fest.

Die Freigabe ist eine Datei mit Ablaufzeit (nachbar_eilig.json, sechs Stunden), kein Schalter. Ein Zustand, der von selbst verfällt, kann nicht vergessen werden. Ist das Ziel erreicht, löscht die Fenstersuche ihn selbst.
Lehre vom 12.10.: Zu jedem Bedienelement, das absichtlich schwer auszulösen ist, gehört ein leicht erreichbarer Gegenschalter. Der Stopp fehlte zuerst, und er stoppte dann nicht, weil die Tor-Logik den Modus now bewusst nie übersteuert — diese Schutzsperre stand dem planmäßigen Verlassen des Zustands im Weg. Eine Sperre muss den Weg kennen, auf dem der geschützte Zustand planmäßig endet.

Die Hausmaske hat diesen Schieber nicht. Dort steht stattdessen ein Knopf (siehe unten) — es gibt keinen Preis zu erzwingen und keinen Aufschlag zu rechtfertigen.

Die Hausmaske im Einzelnen

FeldWie es sich verhält
Welches AutoAuswahl aus input_select.ev_ladeplan_auto, ohne den Eintrag „Nachbar“. Sie ist nur der Rückfall: sobald der Chip an der Box ein Fahrzeug meldet, ist das Feld gesperrt und zeigt, was wirklich steckt. Ein Anzeigefeld schlägt eine Auswahl.
Berichtigt am 22.10.2026 — das Feld zeigte das falsche Auto. Es zeigte den Helfer input_select.ev_ladeplan_auto, und der stand seit dem 18.10. fest auf „ID.3 pro (59 kWh)“: nachgeführt hatte ihn die Automation ev_ladeplan_fahrzeug_folgen, und die wurde beim Aufräumen der alten Ladelogik gelöscht mit dem Vermerk „jetzt Sache der Masken“ — die Masken hatten die Aufgabe aber nie bekommen. Jürgen am 22.10.: der pro S lud, und die Seite schrieb „ID.3 pro“. Jetzt erkennt die Maske das Fahrzeug selbst und zieht den Helfer nach, damit auch der Cron und die HA-Kachel es richtig sehen.
Die Falle beim Bauen: nicht über den Textanfang vergleichen. „ID.3 pro“ ist ein Namensanfang von „ID.3 pro S“, damit hätte der eine den Eintrag des anderen gewählt. Darum wird die Klammer mit der kWh-Zahl abgeschnitten und auf Gleichheit geprüft. Die Akkugröße war nie betroffen — die kommt zuerst aus EVCC.
LadestandMeldet das Auto seinen Stand (vehicleSoc), steht er als Angebot da mit einem Knopf „übernehmen“ — nicht automatisch eingesetzt, denn der gemeldete Wert ist manchmal Stunden alt. Meldet das Auto nichts, Handeingabe.
ZielProzent. Steht neben dem Ladestand in einer Zeile — nur im Haus, beim Nachbarn übereinander.
Fertig bisErscheint erst, wenn man das vorgeschlagene Fenster ablehnt (siehe unten).
ZählerMonatssumme je Auto, aus den EVCC-Sitzungen. „Bisher geladen“ zählt nur die laufende Sitzung.
Die Kartensperre ist im Haus umgekehrt. Beim Nachbarn sind die Felder frei, solange seine Karte steckt. Im Haus sind sie frei, solange seine Karte nicht steckt — wir sollen seine laufende Ladung nicht umplanen können. Der Sperrtext in Jürgens Worten: „…weil dein Auto gerade nicht an der Wallbox steckt. Steck es an und halte deinen Chip dran, dann sind die Felder beschreibbar.“

Erst zeigen, dann fragen — der Vorschau-Ablauf

Von Jürgen entworfen am 16.10. Der alte Weg verlangte zuerst eine Uhrzeit und rechnete danach. Der neue dreht das um:

POST /api/vorschau   rechnet ZWEIMAL: bestes Fenster ohne Frist, und mit seiner Frist
                     schreibt NICHTS in den Ladeplan

Die Seite zeigt daraufhin einen grünen Kasten: „Günstigstes Fenster … Passt das?“ mit zwei Knöpfen — [Passt] speichert sofort, [Andere Zeit] klappt erst dann „Fertig bis“ auf und nennt beim Eintragen unmittelbar den Mehrpreis gegenüber dem besten Fenster.

Der Mehrpreis wird nur bei gleicher Menge genannt. Zwei Fenster, die unterschiedlich viel laden, haben keine vergleichbare Differenz — dann steht die Zahl nicht da. Ebenso gilt: Auf eine Frage gibt es eine Zahl. Als Seite und Suche einmal 40 % und 42 % nebeneinander zeigten, war das ein Fehler, nicht Information — im Haus gilt das Ergebnis der Suche.
Warnung ab 0,40 €/kWh (WARN_PREIS), serverseitig gerechnet: der Kasten nennt dann das bessere Fenster und endet mit „Wenn du das Auto brauchst, lass es so.“ — keine Sperre. Es ist unser eigenes Auto; die Entscheidung bleibt beim Menschen.

Die Hausfenstersuche

haus_fenster.py, Cron */15, Zustand in haus_fenster_stand.json. Preisquelle ist EVCCs forecast.grid — 192 Viertelstunden im 15-Minuten-Raster. Die Vorschau reicht bis heute 23:45; die Preise des nächsten Tages kommen mittags.

Übernommen aus Dietrichs Suche, nach Jürgens Rüge „warum hast du das nicht von Dietrich übernommen“:

FallWas die Seite sagt
Kein Termin gesetztEs wird über die ganze Vorschau geplant, nicht bis irgendeiner Uhrzeit
Nichts passt„keine Viertelstunde brauchbar“
Zeit reicht mehr als nötig„Lass dir Zeit bis …, dann ist es voll“
Zeit reicht nicht„In der Zeit kommt es auf rund X %“
Frist hinter der Vorschauausdrücklicher Hinweis, dass dafür noch keine Preise vorliegen

Der Knopf „Jetzt laden“

POST /api/jetzt und /api/jetzt_stopp, Freigabe in haus_jetzt.json mit sechs Stunden Ablauf. Der Knopf setzt den EVCC-Modus auf now und gibt die Phasen frei. Daneben steht ein roter Stoppknopf; läuft die Freigabe ab, stellt die Fenstersuche selbst auf smart zurück — aber nur, wenn die Datei auch von uns stammt.

Belegt, nicht vermutet: Den Hausakku schützt dabei die Automation hausakku_schutz_evcc_schnellladen. Gelesen, nicht am Namen geraten: sie löst auch auf Modus → now aus (nicht nur auf die Preisfreigabe), prüft Anstecken und den Victron-Mindest-SoC und hebt diesen dann an — der Akku ist gesperrt, die Leistung kommt aus dem Netz. Sie ist fahrzeugneutral; am 30.09. mit Kristinas pro S gemessen: 11,39 kW aus dem Netz. Nicht zu verwechseln mit nachbar_phasen_folgen_dem_preis — die hängt am Leaf.
Noch nicht live erprobt. Der Knopf ist gebaut und die Automation belegt, aber eine echte Ladung darüber ist noch nicht gelaufen. Gesperrt ist er, solange Dietrichs Karte steckt.

Der Ladeverlust: 12 %

Zwischen Zähler und Akku geht Energie verloren — Ladegerät, Kabel, Klimatisierung der Batterie. Grundlage ist die DTU-Messung aus Dänemark (Sevdari et al. 2023, 38 Fahrzeuge): ID.3 Pro bei 11,3 kW 90,95 % Wirkungsgrad, bei 4,18 kW nur 87,43 % — dazu ein Sockel von rund 250 W, der bei kleiner Leistung stärker ins Gewicht fällt.

Auf Jürgens Wort steht VERLUST = 0.12 in allen drei Dateien (nachbar_laden.py, nachbar_fenster.py, haus_fenster.py). Er wirkt nur auf Zeit- und Mengenschätzung, nicht auf eine Abrechnung — abgerechnet wird der Zähler. Beispiele: Dietrichs Leaf 1 → 90 % ergibt 50,4 kWh und 4,6 h; die pro S von 1 auf 90 % 78,7 kWh und 7,2 h.

Abrechnung: Scheiben als Nachweis, Abschluss als Rechnung (nur Nachbar)

~/energie/nachbar_ladungen.db, SQLite. Warum nicht MariaDB: ~/bin/nas_sicherung.sh zieht energie/*.db nachts sauber per sqlite3.backup() aufs NAS und außer Haus — MariaDB fasst keine Sicherung an. Entschieden hat die Sicherung, nicht die Technik.

TabelleInhalt
abrechnungeine Zeile je Viertelstunde (bis 16.10.: je drei Minuten): von, bis, Kennung, kWh, sein Preis, Betrag, Zählerstand, Netz-kWh, Haus-kWh, Einkauf, Hausanteil, chip_wh. Die Menge kommt aus dem Chipzähler der Wallbox (cards[3].energy), nicht mehr aus EVCC
fristeine Zeile: Dietrichs Termin als echter Zeitpunkt. Seit 17.10. — vorher standen Tag/Stunde/Minute als Wörter in Home Assistant, und „morgen 09:00“ wanderte um Mitternacht einen Tag weiter
zaehlereine Zeile: der Nullpunkt des Ladezählers im Moment der Eingabe, dazu der eingetragene Ladestand
plan / plan_fensterjede Planänderung mit ihren Viertelstunden — was war geplant, als Gegenstück zu was ist geflossen
monatsabschlussdie eingefrorene Rechnung je Monat, Lauf am 1. um 00:30

Warum Scheiben und nicht eine Zeile je Ladung: Der Preis gilt je Viertelstunde, und der Plan kann mitten in der Ladung geändert werden. Mit Scheiben steht er fest, sobald die Energie geflossen ist — eine spätere Planänderung kann einen berechneten Preis nicht mehr rückwirkend ändern.

Warum der Monat eingefroren wird: Käme später eine Scheibe dazu, stimmte eine bezahlte Rechnung nicht mehr. Der Abschluss ist die Rechnung, die Scheiben sind der Nachweis.

Was die Datenbank festhält, damit es nicht wandert (17.10.2026)

Drei Dinge standen vorher nur in Home Assistant oder wurden jedes Mal neu gerechnet — und wanderten deshalb. Alle drei liegen jetzt als Zahl mit Zeitpunkt in der Abrechnungs- datenbank.

WasDas Problem vorherJetzt
Die FristTag als Wort („heute“/„morgen“) plus Stunde und Minute. Um Mitternacht hieß „morgen 09:00“ plötzlich einen Tag später. Belegt: Plan am 16.10. 18:14 hielt die Frist 17.10. 09:00, der Plan um 17.10. 00:00:01 daraus 18.10. 09:00 echter Zeitpunkt in frist, bei jedem Speichern neu geschrieben. Die HA-Felder sind nur noch Anzeige
Der Nullpunkt des ZählersEVCCs chargedEnergy zählt ab dem Anstecken und wird erst beim Abstecken genullt. Nach einer gelungenen Ladung war darum jeder neue Wunsch sofort „Ziel erreicht“ — gemessen: Wunsch 4,20 kWh, abgezogen 6,00 kWh aus der Ladung davorder Zählerstand wird beim Eintragen gemerkt (zaehler). Abgezogen wird nur, was seit der Eingabe dazukam. Jürgen dazu: „ich setze ja auch die soc werte neu“ — der eingetragene Ladestand ist der Nullpunkt
Der angelaufene PlanDie Suche wählte alle 15 Minuten neu. Ein halbes Zehntel Cent (0,30310 gegen 0,30315) reichte, damit der Start eine Viertelstunde nach hinten rutschte — und beim nächsten Lauf wiederläuft die erste Viertelstunde schon oder beginnt sie in der nächsten, bleibt der Plan stehen. Eine neue Zeiteingabe verwirft ihn, denn das ist eine echte Änderung. Jürgen: „es soll nicht verschoben werden. Man will das es dann geladen hat“
Eine Zeile für beide Seiten ist gewollt — Haus und Nachbar laden an derselben Wallbox, EVCC führt dafür einen Ladepunkt und damit einen Sitzungszähler. Der Nullpunkt gehört zum Zähler, nicht zur Person.

Der Hausanteil

Während seiner Ladung ist der Hausakku gesperrt, also zieht auch das Haus sein Netz. Gemessen, nicht geschätzt:

netz_kwh   = Differenz der drei Zähler "ac_in_1_zu_ac_out"  (Netz in die LASTEN)
haus_kwh   = max(0, netz_kwh − kWh des Autos)
hausanteil = haus_kwh × max(0, Einkaufspreis − 0,20)

Die 0,20 €/kWh trägt Jürgen selbst, der Rest geht auf die Rechnung des Nachbarn. Deckt die PV das Haus, ist der Wert von selbst null — es braucht keine Sonnenabfrage und keine Jahreszeitregel. Läuft das Auto nicht, wird nichts angerechnet.

Gemessen im Fenster 10:30–16:30 über sieben Tage: Haus 878 W im Mittel, also 5,3 kWh in sechs Stunden; PV im selben Fenster 3.073 W, Netzbezug an fünf von sieben Tagen nahe null. Der schlechte Fall ist die Winternacht: 5,3 kWh zu 0,317 sind 1,68 €, davon 1,06 € Jürgen und 0,62 € der Nachbar — das verdoppelt den Gewinn der schlechten Nacht von 0,59 auf 1,21 €.

Was er sieht und was nicht

SichtbarNicht sichtbar
Zustand, Leistung, bisher geladenEinkaufspreis, Marge, Hausanteil
Sein Preis (0,35, rot wenn höher)Budget, Aufschlag, Preisgrenze
Ladefenster in Blöcken, laufender Monat, Vormonat, alle Monate mit Jahressumme Bezahlt-Status — den setzt Jürgen in seiner eigenen Oberfläche
Regel für alles, was er aufrufen kann: Kommentare, Feldnamen und JSON-Schlüssel sind öffentlich. Begründungen gehören auf die Serverseite. Der Hausanteil stand zuerst auch im JSON von /api/stand — lesbar für jeden, der die Seite aufmacht. Schreibwege gibt es genau drei; unbekannte POST-Pfade werden abgewiesen.

Dieselbe Trennung gilt zwischen den beiden Masken: die Hausmaske antwortet auf /api/jetzt, die Nachbarmaske mit 404, und umgekehrt beim Eilschieber. Was eine Betriebsart nicht haben soll, ist dort nicht abgeschaltet, sondern nicht vorhanden.

Der Hausakku

Stand 23.10.2026 — die Tabelle unten ist Geschichte, nicht Gegenwart. Am 18.10. um 20:45 wurden drei der vier Schutzstücke abgeschaltet und um 21:41 dauerhaft festgeschrieben, auf Jürgens Wort: „wir nutzen nur noch die Lösung über evcc und DESS.“ Der sachliche Grund war die PV-Drosselung durch den angehobenen Mindest-SoC, rund 1 kWh an einem Nachmittag. Die Annahme dahinter ist am 22.10. widerlegt worden: DESS stand auf Eigenverbrauch, und der Hausakku lief von 78 auf 48 % ins Auto, bei 11,4 kW. Am 23.10. ist der Einfrier-Weg über DESS wieder scharf, umgebaut und dauerhaft — der Mindest-SoC-Weg und die Notbremse bleiben aus. Die ganze Kette samt Messungen steht auf Dynamic ESS verstehen und steuern.

Die Maske sagt jetzt selbst, woher der Ladestrom kommt

Gebaut am 23.10.2026, nur in der Hausmaske. Am 22.10. lud der ID.3 pro S mit 11,4 kW aus dem Hausakku, und auf der Seite stand davon kein Wort — aufgefallen ist es am Akkustand, nicht an der Maske. Jetzt steht unter den Zahlen einer von diesen Sätzen, gelb im Normalfall und rot bei Abgabe ins Auto:

LageWas dasteht
Akku gibt ab, über 500 W„Der Hausakku gibt gerade 13,0 kW ab, während das Auto mit 11,4 kW lädt. Stand 27 %.“ — rot
von uns eingefroren„Der Hausakku ist stillgelegt (von uns eingefroren) — der Ladestrom kommt aus dem Netz.“
von DESS selbst gehaltendasselbe mit „(von DESS selbst)“
Akku gibt ab, 150 bis 500 W„Der Hausakku gibt 450 W ab, während das Auto lädt — wenig, aber nicht nichts. Stand 44 %.“
Akku lädt, Auto lädt„Der Hausakku lädt mit 4,0 kW, während das Auto lädt — die Ladung kommt aus PV und Netz.“ Der schönste Fall: die Sonne reicht für beides.
Akku steht„Der Hausakku steht bei 27 % und −80 W — die Ladung kommt aus PV und Netz.“
Warum nicht „liefert X kW ins Auto“. So stand es zuerst hier und im Code, und es war falsch zugeordnet: die Zahl ist die DC-Abgabe des Akkus, nicht der Anteil, der ins Auto geht. Läuft die Wärmepumpe mit, gehört sie nicht dem Auto — und eine Einzelverbrauchermessung gibt es nicht, nur die Summe über drei Phasen. Darum nennt der Satz jetzt beide Zahlen und lässt die Zuordnung dem Leser. Die Warnung bleibt dieselbe.
Und warum es den Mittelfall gibt: die rote Schwelle liegt bei −500 W, damit sie dieselbe ist wie in der HA-Notbremse. Darunter sagte die Seite „der Hausakku steht bei 61 % und −450 W“ — sie widersprach sich im eigenen Satz, bei fast einer halben Kilowattstunde je Stunde. Ab −150 W wird das jetzt gesagt, ohne Alarm.

Dazu, wenn der DESS-Fahrplan es hergibt, eine Zeile „DESS hält den Akku ohnehin still: 14:00–15:30 — dort braucht eine Ladung keinen Eingriff“. Die kommt aus ~/energie/dess_plan.py, das den Fahrplan zwölf Stunden voraus liest.

Nur Fenster, die noch nicht vorbei sind. Der erste Platz des Fahrplans liegt regelmäßig in der Vergangenheit — gemessen 20 bis 21 Minuten, weil VRM die Reihe als Block nur etwa halbstündlich schreibt. Ungefiltert hätte dort „DESS hält den Akku ohnehin still: 09:00–09:30“ gestanden, während es 12:35 war; wer sich darauf verlässt, lädt ohne Schutz aus dem Hausakku.
Die Seite prüft die Wirkung, nicht den Plan. Ein Merker „eingefroren“ allein wäre kein Beleg: am 22.10. zeigte number.multi_rs_6000_3p_ess_mindest_soc 73 %, während der Akku bis 48 % durchlief — die Zahl stand im falschen Register. Entschieden wird darum an der DC-Batterieleistung; Merker und Strategie erklären nur das Warum. Vorzeichenfalle: der Victron-Sensor ist positiv beim Laden, sensor.evcc_battery_power hat das umgekehrte Vorzeichen — am 23.10. um 11:30 gleichzeitig gemessen: +1703 W gegen −1681 W für denselben Vorgang.

Dietrich sieht davon nichts. Der Hausakku geht ihn nichts an, und handeln könnte er daran ohnehin nicht — seine Maske bekommt das Feld leer.

Wie der Schutz früher aussah (bis 18.10.2026)

SchutzWie
Beim Netzladenhausakku_schutz_evcc_schnellladen hebt den Victron-Mindest-SoC auf Stand+1, drosselt auf 10 A und stuft nach der Netzbestätigung auf 16 A hoch. Hört auf smart_cost_active und Modus now — der Schieber und der Haus-Knopf brauchen deshalb nichts Eigenes.
Puffer~/energie/hausakku_puffer.py, stündlich: Sonnenvorschau ≥ 20 kWh → Puffer 80, < 15 kWh → 90, dazwischen bleibt es stehen. Ersetzt die saisonale Automation, die darum aus ist.
Nach dem LadenDer MinSoC-Wachhund gibt selbst auf 20 frei. DESS dagegen lief nach: VRMs dynamischer Soll-SoC blieb auf 76 bei SoC 75 stehen, der Akku gab nur 74 W Leerlauf und das Haus lief 30 Minuten mit 425–500 W am Netz.
Die Lösung (Jürgen, 12.10.): „das einfachste ist es auf off zu setzen und dann wieder on.“ Die Freigabe schaltet den DESS-Modus kurz auf off und nach fünf Sekunden auf den vorher gelesenen Wert zurück. Das verwirft den laufenden Zeitplan und erzwingt eine Neuplanung bei VRM.
Gemessen: Soll-SoC fiel binnen 70 Sekunden von 76 auf 0. Im Echtbetrieb danach: Ladung 11,4 kW gestoppt um 22:54:32, 20 Sekunden später 0 W und der Akku trug das Haus, Netz zwischen −5 und +9 W über sechs Messungen.

Der Netzsollwert — geprüft und verworfen

Naheliegende Idee: den Netzsollwert auf die Ladeleistung des Autos setzen, dann deckt der Akku das Haus und das Netz das Auto — der Hausanteil entfiele ganz. Physikalisch richtig: der Sollwert legt den gesamten Netzfluss fest, der Akku gleicht die Differenz aus, und kann er nicht liefern, nimmt der Wechselrichter mehr aus dem Netz. Das Haus kann man damit nicht aushungern.

Auf dieser Anlage greift er nicht. Jürgen hat es am 12.10. gemessen: Sollwert im Multi RS auf 500 gesetzt → kein Netzbezug. Grund: die Einheiten laufen im Modus self_consumption, in dem der GX seinen Sollwert selbst rechnet und einen externen ignoriert. Umstellen ließe sich das nur am Ekrano oder in VRM — die beiden HA-Auswahlfelder dafür sind unavailable. Damit ist auch erklärt, warum das MinSoC-Konzept am 07.09. den Sollwert-Ansatz ersetzt hat.
Nebenbefund: number.multi_rs_6000_3p_ac_leistungssollwert zeigte 500, das Gerät dagegen 0. Die HA-Zahl ist ein Schreibfeld mit dem letzten geschriebenen Wert — auch eine Zahl ist kein Beleg.

Woher die Mengen je Karte kommen

Die Seite Ladungen je Karte liest seit dem 16.10. EVCCs eigene Datenbankdatei /opt/evcc/data/evcc.db, nur lesend, statt der Schnittstelle. Grund: die Schnittstelle gibt nur die neuesten 100 Sitzungen heraus — die Datei hatte 102. Zwei Ladungen wären still verschwunden. Der Weg über die Schnittstelle bleibt als Rückfall, mit diesem Hinweis im Protokoll.

WasQuelleHängt ab von
Mengen je Karte, seit 17.06.EVCCs Datenbankdateinur der Datei
Abrechnung in Dreiminuten-Scheibennachbar_ladungen.dbunserem Cron
Lebenszähler der Boxgo-eder Box im Netz
Warum die Scheiben trotzdem nötig sind: EVCC speichert eine Zeile je Ladung mit einem Durchschnittspreis. Eine Ladung über 24 Stunden läuft über rund hundert Viertelstundenpreise — welche kWh wann floss, steht dort nicht und ist rückwirkend nicht herstellbar, nur mitschreibend. Dazu rechnet EVCCs price_per_kwh die Sonne mit 0 ct ein und ist damit für eine Abrechnung untauglich.

Dateien und Zeitpläne

DateiAufgabeLauf
nachbar_laden.py --modus nachbarNachbarmaske, Port 8089 systemd nachbar-laden (seit 17.10.2026)
nachbar_laden.py --modus hausHausmaske, Port 8091 systemd haus-laden (seit 17.10.2026)
nachbar_fenster.pyFenstersuche Nachbar: Tor, Grundladung, Preis*/15
haus_fenster.pyFenstersuche Haus, Rückfall aus „Jetzt laden“*/15
nachbar_ladungen.pyAbrechnung, Plan, Frist, Nullpunkt, Monatsabschluss*/15 mit 20 s Versatz (seit dem Umbau auf Viertelstunden am 16.10.; vorher */3), Abschluss am 1. um 00:30
ladungen_build.pySeite + PDF „Ladungen je Karte“täglich 6:45
hausakku_puffer.pyPuffer nach Sonnenvorschaustündlich :17
dess_plan.py neu 23.10.liest den DESS-Fahrplan zwölf Stunden voraus und legt ihn als JSON ab; nur lesend, schreibt nichts an den Victron. Die Hausmaske nimmt daraus die Zeile „DESS hält den Akku ohnehin still“. 2,17,32,47 für die JSON-Datei, :05 zusätzlich mit --csv für die Mitschrift
Warum dess_plan.py zweigeteilt ist. Der Sammler braucht paho-mqtt und läuft nur mit ~/.venvs/mqtt/bin/python; die Masken laufen mit dem System-Python, und dort fehlt das Paket. Darum wird paho erst innerhalb der Sammelfunktion importiert, und die Masken lesen nur die JSON-Datei mit der Standardbibliothek. Ist sie älter als 30 Minuten, gilt sie als unbrauchbar und die Maske zeigt die Zeile gar nicht — lieber nichts anbieten als ein Fenster von gestern.

Die beiden Masken sind Dienste, kein Cron-Wächter mehr (17.10.2026)

Vorher hielt Cron sie am Leben: alle fünf Minuten pgrep || starten. Das deckte Neustart und Absturz ab — aber mit bis zu fünf Minuten Lücke, und ein Absturz blieb genauso lange unbemerkt. Jürgen am 17.10.: „warum müssen die so laufen? wenn wir mal neu starten geht das nicht mehr?“

DienstWas
nachbar-ladenNachbarmaske, Port 8089
haus-ladenHausmaske, Port 8091

Vorbild war der schon laufende austausch.service, gleiche Härtung (ProtectSystem=strict, ProtectHome=read-only, ReadWritePaths=/home/juergen/energie), dazu Restart=always mit fünf Sekunden. Die Quelldateien liegen in ~/energie/, die Kopien in /etc/systemd/system/. Neustart nach einer Programmänderung:

sudo systemctl restart nachbar-laden haus-laden
Die alte Regel gilt weiter, wenn doch einmal per Hand beendet wird. pkill -f auf den Dienstnamen trifft die eigene Shell mit — am 16.10. hat es beide Masken auf einmal getötet, obwohl die Regel seit dem 12.10. hier stand. Darum nur per PID aus einem verankerten pgrep (^/usr/bin/python3 …nachbar_laden\.py --modus haus$). Im Normalfall macht das systemctl und die Frage stellt sich nicht.
Zwei Fallen beim Ändern: nachbar_laden.py importiert die Fenstersuche und hält das Modul fest — nach einer Änderung an haus_fenster.py oder nachbar_fenster.py muss die Maske neu gestartet werden (sudo systemctl restart nachbar-laden haus-laden), sonst rechnet sie mit der Fassung von ihrem Start. Am 17.10. genau so passiert: die Masken liefen seit 12:47, die Änderungen reichten bis 16:52 — die Seiten hätten den Nullpunkt des Ladezählers nicht gesetzt. Die Fenstersuchen aus dem Zeitplan sind nicht betroffen, die starten bei jedem Lauf neu. Und Änderungen am Aussehen erscheinen erst beim Neuladen der Seite, nicht beim nächsten Zustandsabruf. Steht ein alter Prozess noch auf dem Port, stirbt der neue still ins Log.

Offene Punkte

Die gemeinsame Todo für alles steht auf docs.mttec.de/claude-code/#todo — dort sind diese Punkte am 21.10.2026 im Abschnitt „Lademasken“ eingetragen, damit nicht zwei Listen auseinanderlaufen. Hier stehen die Einzelheiten, dort der Zusammenhang mit dem übrigen Energie-Projekt. Nachgemessen am 21.10.: beide Dienste active, 8089/8091 je HTTP 200, laden.askai.de und charge.askai.de von außen je 302 auf die Anmeldung — im Betrieb ist nichts offen.
PunktStand
Dietrichs Maske bequemerVorschlag vom 18.10., gebaut ist nichts: Fenster ohne Datum (24 h ab dem Eintragen, eine Alternative jenseits der 24 h wird angeboten statt heimlich genommen) und der Hinweis „gegen 13:30 kommen die Preise für morgen“, die Zeit aus preise_ankunft.log statt eingetippt. ⚠ Wartet auf eine Entscheidung: ab welchem Preisvorteil soll er über die 24 h hinaus warten? Vorschlag 2 ct/kWh auf die Restmenge. Der Preisblick endet immer mit dem Folgetag — kürzester Fall ~10 h, längster ~34,5 h
Fenstersuche fürs Hausautofehlt ganz — der ID.3 wird nicht automatisch geplant, nur Dietrichs Leaf. Sie darf das Tor nur anfassen, solange der Leaf nicht steckt; zwei Schreiber auf einem Feld waren der Fehler vom 12.10.
Zwei Dateien unatomarnachbar_eilig.json und haus_jetzt.json werden weiter ohne den atomaren Helfer geschrieben (nachbar_fenster.json_ablegen() liegt daneben). Am 21.10. im Quelltext nachgeprüft, gilt weiter
Kristinas Anmeldungfehlt noch in Authelia. Das Passwort wird interaktiv gehasht (docker exec -it authelia authelia crypto hash generate argon2) — nie über --password, das stünde in der Prozessliste. Nachgeprüft 21.10.2026: in users_database.yml stehen weiterhin nur juergen und dietrich
„Jetzt laden“ livegebaut und die Automation belegt, eine echte Ladung darüber ist noch nicht gelaufen
Dietrichs Testdatendie EVCC-Sitzungen vom 30.09. und 12.10. sind Tests (5,41 und 4,89 kWh) und werden gelöscht. ⚠ Die vier Scheiben vom 17.10. sind echt (zusammen 10,1 kWh, erste Ladungen über den Plan) — die bleiben
MariaDB ohne Sicherungtibber_preis (0,2 MB) hat niemanden im Rücken; ein mysqldump in der Nachtsicherung wäre drei Zeilen
Die Hausseite legt keine Pläne abdort gibt es also nichts festzuhalten — Ungleichheit zur Nachbarseite, offen seit 17.10.
Decke für „nicht unterbrechen“ im Haussteht auf der Notbremse 0,60; Jürgens Hausgrenze 0,40 liegt in der Maske, nicht in der Suche
Die HA-Kachel ist nur noch Anzeigeund ab übermorgen verlustbehaftet (alles wird „morgen“). Das muss dort sichtbar werden, sonst ist es eine Falle
Zweite 02:30 des 25.10.die doppelte Stunde der Zeitumstellung ist in der Maske nicht eingebbar
Freigabe feuert mehrfachfünf Auslöser, der erste leert die Slot-Sicherung, jeder weitere nullt Slot 0. Ein Merker wie ev_minsoc_erhoeht würde es auf einen Lauf begrenzen — vorgeschlagen, nicht gebaut
DESS-off/onein bestandener Lauf. Vor „gelöst“ zwei- bis dreimal wiederholen, davon einmal mit Abstecken statt Knopf
Bezahlt-Oberflächenachbar_ladungen.py --bezahlt 2026-10 vorhanden, Maske fehlt
Umgezogen am 15.10.2026: von laden.lavolt.de auf laden.askai.de. Grund: lavolt.de ist abgemeldet, und askai.de liegt seit demselben Tag bei Hetzner. Geändert wurden der nginx-Block auf dem apps-server, die beiden Authelia-Einträge (Regel und Portal), die Mailadresse des Nachbarn im Benutzerverzeichnis sowie Vhost und Zertifikat auf der .55. Das alte Zertifikat und der alte Vhost sind gelöscht. Am 16.10. kam charge.askai.de hinzu; das Zertifikat deckt mit certbot --expand jetzt alle drei Namen ab, gültig bis 13.01.2027.