# Die Solar-Übersicht (Energiefluss) Die Karte „Realtime“ auf der Solar-Seite: Wo kommt der Strom her, wo geht er hin, wie viel ist es gerade. Sie ist kein gezeichnetes Bild, sondern ein **Katalog plus ein Renderer** — und genau diese Trennung macht sie erweiterbar. ![Die Übersicht](bilder/uebersicht.png) | Datei | Aufgabe | |---|---| | `js/solar/energieflussKatalog.js` | **Was** es gibt: jeder Kreis genau einmal — Name, Symbol, Farbe, Elternkreis, Lage, Ringskala, Klickziel und `lesen()` | | `js/solar/energiefluss.js` | **Wie** gezeichnet wird: Kreise, Ringe, Flüsse, Symbole, Plaketten, Hochkant, Bearbeitungsmodus. Kennt keinen Kreis beim Namen | | `restricted/energiefluss.php` | speichert **Abweichungen** je Haus (`homeMesh.energiefluss`) | | `js/solar/solarMQTT.js` | hängt beides an den MQTT-Strom und an die Reiter | --- ## 1. Der Aufbau: Kreise und ihre Eltern Jeder Kreis nennt seinen **Elternkreis**; daraus entsteht der Fluss zwischen beiden. Es gibt keine getrennte Liste von Verbindungen — die Struktur ist die Elternbeziehung. ```mermaid flowchart LR PV["Sonne
pv"] --> WR NETZ["Netz
netz"] --> WR AKKU["Batterie
akku"] --> WR(("Autarkie
wr · Pflicht")) WR --> VERB(("Verbrauch
verbrauch · Pflicht")) VERB --> OG["Obergeschoss"] --> WBOG["Wallbox OG"] VERB --> EG["Erdgeschoss"] --> WBEG["Wallbox EG"] VERB --> UG["Untergeschoss"] VERB --> HEIZ["Heizstab"] BEW["Bewässerung
frei stehend"] ``` Ein Eintrag im Katalog sieht so aus: ```js { id: "heizstab", name: "Heizstab", symbol: "speicher", farbe: "#c2614e", eltern: "verbrauch", // woher der Fluss kommt x: 1120, y: 380, // Lage in der Queransicht hochX: 670, hochY: 600, // optional: eigene Lage hochkant skala: 6000, // W, bei denen der Ring voll ist klick: { modal: "heater" }, // Ziel beim Antippen lesen(m, opt) { // aus MQTT-Werten wird ein Zustand const w = m.zahl("solarManager/pHeat"); return { leistung: w, fluss: w, ruhe: w < 20, neben: "…", zeichen: [...], zustand: { an: w >= 20, temps: [...] } }; }, } ``` Was `lesen()` zurückgeben darf: | Feld | Wirkung | |---|---| | `leistung` | die große Zahl und der Ring | | `wert` | Text **statt** der Leistung (z. B. „93 %“) | | `neben` | die kleine Zeile darunter | | `segmente` | `[{w, farbe, knoten?}]` statt eines einfarbigen Rings | | `fluss` | Watt vom Elternkreis hierher; negativ = Gegenrichtung | | `ruhe` | Kreis blass zeichnen — es ist nichts los | | `zeichen` | Plaketten am Rand: `[{art, text?, titel?}]` | | `zustand` | was das Symbol braucht: `{an, stand, laden, temps}` | > **Ein neuer Kreis ist ein neuer Eintrag hier — sonst nichts.** Kein > Markup, keine CSS-Regel, keine Zeile im Renderer. Nur ein neues *Symbol* > braucht einen Eintrag in `SYMBOLE` (`energiefluss.js`). ### Abweichungen je Haus Der Katalog ist der Normalfall, `homeMesh.energiefluss` hält nur, was davon abweicht: Kreis aus, anderer Name, andere Lage, andere Optionen. Eingestellt wird das unter **Einstellungen → Übersicht** (→ [einstellungen.md](einstellungen.md#3-übersicht-energiefluss)). Ist ein Kreis **aus**, rückt sein Kind automatisch an den nächsten eingeschalteten Vorfahren: `elternWirksam` sucht die Kette hinauf. Und ein Ringsegment, das zu einem ausgeschalteten Kreis gehört, fällt in das letzte verbleibende Segment — die Summe bleibt also richtig, auch wenn jemand die Wallbox ausblendet. --- ## 2. Die Leistungsvisualisierung Drei Dinge zeigen dieselbe Leistung auf drei Arten. Das ist Absicht: Die Zahl ist genau, der Ring vergleicht, der Fluss zeigt die Richtung. ```mermaid flowchart TB W["Leistung in Watt
aus lesen()"] --> Z["Zahl im Kreis
W bis 999, danach kW"] W --> R["Ring
Anteil an skala, gestaucht"] W --> F["Fluss zum Elternkreis
Punktgröße + Tempo"] W --> S["Symbolzustand
an/aus, Füllstand, Temperaturen"] ``` ### 2.1 Der Ring — gestauchte Skala Linear wäre der Ring unbrauchbar: Bei `skala = 15000` wären 300 W ein Strich von zwei Grad, und genau in diesem Bereich steht die Anlage die meiste Zeit. ``` Anteil = log10(1 + 9 · w/skala) w/skala 0 % 10 % 25 % 50 % 100 % Ring 0 % 28 % 47 % 70 % 100 % ``` Der volle Ausschlag bleibt exakt bei `skala`, aber das untere Zehntel bekommt knapp ein Drittel des Rings. **Mehrere Segmente** (etwa Verbrauch = Etagen + Heizstab + Rest) werden über die **Summe** gerechnet und nicht je Segment — die gestauchte Skala ist nicht additiv, sonst reichten drei Viertel-Segmente weiter als ein volles: ``` Segment i = ringAnteil(Σ bis i) − ringAnteil(Σ bis i−1) ``` Zwischen zwei Segmenten bleibt eine Lücke von 0,6 %; Segmente unter 0,4 % verschwinden ganz, sonst stünde dort ein Punkt statt eines Bogens. ### 2.2 Der Fluss — Punkte statt Linie Ein Fluss ist ein Bogen vom Eltern- zum Kindkreis (`flussPfad()`): senkrecht los, dann waagerecht hinein — hochkant umgekehrt. Gezeichnet wird er als gestrichelte Linie mit runden Enden, also als Punktreihe, die über `stroke-dashoffset` wandert. | Größe | Formel | Wirkung | |---|---|---| | Punktgröße | `2,4 + 11 · (1 − e^(−W/5000))` | 2,4 px bei ein paar hundert Watt, ~13 px bei Volllast | | Tempo | `1 / (0,7 + 1,9 · e^(−W/3000))` | 2,6 s je Abschnitt bei wenig, 0,7 s bei viel | | Richtung | Vorzeichen von `fluss` | `enf-rueck` dreht die Animation um | | Stillstand | unter 15 W | Punkte ausgeblendet | Zwei Feinheiten, die teuer erkauft waren: * **Das Tempo wird über `Animation.updatePlaybackRate()` gesetzt**, nicht über eine neue `animation-duration`. Eine geänderte Dauer rechnet die Position neu — die Punkte sprängen bei jeder Wertänderung. * **Keine Bahn unter den Punkten.** Die dunkle Linie gibt es nur im Bearbeitungsmodus, wo sie zeigt, was mit was verbunden ist. In der Ansicht erzählen die Punkte den Weg; wo nichts fließt, steht nichts. ### 2.3 Füllstände Batterie und Wallbox haben statt eines bloßen Rings eine **Welle im Kreis**, die mit dem Ladestand steigt (`zustand.stand`, 0…1). Die Fläche liegt bei 22 % Deckung, beim Laden 32 %, und die Wasserlinie ist zusätzlich als Kante gezeichnet — sonst muss man den Stand suchen. Meldet ein Kreis keinen Stand, bleibt die Füllung ganz weg: Eine Wallbox ohne Auto zeigte sonst eine Linie am unteren Rand. ### 2.4 Plaketten und Symbolzustände Kleine runde Zeichen am Kreisrand, aus `lesen().zeichen`: | `art` | Zeichen | Bedeutung | |---|---|---| | `eco` | Blatt | Eco-Modus (Wallbox, Heizstab) | | `uhr` | Uhr | „Nächste Fahrt“ | | `manuell` | Regler | fester Modus | | `stecker`, `schloss` | Stecker, Schloss | Fahrzeug angesteckt / verriegelt | | `heizung` | Flamme | Heizung im Geschoss an | | `tank` | Zapfsäule | Tankfüllung, mit Text | | `warnung` | Dreieck | Störung | Die Glyphen sind Bootstrap-Icons und stehen als `\uF…`-Escapes im Quelltext — die Zeichen selbst liegen im privaten Unicode-Bereich, sind im Editor unsichtbar und werden beim Kopieren leicht verfälscht. `zustand` steuert dagegen das Symbol selbst: `an` schaltet die Zustandsklasse (leuchtender Kern), `laden` die Ladeanimation, `temps` färbt die Schichten des Pufferspeichers zwischen kalt und heiß. ### 2.5 Die Bewässerung Der einzige Kreis ohne Leistung: Statt Zahl und Ring stehen darin drei Zeilen, je Zone ein Symbol, der Name und der Zustand. Gezeichnet werden sie aus `katalog.zonen`, gefüllt von `bewaesserungAnzeige.js`, das die Ids kennt, die der Renderer vergibt. --- ## 3. Zwei Ansichten aus einer Lage ```mermaid flowchart LR subgraph Q["quer (≥ 720 px)"] direction LR Q1["Quellen links"] --> Q2["Mitte"] --> Q3["Verbraucher rechts"] end subgraph H["hochkant (< 720 px)"] direction TB H1["Quellen oben"] --> H2["Mitte"] --> H3["Verbraucher unten"] end ``` Hochkant werden schlicht **x und y vertauscht**. So braucht es keine zweite Lage je Kreis, und eine eigene Lage aus den Einstellungen gilt in beiden Ansichten. Wo das Vertauschen unglücklich steht, nennt der Katalog mit `hochX`/`hochY` eine eigene Lage — so stehen UG und Heizstab hochkant getauscht, damit die drei Geschosse untereinander liegen. Umgeschaltet wird über einen `ResizeObserver` am Behälter, und nur dann, wenn die Ansicht wirklich wechselt. Das Kiosk-Display (`anzeige.js`) setzt `hochkantUnter: 0` und bleibt damit immer quer. Der `viewBox` wird nach dem Zeichnen aus den äußersten Kreisen gerechnet (`rahmenSetzen()`), inklusive Platz für den Namen unter dem Kreis — ein ausgeschalteter Randkreis verkleinert das Bild also automatisch. --- ## 4. Wie die Werte hereinkommen ```mermaid sequenceDiagram participant M as MQTT-Broker participant S as solarMQTT.js participant E as Energiefluss participant K as Katalog M->>S: solarManager/# (viele Nachrichten je Sekunde) Note over S: Werte in mqttData ablegen,
höchstens 1× pro Sekunde weiterreichen S->>E: aktualisieren(mqttData) loop je sichtbarem Kreis E->>K: lesen(m, optionen) K-->>E: {leistung, fluss, zeichen, zustand, …} Note over E: nur schreiben, was sich geändert hat end ``` Drei Sparmaßnahmen, die zusammen die Karte flüssig halten: 1. **Ein Takt je Sekunde.** Der Broker schickt deutlich öfter. 2. **Nur geänderte Werte schreiben.** Jeder Schreibzugriff macht Style und Paint ungültig; gemessen waren rund 71 % der Zugriffe reine Wiederholungen. 3. **Animationen stehen still, wenn niemand hinsieht** — `IntersectionObserver` für „Karte im Bild“ und `visibilitychange` für „Tab im Vordergrund“. Ein Fehler in einem `lesen()` fängt der Renderer je Kreis ab: Er meldet ihn auf der Konsole und zeichnet die übrigen weiter. Eine kaputte Zeile im Katalog nimmt also nicht die ganze Übersicht mit. --- ## 5. Antippen und Bearbeiten `klick` im Katalog sagt, was ein Tipp auf einen Kreis tut: | Eintrag | Wirkung | |---|---| | `{ ansicht: "anlage" }` | schaltet auf die Anlagen-Ansicht (`anlage.js`) | | `{ ansicht: "speicher" }` | schaltet auf die Speicher-Ansicht (`speicher.js`) | | `{ modal: "heater" }` … | öffnet das passende Bedienfenster (`bedienfenster.js`, `ladefenster.js`) | Klickbare Kreise bekommen ein Pfeil-Abzeichen, `tabindex`, `role="button"` und reagieren auf Enter und Leertaste. Im **Bearbeitungsmodus** (`bearbeiten: true`, nur in den Einstellungen) sind die Kreise ziehbar, die Verbindungslinien sichtbar und die Lage wird beim Loslassen auf zehn Einheiten gerundet an `onVerschoben(id, x, y)` gemeldet. Die Vorschau dort läuft mit festen Beispielwerten, damit man auch nachts sieht, was man einstellt. --- ## 6. Wo fange ich an, wenn ich … | Vorhaben | Ort | |---|---| | … einen Kreis hinzufügen | ein Eintrag in `ENERGIEFLUSS_KATALOG.knoten` — mehr nicht | | … ein neues Symbol | `SYMBOLE` in `energiefluss.js` (24er Raster, Mitte 0/0, `currentColor`) | | … eine neue Plakette | `ZEICHEN` in `energiefluss.js`, Glyph als `\uF…`-Escape | | … die Ringspreizung ändern | `ringAnteil()` — der Faktor 9 bestimmt die Stauchung | | … Punktgröße oder Tempo | `flussSetzen()` in `energiefluss.js` | | … die Lage hochkant korrigieren | `hochX`/`hochY` im Katalog (gilt nur dort) | | … einen Kreis ausblenden oder verschieben | Einstellungen → Übersicht, keine Codeänderung |