/* ========================================================================== Reiter "Übersicht": die Energiefluss-Übersicht an dieses Haus anpassen. Links die Übersicht selbst im Bearbeitungsmodus (energiefluss.js) mit Beispielwerten, rechts je Kreis Schalter und Name, beim PV-Kreis die Flächen. Verschoben wird in der Vorschau. Gespeichert wird der ganze Stand auf einmal (restricted/energiefluss.php hält davon nur die Abweichungen). Nutzt textSchuetzen() aus settings.js. ========================================================================== */ (function () { "use strict"; // Beispielwerte: ein sonniger Nachmittag, Auto lädt im EG, Heizstab läuft. const BEISPIEL = { "solarManager/P_PV": "8400", "solarManager/P_PVn": JSON.stringify([2600, 1300, 130, 130, 140, 140, 130, 130, 135, 135, 130, 130, 120, 120, 155, 150, 152, 158, 150, 149, 150, 154, 151, 150, 153, 155, 150, 92, 150, 152, 151, 150, 149, 0]), "solarManager/P_Grid": "-900", "solarManager/i_l1evu": "-1.3", "solarManager/i_l2evu": "-1.4", "solarManager/i_l3evu": "-1.2", "solarManager/P_Akku": "-2100", "solarManager/SOC": "64", "solarManager/crgMaxPct": "100", "solarManager/autarky": "100", "solarManager/eff": "96", "solarManager/P_Load": "-5400", "solarManager/og": "-350", "solarManager/eg": "3720", "solarManager/ug": "270", "solarManager/pHeat": "1000", "solarManager/t_buffT": "61", "solarManager/t_buffM": "48", "solarManager/t_buffB": "35", "solarManager/heatMode": "eco", "solarManager/heatOG": "1", "solarManager/heatEG": "0", "solarManager/evPower": "3.2", "solarManager/evSOC": "78", "solarManager/evFuel": "0", "solarManager/evMode": "Eco", "solarManager/evPlug": "1", "solarManager/evLock": "1", "solarManager/carRemChrg": "95", "solarManager/evPowerOG": "0", "solarManager/evPlugOG": "0", "solarManager/evModeOG": "Eco", }; let bild = null; let gespeichert = {}; // Stand der Datenbank let stand = {}; // Stand der Maske let geladen = false; const $ = id => document.getElementById(id); const katalog = () => ENERGIEFLUSS_KATALOG.knoten; const kopie = o => JSON.parse(JSON.stringify(o)); function hinweis(text, klasse) { const ziel = $("energieflussHinweis"); if (!ziel) return; ziel.className = "small " + (klasse || "text-body-secondary"); ziel.textContent = text; } function geaendert() { const sauber = s => JSON.stringify(Object.keys(s).sort().map(k => [k, s[k]])); const anders = sauber(normalisiert(stand)) !== sauber(normalisiert(gespeichert)); hinweis(anders ? "Nicht gespeichert." : "", anders ? "text-warning" : ""); } /** Nur echte Abweichungen, in fester Form - zum Vergleichen und Schicken. */ function normalisiert(s) { const aus = {}; for (const k of katalog()) { const e = s[k.id] || {}; const eintrag = { aktiv: k.pflicht ? true : e.aktiv !== false, name: (e.name || "").trim() || null, x: e.x ?? null, y: e.y ?? null, optionen: e.optionen || null, }; if (eintrag.x === k.x && eintrag.y === k.y) { eintrag.x = null; eintrag.y = null; } if (!eintrag.aktiv || eintrag.name || eintrag.x !== null || eintrag.optionen) aus[k.id] = eintrag; } return aus; } function vorschauZeichnen() { if (!bild) { bild = new Energiefluss($("energieflussVorschau"), { bearbeiten: true, einstellungen: stand, onVerschoben(id, x, y) { stand[id] = Object.assign({}, stand[id], { x, y }); listeZeichnen(); geaendert(); }, }); } bild.setzeEinstellungen(normalisiert(stand)); bild.zeichnen(); bild.aktualisieren(BEISPIEL); } /* ------------------------------------------------------------ Liste */ function listeZeichnen() { const ziel = $("energieflussListe"); const wirksam = bild ? bild.nachId : {}; ziel.innerHTML = katalog().map(k => { const e = stand[k.id] || {}; const aktiv = k.pflicht || e.aktiv !== false; const w = wirksam[k.id]; const eltern = w && w.elternWirksam; const elternName = eltern ? (stand[eltern.id] && stand[eltern.id].name) || eltern.titel || eltern.name : null; const verschoben = e.x != null && (e.x !== k.x || e.y !== k.y); const flaechen = k.id === "pv" && aktiv ? flaechenZeichnen(k, e) : ""; return `
${elternName ? "an " + textSchuetzen(elternName) : k.eltern ? "" : "frei"} ${flaechen}
`; }).join(""); } function flaechenZeichnen(k, e) { const liste = (e.optionen && e.optionen.flaechen) || k.optionen.flaechen; const eigen = !!(e.optionen && e.optionen.flaechen); return `
Flächen im Sonnenring – welche Einträge von P_PVn dazugehören ${eigen ? `` : ""}
${liste.map((f, i) => `
`).join("")}
`; } function flaechenLesen(zeile) { return [...zeile.querySelectorAll(".enf-e-flaeche")].map(r => ({ name: r.querySelector('[data-flaeche="name"]').value.trim(), von: Math.max(0, parseInt(r.querySelector('[data-flaeche="von"]').value, 10) || 0), bis: Math.max(0, parseInt(r.querySelector('[data-flaeche="bis"]').value, 10) || 0), farbe: r.querySelector('[data-flaeche="farbe"]').value, })); } function flaechenSetzen(id, flaechen) { const e = stand[id] = Object.assign({}, stand[id]); e.optionen = Object.assign({}, e.optionen, { flaechen }); } /* --------------------------------------------------------- Ereignisse */ function binden() { const liste = $("energieflussListe"); liste.addEventListener("change", ev => { const zeile = ev.target.closest(".enf-e-zeile"); if (!zeile) return; const id = zeile.dataset.id; if (ev.target.dataset.feld === "aktiv") { stand[id] = Object.assign({}, stand[id], { aktiv: ev.target.checked }); vorschauZeichnen(); listeZeichnen(); } else if (ev.target.dataset.flaeche) { flaechenSetzen(id, flaechenLesen(zeile)); vorschauZeichnen(); } geaendert(); }); liste.addEventListener("input", ev => { const zeile = ev.target.closest(".enf-e-zeile"); if (!zeile || ev.target.dataset.feld !== "name") return; stand[zeile.dataset.id] = Object.assign({}, stand[zeile.dataset.id], { name: ev.target.value }); clearTimeout(binden._uhr); binden._uhr = setTimeout(vorschauZeichnen, 300); geaendert(); }); liste.addEventListener("click", ev => { const knopf = ev.target.closest("[data-aktion]"); if (!knopf) return; const zeile = knopf.closest(".enf-e-zeile"); const id = zeile.dataset.id; switch (knopf.dataset.aktion) { case "lage": stand[id] = Object.assign({}, stand[id], { x: null, y: null }); break; case "flaeche-neu": { const f = flaechenLesen(zeile); const bis = f.length ? f[f.length - 1].bis + 1 : 0; f.push({ name: "Fläche " + (f.length + 1), von: bis, bis, farbe: "#f7c000" }); flaechenSetzen(id, f); break; } case "flaeche-weg": { const f = flaechenLesen(zeile); f.splice(Number(knopf.closest(".enf-e-flaeche").dataset.index), 1); flaechenSetzen(id, f); break; } case "flaechen-zurueck": if (stand[id] && stand[id].optionen) { const o = Object.assign({}, stand[id].optionen); delete o.flaechen; stand[id].optionen = Object.keys(o).length ? o : null; } break; default: return; } vorschauZeichnen(); listeZeichnen(); geaendert(); }); $("energieflussSpeichern").addEventListener("click", speichern); $("energieflussNeuLaden").addEventListener("click", () => { stand = kopie(gespeichert); vorschauZeichnen(); listeZeichnen(); geaendert(); }); $("energieflussZuruecksetzen").addEventListener("click", () => { stand = {}; vorschauZeichnen(); listeZeichnen(); geaendert(); }); } async function speichern() { const knopf = $("energieflussSpeichern"); knopf.disabled = true; try { const n = normalisiert(stand); const knoten = Object.keys(n).map(id => Object.assign({ id }, n[id])); const antwort = await fetch("ajax/settings.php?action=energiefluss-save", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ knoten }), }); const d = await antwort.json(); if (!antwort.ok || d.error) throw new Error(d.error || "HTTP " + antwort.status); gespeichert = d.einstellungen || {}; stand = kopie(gespeichert); vorschauZeichnen(); listeZeichnen(); hinweis("Gespeichert – die Solar-Seite zeigt es beim nächsten Laden.", "text-success"); } catch (e) { hinweis(e.message, "text-danger"); } finally { knopf.disabled = false; } } async function laden() { if (geladen) return; geladen = true; try { const antwort = await fetch("ajax/settings.php?action=energiefluss", { cache: "no-store" }); const d = await antwort.json(); if (!antwort.ok || d.error) throw new Error(d.error || "HTTP " + antwort.status); gespeichert = d.einstellungen || {}; stand = kopie(gespeichert); binden(); vorschauZeichnen(); listeZeichnen(); } catch (e) { geladen = false; $("energieflussListe").innerHTML = '
Übersicht nicht abrufbar: ' + textSchuetzen(e.message) + "
"; } } document.addEventListener("DOMContentLoaded", () => { if (!$("energieflussVorschau") || typeof Energiefluss === "undefined") return; // Erst zeichnen, wenn der Reiter offen ist - verborgen hat die Vorschau keine Größe. const knopf = $("einstellung-energiefluss-tab"); if (knopf) knopf.addEventListener("shown.bs.tab", laden); if ($("einstellung-energiefluss").classList.contains("active")) laden(); // Ein Anker in der Adresse öffnet den Reiter erst nach DOMContentLoaded. setTimeout(() => { if ($("einstellung-energiefluss").classList.contains("active")) laden(); }, 0); }); })();