diff --git a/ajax/carForm.php b/ajax/carForm.php index 5c80902..25833d7 100644 --- a/ajax/carForm.php +++ b/ajax/carForm.php @@ -65,7 +65,7 @@ $box = wallboxen()[$wallbox]; -
0 %100 %
+
0 %100 %
@@ -88,9 +88,9 @@ $box = wallboxen()[$wallbox];

Höchstens

- - - + + +
diff --git a/js/solar/anlage.js b/js/solar/anlage.js index 6a98d70..b100958 100644 --- a/js/solar/anlage.js +++ b/js/solar/anlage.js @@ -32,8 +32,8 @@ let laufendeNummer = 0; - const kw = w => (Math.abs(w) / 1000).toFixed(Math.abs(w) < 10000 ? 2 : 1).replace(".", ",") + " kW"; - const watt = w => Math.abs(w) < 1000 ? Math.round(w) + " W" : kw(w); + const kw = w => (Math.abs(w) / 1000).toFixed(Math.abs(w) < 10000 ? 2 : 1).replace(".", ",") + " kW"; + const watt = w => Math.abs(w) < 1000 ? Math.round(w) + " W" : kw(w); const schutz = s => String(s).replace(/[&<>"]/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); function el(name, attr, eltern) { @@ -393,7 +393,7 @@
${schutz(s.f.name)}${kw(s.w)}
- ${s.n} Platten · ${Math.round(s.w / summe * 100)} % der Erzeugung + ${s.n} Platten · ${Math.round(s.w / summe * 100)} % der Erzeugung
`).join(""); if (this.dom.flaechen._html !== flaechenHtml) { // Beim ersten Mal ganz schreiben, danach nur Werte - sonst springen die Balken. @@ -404,7 +404,7 @@ const s = summen[i]; c.querySelector("b").textContent = kw(s.w); c.querySelector(".anl-flaeche-balken span").style.width = (s.w / summe * 100).toFixed(1) + "%"; - c.querySelector("small").textContent = s.n + " Platten · " + Math.round(s.w / summe * 100) + " % der Erzeugung"; + c.querySelector("small").textContent = s.n + " Platten · " + Math.round(s.w / summe * 100) + " % der Erzeugung"; }); } this.dom.flaechen._html = flaechenHtml; @@ -419,7 +419,7 @@ const ausl = w.limit > 0 ? Math.min(100, w.ac / w.limit * 100) : 0; const aktiv = wahlWr === d.nr ? ' aria-pressed="true"' : ' aria-pressed="false"'; return `
  • `; @@ -452,7 +452,7 @@ : 'normal'; const vergleich = p.offline ? "Wechselrichter meldet nichts" : mittel[p.def.flaeche] < SCHWACHLICHT ? "zu wenig Licht zum Vergleichen" - : Math.round(p.anteil * 100) + " % des Mittels " + f.name; + : Math.round(p.anteil * 100) + " % des Mittels " + f.name; return `

    ${schutz(this.plattenName(p))}

    ${pille}
    ${p.offline ? "–" : watt(p.w)}${vergleich}
    @@ -461,7 +461,7 @@
    Wechselrichter
    Eingang
    ${p.index + 1} von ${p.wr.def.eingaenge.length}
    Mittel ${schutz(f.name)}
    ${watt(mittel[p.def.flaeche])}
    -
    Temperatur Wechselrichter
    ${p.wr.temp > 0 && !p.offline ? Math.round(p.wr.temp) + " °C" : "–"}
    +
    Temperatur Wechselrichter
    ${p.wr.temp > 0 && !p.offline ? Math.round(p.wr.temp) + " °C" : "–"}
    ${p.klasse === "schwach" ? '

    Liefert deutlich weniger als die anderen Platten dieser Fläche. Mögliche Gründe: Verschattung, Schmutz, ein loser Stecker – oder die Platte liegt anders ausgerichtet.

    ' : ""}`; } @@ -505,8 +505,8 @@
    ${w.gestoert ? "–" : watt(w.ac)}${schutz(w.def.modell)}
    - ${w.limit > 0 ? `
    Leistungsgrenze
    ${watt(w.limit)} · ${Math.round(w.ac / w.limit * 100)} % genutzt
    ` : ""} -
    Temperatur
    ${w.temp > 0 && !w.gestoert ? Math.round(w.temp) + " °C" : "–"}
    + ${w.limit > 0 ? `
    Leistungsgrenze
    ${watt(w.limit)} · ${Math.round(w.ac / w.limit * 100)} % genutzt
    ` : ""} +
    Temperatur
    ${w.temp > 0 && !w.gestoert ? Math.round(w.temp) + " °C" : "–"}
    ${w.gestoert ? '

    Der Wechselrichter antwortet der DTU gerade nicht. Seine Platten stehen deshalb auf „ohne Daten“ – über ihren Zustand sagt das nichts.

    ' : ""} ${w.geschaetzt ? '

    Aktuelle Werte fehlen; die Leistung ist aus den anderen Wechselrichtern hochgerechnet.

    ' : ""}`; @@ -521,7 +521,7 @@ const punkte = [ ...ohne.map(w => `
  • `), ...geschaetzt.map(w => `
  • `), - ...auffaellig.map(p => `
  • `), + ...auffaellig.map(p => `
  • `), ]; return `

    Anlage

    ${punkte.length ? punkte.length + " Hinweis" + (punkte.length > 1 ? "e" : "") : "alles in Ordnung"}
    diff --git a/js/solar/energiefluss.js b/js/solar/energiefluss.js index 9335fea..af4a6b6 100644 --- a/js/solar/energiefluss.js +++ b/js/solar/energiefluss.js @@ -164,8 +164,8 @@ function leistungText(w) { const a = Math.abs(w); - if (a < 1000) return Math.round(a) + " W"; - return (a / 1000).toFixed(a < 10000 ? 2 : 1).replace(".", ",") + " kW"; + if (a < 1000) return Math.round(a) + " W"; + return (a / 1000).toFixed(a < 10000 ? 2 : 1).replace(".", ",") + " kW"; } /** Liest mqttData: Werte kommen als Buffer, Zahl und Text wie gehabt. */ diff --git a/js/solar/energieflussKatalog.js b/js/solar/energieflussKatalog.js index 37b3ad1..ec0b2c9 100644 --- a/js/solar/energieflussKatalog.js +++ b/js/solar/energieflussKatalog.js @@ -113,7 +113,7 @@ const ENERGIEFLUSS_KATALOG = (function () { const a = n => (Math.round(Math.abs(m.zahl("solarManager/i_" + n + "evu")) * 10) / 10).toString().replace(".", ","); return { leistung: Math.abs(netz), - neben: a("l1") + " / " + a("l2") + " / " + a("l3") + " A", + neben: a("l1") + " / " + a("l2") + " / " + a("l3") + " A", fluss: -netz, ruhe: Math.abs(netz) < 30, }; @@ -130,7 +130,7 @@ const ENERGIEFLUSS_KATALOG = (function () { const laden = p < -20; const zeichen = []; if (laden && grenze < 100) { - zeichen.push({ art: "uhr", text: Math.round(grenze) + " %", titel: "Ladeleistung begrenzt" }); + zeichen.push({ art: "uhr", text: Math.round(grenze) + " %", titel: "Ladeleistung begrenzt" }); } return { leistung: Math.abs(p), @@ -151,8 +151,8 @@ const ENERGIEFLUSS_KATALOG = (function () { const aut = m.zahl("solarManager/autarky"); return { leistung: aut, - wert: Math.round(aut) + " %", - neben: "Wirkungsgrad " + Math.round(m.zahl("solarManager/eff")) + "%", + wert: Math.round(aut) + " %", + neben: "Wirkungsgrad " + Math.round(m.zahl("solarManager/eff")) + " %", segmente: [{ w: aut, farbe: "#6cbe58" }], zustand: { an: m.zahl("solarManager/P_PV") >= 20 }, }; @@ -174,7 +174,7 @@ const ENERGIEFLUSS_KATALOG = (function () { const allgemein = Math.max(0, gemein - heiz); return { leistung: last, - neben: "Gemein " + kw(gemein) + " kW", + neben: "Gemein " + kw(gemein) + " kW", segmente: [ { w: og, farbe: "#70ccbb", knoten: "og" }, { w: eg, farbe: "#70cc9c", knoten: "eg" }, @@ -238,7 +238,7 @@ const ENERGIEFLUSS_KATALOG = (function () { const grad = t => (Math.round(t * 10) / 10).toString().replace(".", ","); return { leistung: w, fluss: w, ruhe: w < 20, - neben: grad(oben) + " · " + grad(mitte) + " °C", + neben: grad(oben) + " / " + grad(mitte) + " °C", zeichen: [eco ? { art: "eco", titel: "Automatik (Überschuss)" } : { art: "manuell", titel: "Feste Leistung" }], zustand: { an: w >= 20, temps: [oben, mitte, unten] }, }; @@ -271,7 +271,7 @@ const ENERGIEFLUSS_KATALOG = (function () { // Text der Wallbox ("no car", "charging", ...). const roh = m.text("solarManager/evPlug"); const stecker = isNaN(Number(roh)) ? (roh !== undefined && roh !== "no car") : Number(roh) > 0; - let neben = Math.round(soc) + " %"; + let neben = Math.round(soc) + " %"; // Restladezeit in Minuten laut Fahrzeug (gatherSkodaData.py). Das // frühere carRemChrg schickt der Manager nicht mehr. const rest = m.zahl("solarManager/evRemChrgTime"); @@ -280,7 +280,7 @@ const ENERGIEFLUSS_KATALOG = (function () { neben = "fertig " + String(fertig.getHours()).padStart(2, "0") + ":" + String(fertig.getMinutes()).padStart(2, "0"); } const zeichen = wallboxZeichen(m.text("solarManager/evMode"), stecker, m.zahl("solarManager/evLock") > 0); - if (tank > 0) zeichen.push({ art: "tank", text: Math.round(tank) + " %", titel: "Tank" }); + if (tank > 0) zeichen.push({ art: "tank", text: Math.round(tank) + " %", titel: "Tank" }); return { leistung: w, fluss: w, ruhe: w < 50 && !stecker, neben, zeichen, diff --git a/js/solar/ladefenster.js b/js/solar/ladefenster.js index acee3e8..dc64a78 100644 --- a/js/solar/ladefenster.js +++ b/js/solar/ladefenster.js @@ -135,18 +135,18 @@ const zb = wurzel.querySelector(".lade-ziel"); zb.style.strokeDasharray = zielBogen + " 100"; zb.style.strokeDashoffset = -ist; - setze("soc", mitSoc ? Math.round(s.soc) + " %" : (Number(wurzel.dataset.akkuKwh) ? komma(Number(wurzel.dataset.akkuKwh)) + " kWh" : "–")); - setze("ringUnten", s.modus === "plan" ? (mitSoc ? "Ziel " + s.ziel + " %" : "+ " + s.ziel + " %") : mitSoc ? "Ladestand" : "Akku"); + setze("soc", mitSoc ? Math.round(s.soc) + " %" : (Number(wurzel.dataset.akkuKwh) ? komma(Number(wurzel.dataset.akkuKwh)) + " kWh" : "–")); + setze("ringUnten", s.modus === "plan" ? (mitSoc ? "Ziel " + s.ziel + " %" : "+ " + s.ziel + " %") : mitSoc ? "Ladestand" : "Akku"); // Zustand const zustand = laedt ? "lädt" : s.frc === 1 ? "gestoppt" : s.auto === "fertig" ? "beendet" : s.auto === "kein" ? "kein Auto" : "wartet"; - $("leistung").innerHTML = komma(s.kw) + " kW" + zustand + ""; + $("leistung").innerHTML = komma(s.kw) + " kW" + zustand + ""; const pille = $("stecker"); const pilleText = s.auto === "kein" ? "kein Auto" : s.auto === "laedt" ? "lädt" : s.auto === "fertig" ? "beendet" : s.auto ? "angesteckt" : ""; setze("stecker", pilleText); pille.className = "lade-pille" + (s.auto && s.auto !== "kein" ? " an" : ""); const anteil = laedt && s.kw > 0 ? Math.max(0, Math.min(1, 1 - Math.max(0, s.netz) / (s.kw * 1000))) : null; - setze("quelle", anteil !== null ? Math.round(anteil * 100) + " % aus Sonnenstrom" : ""); + setze("quelle", anteil !== null ? Math.round(anteil * 100) + " % aus Sonnenstrom" : ""); let fertig = ""; if (laedt && s.restMin > 0) fertig = "fertig " + inStunden(s.restMin / 60); else if (s.modus === "plan") fertig = "Ziel bis " + naechstes(s.zeit); @@ -168,9 +168,9 @@ // Erklärung const kwMax = KW_JE_A[s.ampere] || (s.ampere ? s.ampere * 0.69 : 11); let erkl = { - sofort: "Lädt mit bis zu " + komma(kwMax) + " kW – reicht die Sonne nicht, kommt der Rest aus dem Netz.", + sofort: "Lädt mit bis zu " + komma(kwMax) + " kW – reicht die Sonne nicht, kommt der Rest aus dem Netz.", sonne: "Lädt nur mit Überschuss. Scheint zu wenig Sonne, pausiert die Ladung.", - plan: "Lädt bis " + naechstes(s.zeit) + (mitSoc ? " auf " + s.ziel + " %" : " " + s.ziel + " % nach") + " – bevorzugt mit Sonnenstrom, nötigenfalls aus dem Netz.", + plan: "Lädt bis " + naechstes(s.zeit) + (mitSoc ? " auf " + s.ziel + " %" : " " + s.ziel + " % nach") + " – bevorzugt mit Sonnenstrom, nötigenfalls aus dem Netz.", }[s.modus] || ""; if (s.frc === 1) erkl = "Gestoppt: die Wallbox lädt erst wieder nach „Ladung freigeben“ – dann entscheidet der Modus."; if (s.frc === 2) erkl = "Lädt sofort, unabhängig vom Modus. „Zurück zur Automatik“ überlässt es wieder dem Modus."; @@ -190,9 +190,9 @@ regler.style.setProperty("--ist", (mitSoc ? s.soc : 0) + "%"); regler.style.setProperty("--wert", wert + "%"); setze("zielTitel", mitSoc ? "Ladestand bei Abfahrt" : "Zusätzlich laden"); - setze("markeMitte", mitSoc ? "jetzt " + Math.round(s.soc) + " %" : ""); + setze("markeMitte", mitSoc ? "jetzt " + Math.round(s.soc) + " %" : ""); const plusWh = mitSoc ? Math.max(0, wert - s.soc) * whProProzent : wert * whProProzent; - $("zielAusgabe").innerHTML = wert + " %" + (plusWh > 0 ? "+ " + komma(plusWh / 1000) + " kWh" : "schon erreicht") + ""; + $("zielAusgabe").innerHTML = wert + " %" + (plusWh > 0 ? "+ " + komma(plusWh / 1000) + " kWh" : "schon erreicht") + ""; setze("abfahrtAusgabe", naechstes(s.zeit)); let bekannt = false; wurzel.querySelectorAll("[data-zeit]").forEach(c => { diff --git a/js/solar/speicher.js b/js/solar/speicher.js index 74496fb..407411d 100644 --- a/js/solar/speicher.js +++ b/js/solar/speicher.js @@ -233,25 +233,25 @@ const h = kwh * soc / 100 / (-p / 1000); prognose = "leer " + wann(h); } - if (p > 50 && s.grenze > 0 && s.grenze < 100) prognose += " · begrenzt auf " + Math.round(s.grenze) + " %"; + if (p > 50 && s.grenze > 0 && s.grenze < 100) prognose += " · begrenzt auf " + Math.round(s.grenze) + " %"; const kacheln = [ - { titel: "Ladestand", zeichen: "\uF187", wert: Math.round(soc) + " %", neben: kwh ? "≈ " + komma(kwh * soc / 100) + " von " + komma(kwh) + " kWh" : "", balken: soc }, - { titel: "Leistung", zeichen: p > 50 ? "\uF185" : "\uF46E", wert: (p > 50 ? "+" : p < -50 ? "−" : "") + komma(Math.abs(p) < 50 ? 0 : Math.abs(p) / 1000, 2) + " kW", + { titel: "Ladestand", zeichen: "\uF187", wert: Math.round(soc) + " %", neben: kwh ? "≈ " + komma(kwh * soc / 100) + " von " + komma(kwh) + " kWh" : "", balken: soc }, + { titel: "Leistung", zeichen: p > 50 ? "\uF185" : "\uF46E", wert: (p > 50 ? "+" : p < -50 ? "−" : "") + komma(Math.abs(p) < 50 ? 0 : Math.abs(p) / 1000, 2) + " kW", neben: (p > 50 ? "lädt · " : p < -50 ? "entlädt · " : "") + prognose, klasse: p > 50 ? "spk-gut" : p < -50 ? "spk-warm" : "" }, ]; if (s.bmu) { const zyklen = kwh ? s.entladen / kwh : 0; kacheln.push( - { titel: "Gesundheit", zeichen: "\uF76E", wert: komma(s.soh, 0) + " %", neben: "SOH laut BMU" }, - { titel: "Zellspreizung", zeichen: "\uF304", wert: tausend(s.spreizung) + " mV", - neben: komma(s.zelleMin / 1000, 3) + "–" + komma(s.zelleMax / 1000, 3) + " V", + { titel: "Gesundheit", zeichen: "\uF76E", wert: komma(s.soh, 0) + " %", neben: "SOH laut BMU" }, + { titel: "Zellspreizung", zeichen: "\uF304", wert: tausend(s.spreizung) + " mV", + neben: komma(s.zelleMin / 1000, 3) + "–" + komma(s.zelleMax / 1000, 3) + " V", klasse: s.spreizung > 30 ? "spk-warm" : "" }, - { titel: "Temperatur", zeichen: "\uF5CD", wert: Math.round(s.tMin) + "–" + Math.round(s.tMax) + " °C", neben: (s.temps || []).length + " Fühler" }, - { titel: "Spannung", zeichen: "\uF66B", wert: komma(s.spannung) + " V", neben: komma(s.strom) + " A" }, - { titel: "Durchsatz", zeichen: "\uF130", wert: tausend(s.entladen) + " kWh", - neben: "≈ " + tausend(zyklen) + " Zyklen · " + (s.geladen ? Math.round(s.entladen / s.geladen * 100) : 0) + " % zurück", - titelLang: "entladen seit Inbetriebnahme; Zyklen gerechnet mit " + komma(kwh) + " kWh; zurück = entladen je geladen" }, + { titel: "Temperatur", zeichen: "\uF5CD", wert: Math.round(s.tMin) + "–" + Math.round(s.tMax) + " °C", neben: (s.temps || []).length + " Fühler" }, + { titel: "Spannung", zeichen: "\uF66B", wert: komma(s.spannung) + " V", neben: komma(s.strom) + " A" }, + { titel: "Durchsatz", zeichen: "\uF130", wert: tausend(s.entladen) + " kWh", + neben: "≈ " + tausend(zyklen) + " Zyklen · " + (s.geladen ? Math.round(s.entladen / s.geladen * 100) : 0) + " % zurück", + titelLang: "entladen seit Inbetriebnahme; Zyklen gerechnet mit " + komma(kwh) + " kWh; zurück = entladen je geladen" }, { titel: "Zustand", zeichen: s.fehler ? "\uF33A" : "\uF26A", wert: s.fehler ? "Fehler" : "in Ordnung", neben: (s.firmware || "") + (s.ausgleich ? " · gleicht " + s.ausgleich + " Zellen aus" : ""), klasse: s.fehler ? "spk-krit" : "spk-gut" }, ); @@ -324,9 +324,9 @@ M${i + 1} ${d.zellen.map((v, j) => { const a = (v - mittel) / skala; - return ``; + return ``; }).join("")} - ${komma(d.min / 1000, 3)}–${komma(d.max / 1000, 3)} V${d.tMin !== null ? d.tMin + "–" + d.tMax + " °C" : ""} + ${komma(d.min / 1000, 3)}–${komma(d.max / 1000, 3)} V${d.tMin !== null ? d.tMin + "–" + d.tMax + " °C" : ""} `).join(""); this.dom.turm.innerHTML = kopf + reihen + '
    '; this.detailZeigen(s, gewaehlt); @@ -350,14 +350,14 @@ const nrGesamt = nr * ZELLEN_JE_MODUL + j + 1; const markiert = nrGesamt === s.zelleMinNr || nrGesamt === s.zelleMaxNr; return `Zelle ${nrGesamt}: ${tausend(v)} mV`; + height="${(H - U - Y(v)).toFixed(1)}" rx="2" style="fill:${zellFarbe(a)};--j:${j}">Zelle ${nrGesamt}: ${tausend(v)} mV`; }).join(""); const temps = d.temps.map((t, j) => `${t}°`).join(""); const html = `

    Modul ${nr + 1}

    ${spr > 20 ? "ungleich" : "ausgeglichen"}
    -
    ${spr} mVSpreizung der ${d.zellen.length} Zellen
    +
    ${spr} mVSpreizung der ${d.zellen.length} Zellen
    ${stufen.map(v => `${v}`).join("")} @@ -368,9 +368,9 @@
    Zellspannung in mV · gestrichelt: Mittel aller Zellen · weiß umrandet: tiefste und höchste Zelle der Batterie
    ${temps}
    -
    Modulspannung
    ${komma(d.zellen.reduce((a, v) => a + v, 0) / 1000)} V
    -
    Zellen
    ${komma(d.min / 1000, 3)} – ${komma(d.max / 1000, 3)} V
    -
    Temperatur
    ${d.tMin !== null ? d.tMin + " – " + d.tMax + " °C" : "–"}
    +
    Modulspannung
    ${komma(d.zellen.reduce((a, v) => a + v, 0) / 1000)} V
    +
    Zellen
    ${komma(d.min / 1000, 3)} – ${komma(d.max / 1000, 3)} V
    +
    Temperatur
    ${d.tMin !== null ? d.tMin + " – " + d.tMax + " °C" : "–"}
    Ganze Batterie
    Zelle ${s.zelleMinNr} am tiefsten, ${s.zelleMaxNr} am höchsten
    `; this.dom.detail.innerHTML = html; @@ -399,8 +399,8 @@ links: { min: 0, max: 100, einheit: "%" }, rechts: { einheit: "kW", symmetrisch: true }, reihen: [ - { name: "Leistung", achse: "rechts", werte: v.soc.map(r => [r[0], r[2] / 1000]), flaeche: true, farbe: "#6cbe58", farbeNegativ: "#e8a33a", format: w => (w > 0 ? "+" : "") + komma(w, 2) + " kW" }, - { name: "Ladestand", achse: "links", werte: v.soc.map(r => [r[0], r[1]]), farbe: "#e3e8ec", format: w => komma(w, 0) + " %" }, + { name: "Leistung", achse: "rechts", werte: v.soc.map(r => [r[0], r[2] / 1000]), flaeche: true, farbe: "#6cbe58", farbeNegativ: "#e8a33a", format: w => (w > 0 ? "+" : "") + komma(w, 2) + " kW" }, + { name: "Ladestand", achse: "links", werte: v.soc.map(r => [r[0], r[1]]), farbe: "#e3e8ec", format: w => komma(w, 0) + " %" }, ], }); if (!v.bmu.length) { @@ -416,9 +416,9 @@ links: { min: 0, einheit: "mV" }, rechts: { einheit: "°C" }, reihen: [ - { name: "Spreizung", achse: "links", werte: v.bmu.map(r => [r[0], r[1]]), flaeche: true, farbe: "#8fb8de", format: w => komma(w, 0) + " mV" }, - { name: "wärmster Fühler", achse: "rechts", werte: v.bmu.map(r => [r[0], r[3]]), farbe: "#e8a33a", format: w => komma(w, 0) + " °C" }, - { name: "kältester Fühler", achse: "rechts", werte: v.bmu.map(r => [r[0], r[2]]), farbe: "#4f8fb8", format: w => komma(w, 0) + " °C" }, + { name: "Spreizung", achse: "links", werte: v.bmu.map(r => [r[0], r[1]]), flaeche: true, farbe: "#8fb8de", format: w => komma(w, 0) + " mV" }, + { name: "wärmster Fühler", achse: "rechts", werte: v.bmu.map(r => [r[0], r[3]]), farbe: "#e8a33a", format: w => komma(w, 0) + " °C" }, + { name: "kältester Fühler", achse: "rechts", werte: v.bmu.map(r => [r[0], r[2]]), farbe: "#4f8fb8", format: w => komma(w, 0) + " °C" }, ], }); }