diff --git a/js/solar/homeMQTT.js b/js/solar/homeMQTT.js
index 713aee1..e878bb5 100644
--- a/js/solar/homeMQTT.js
+++ b/js/solar/homeMQTT.js
@@ -72,9 +72,59 @@ const homeSVG = {
* schneidet Nachkommastellen ab; ohne Angabe bleibt der Text so, wie das
* Geraet ihn meldet.
*/
+ /**
+ * Die Leistung benannter Wechselrichter, aufsummiert.
+ *
+ * Der SolarManager legt sie unter solarManager/inverters0, /inverters1
+ * und so fort ab - die Nummer ist eine Position in einer Liste und sagt
+ * nichts darueber, welches Dach gemeint ist. Deshalb wird ueber
+ * invertersN/name gesucht: aendert sich die Reihenfolge, findet die
+ * Kachel ihren Wechselrichter trotzdem, statt stillschweigend das
+ * falsche Dach anzuzeigen.
+ */
+ wechselrichterWert: function (mqttData, wert) {
+ const zweig = mqttData["solarManager"];
+ if (!zweig) return null;
+
+ const gesucht = wert.wechselrichter;
+ const feld = wert.feld || "p_AC";
+ let summe = 0;
+ const gefunden = [];
+ const vorhanden = [];
+
+ for (const schluessel in zweig) {
+ if (!schluessel.startsWith("inverters")) continue;
+ const wr = zweig[schluessel];
+ if (!wr || typeof wr.name === "undefined") continue;
+ const name = String(wr.name);
+ vorhanden.push(name);
+ if (gesucht.indexOf(name) === -1) continue;
+ if (typeof wr[feld] === "undefined") continue;
+ summe += Number(wr[feld]);
+ gefunden.push(name);
+ }
+
+ if (!gefunden.length) {
+ // Ein Name, den es nicht gibt, ergaebe sonst nur einen Strich auf
+ // der Kachel und keinen Hinweis, woran es liegt.
+ if (vorhanden.length && !homeSVG.gemeldet[gesucht.join("|")]) {
+ homeSVG.gemeldet[gesucht.join("|")] = true;
+ console.warn("Kachel: kein Wechselrichter namens " + gesucht.join(", ")
+ + ". Vorhanden sind: " + vorhanden.join(", "));
+ }
+ return null;
+ }
+ return summe;
+ },
+
+ /** Merkt sich gemeldete Namen, damit die Konsole nicht zulaeuft. */
+ gemeldet: {},
+
kachelWert: function (mqttData, wert) {
- const roh = homeSVG.ausTopic(mqttData, wert.topic);
- if (typeof roh === "undefined") return null;
+ const roh = wert.wechselrichter
+ ? homeSVG.wechselrichterWert(mqttData, wert)
+ : homeSVG.ausTopic(mqttData, wert.topic);
+ if (roh === null || typeof roh === "undefined") return null;
let text = String(roh);
if (wert.pfad) {
try {
diff --git a/restricted/home.php b/restricted/home.php
index 6cf0b0c..021df39 100644
--- a/restricted/home.php
+++ b/restricted/home.php
@@ -144,10 +144,21 @@
$wert): ?>
+
+
+
+ = $zeichen ?>
+
- " x="12" y="= $y[$i] ?>"
+ "
+ x="= $zeichen !== "" ? 15 : 12 ?>" y="= $y[$i] ?>"
= empty($wert["gross"]) ? "" : 'font-size="1.6em"' ?> text-anchor="middle">–
diff --git a/restricted/rooms.php b/restricted/rooms.php
index 690c4bc..248b3bd 100644
--- a/restricted/rooms.php
+++ b/restricted/rooms.php
@@ -98,20 +98,38 @@ $rooms = [
// Zwei Carports, einer je Wohnpartei - deshalb dieselben Kuerzel wie die
// Etagen. Die Wattpilot der EG-Partei steht dagegen in der Garage, siehe
// restricted/config.php; am Carport OG haengt der go-e Charger.
- ["floor" => "AG", "mqtt" => "Carport EG", "id" => "carport_eg", "x" => 156, "y" => 214, "werte" => []],
+ // Die PV des Carports haengt an mehreren Hoymiles. Sie werden ueber
+ // ihren Namen gesucht und aufsummiert, nicht ueber ihre Nummer: die
+ // Topics heissen solarManager/invertersN, und N ist eine Position in
+ // einer Liste. Aendert der SolarManager die Reihenfolge, zeigte eine
+ // fest eingetragene Nummer stillschweigend das falsche Dach an.
+ //
+ // Die Namen muessen genau so lauten wie unter invertersN/name. Passt
+ // einer nicht, schreibt homeMQTT.js die vorhandenen Namen in die
+ // Browser-Konsole - dann steht dort, was hier hingehoert.
+ ["floor" => "AG", "mqtt" => "Carport EG", "id" => "carport_eg", "x" => 156, "y" => 214,
+ "werte" => [
+ ["wechselrichter" => ["Carport 1-6", "Carport 7-12", "Carport 13-16",
+ "Carport 17-19"],
+ "feld" => "p_AC", "einheit" => "W", "stellen" => 0, "icon" => "sun"],
+ ]],
// Am Carport OG haengt der go-e Charger. Die Ladeleistung ist das
// Einzige, was sich hier messen laesst - und sie beantwortet die Frage,
// die man sich vor dem Carport stellt: laedt gerade jemand?
["floor" => "AG", "mqtt" => "Carport OG", "id" => "carport_og", "x" => 38, "y" => 155,
"werte" => [
["topic" => "solarManager/evPowerOG", "einheit" => "W", "stellen" => 0,
- "gross" => true],
+ "icon" => "ev-station"],
]],
// Veranda und Terrasse gibt es zweimal, je Wohnpartei eine. In der
// Draufsicht laege das obere Paar genau auf dem unteren - deshalb die
// Schraegsicht, siehe restricted/aussenplan.php.
["floor" => "AG", "mqtt" => "Veranda EG", "id" => "veranda_eg", "x" => 156, "y" => 132, "werte" => []],
- ["floor" => "AG", "mqtt" => "Veranda OG", "id" => "veranda_og", "x" => 150, "y" => 92, "werte" => []],
+ ["floor" => "AG", "mqtt" => "Veranda OG", "id" => "veranda_og", "x" => 150, "y" => 92,
+ "werte" => [
+ ["wechselrichter" => ["Veranda OG2-4", "Veranda UG/OG1"],
+ "feld" => "p_AC", "einheit" => "W", "stellen" => 0, "icon" => "sun"],
+ ]],
["floor" => "AG", "mqtt" => "Terrasse EG", "id" => "terrasse_eg", "x" => 212, "y" => 160, "werte" => []],
["floor" => "AG", "mqtt" => "Terrasse OG", "id" => "terrasse_og", "x" => 205, "y" => 122, "werte" => []],
["floor" => "AG", "mqtt" => "Pergola", "id" => "pergola", "x" => 339, "y" => 108, "werte" => []],
@@ -120,7 +138,7 @@ $rooms = [
["floor" => "AG", "mqtt" => "Garage", "id" => "garage", "x" => 103, "y" => 134,
"werte" => [
["topic" => "solarManager/evPower", "einheit" => "W", "stellen" => 0,
- "gross" => true],
+ "icon" => "ev-station"],
]],
// Der Garten ist kein Bauteil wie die anderen, aber ein Ort: hier haengen
// der Regensensor und die beiden Ventilsteuerungen.
@@ -131,7 +149,7 @@ $rooms = [
// laufende Tag - die Zahl, die man im Garten wissen will. Ein
// laengeres Fenster ist ein geaenderter Pfad und sonst nichts.
["topic" => "Wetter/Regen", "pfad" => "tage1", "einheit" => "mm",
- "stellen" => 1, "gross" => true],
+ "stellen" => 1, "icon" => "cloud-rain"],
]],
];
@@ -161,6 +179,28 @@ function floorLabel($floor)
return $GLOBALS["floorLabels"][$floor] ?? $floor;
}
+/**
+ * Codepunkt eines Bootstrap-Icons für die Kacheln.
+ *
+ * Die Icons kommen als Schrift, nicht als Pfad: css/bootstrap-icons.min.css
+ * ist ohnehin eingebunden (restricted/header.php), und SVG-Text darf dieselbe
+ * Schriftfamilie benutzen wie HTML. Ein eingebettetes je Symbol wäre
+ * dieselbe Wirkung mit mehr Zeilen.
+ *
+ * Die Zahlen stehen in genau dieser CSS unter .bi-::before und sind von
+ * dort abgeschrieben - deshalb steht der Name daneben, sonst prüft das nie
+ * wieder jemand nach.
+ */
+function kachelIcon($name)
+{
+ $icons = [
+ "ev-station" => "F83A", // Wallbox, Ladeleistung
+ "cloud-rain" => "F2B6", // Regenmenge
+ "sun" => "F5A2", // PV-Erzeugung
+ ];
+ return isset($icons[$name]) ? mb_chr(hexdec($icons[$name]), "UTF-8") : "";
+}
+
/**
* Etagen mit Grundriss - die, die im Menü und im SVG auftauchen.
*
@@ -225,7 +265,14 @@ function tileTopics()
$zweige = [];
foreach (roomsWithTile() as $room) {
foreach ($room["werte"] as $wert) {
- $zweige[explode("/", $wert["topic"])[0] . "/#"] = true;
+ // Ein Wert nennt entweder sein Topic oder einen Wechselrichter,
+ // den homeMQTT.js unter solarManager/invertersN sucht. Ohne diese
+ // Unterscheidung entstand aus dem fehlenden Topic ein "/#" - eine
+ // Anmeldung auf einen leeren Zweig.
+ $zweig = isset($wert["topic"])
+ ? explode("/", $wert["topic"])[0]
+ : "solarManager";
+ $zweige[$zweig . "/#"] = true;
}
}
return array_keys($zweige);