Was draussen haengt, stand als Pseudo-Raum "aussen" in automations.php:
"Geraete, die zu keinem Raum der Wohnung gehoeren - Carport, Garage,
Regensensor". Das waren inzwischen sechzehn Geraete in einem einzigen Topf,
der groesste ueberhaupt - in der Geraeteauswahl des Editors standen die fuenf
Carport-Wechselrichter, das Sonnensegel und das Garagentor unsortiert
nebeneinander.
Jetzt ist das Aussengelaende eine Etage wie OG, EG und UG, mit eigenen
Raeumen: Carport, Veranda, Pergola, Terrasse, Garage und Garten. Die Namen
stammen von den Geraeten selbst - die Hoymiles heissen seit jeher "Carport
1-6" und "Veranda OG2-4". Die achtzehn Geraete sind verteilt (die beiden
Ventilsteuerungen hatten noch gar keinen Raum und stehen jetzt im Garten,
zusammen mit dem Regensensor, der ihre Automatiken ausloest); "Ohne Raum"
faellt damit von acht auf sechs, und die sind es zu Recht.
AG ist eine Etage ohne Grundriss. Ihre Raeume haben kein x/y, es gibt ja
kein Bild, auf das eine Kachel zeigen koennte - ein Raum ohne Position war
schon immer vorgesehen. Das Menue und das SVG richten sich deshalb nach der
neuen floorsWithPlan(), die Zuordnung und die Automatiken nach $floors.
Bekommt der erste AG-Raum eine Kachel, erscheint die Etage von selbst auch
im Menue; es gibt keine zweite Liste, die man nachziehen muesste.
Die Etagen standen bisher an sechs Stellen noch einmal im Code. Jetzt gibt
es genau eine Liste, $floors in rooms.php, und alles andere leitet sich ab:
* automations.php prueft gegen $floors statt gegen eine eigene Whitelist
* ajax/AutoAction.php baut seine Etagen-Auswahl daraus
* home.php baut Reiter und Inhaltsbereiche in einer Schleife statt
dreimal fast dasselbe HTML
* header.php baut die Menuepunkte daraus
* switchTab() in homeMQTT.js liest die Reiter aus der Seite, statt OG, EG
und UG sechsmal aufzuzaehlen
* refreshAutomations() ebenso - eine feste Liste haette die vierte Etage
still ausgelassen, die Tabelle waere ohne Fehlermeldung leer geblieben
Dazu die ausgeschriebenen Namen in $floorLabels: das Kuerzel steht in URLs,
SVG-Ids und im MQTT-Baum, "Aussengelaende" nur als Titel am Menuepunkt und
am Reiter.
In der Datenbank ist das Enum um 'AG' erweitert; header.php laedt rooms.php
jetzt selbst, weil es vor home.php eingebunden wird.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
200 lines
9.0 KiB
PHP
200 lines
9.0 KiB
PHP
<?php
|
|
/*
|
|
* Einzige Quelle für die Räume der Home-Ansicht.
|
|
*
|
|
* Aus dieser Tabelle entstehen sowohl die SVG-Kacheln in home.php als auch
|
|
* die Aktualisierungsschleife in js/solar/homeMQTT.js (die Tabelle wird dafür
|
|
* als JSON in die Seite geschrieben). Ein neuer oder umbenannter Raum ist
|
|
* damit eine Zeile hier statt drei Stellen im Code.
|
|
*
|
|
* floor Etage; bestimmt zugleich den Zweig Raumtemp/<floor>/
|
|
* mqtt Name des Raums im MQTT-Baum: Raumtemp/<floor>/<mqtt>/...
|
|
* id Kürzel in den SVG-Element-IDs: <floor>_w0_<id>, <floor>_<id>_heater
|
|
* x, y Position der Kachel im SVG (viewBox 400x300) - fehlt bei Räumen
|
|
* ohne Kachel
|
|
* werte Was auf der Kachel steht: bis zu drei Messwerte, jeder mit
|
|
* topic, optional pfad (Schlüssel in einer JSON-Nachricht),
|
|
* einheit und stellen (Nachkommastellen). Fehlt der Eintrag, sind
|
|
* es die drei Thermostatwerte - Soll, Ist, Feuchte - wie bisher.
|
|
* Eine leere Liste heisst: kein Messwert, die Kachel trägt nur
|
|
* ihren Namen und öffnet den Raum.
|
|
*
|
|
* Ein Raum ohne x/y ist trotzdem ein Raum: Geräte lassen sich ihm zuordnen
|
|
* (siehe die Spalte actors.room und den Zuordnungsdialog in der Karte
|
|
* "Automatismen"), er erscheint nur nicht als Kachel im Grundriss. Derzeit
|
|
* hat jeder Raum eine Kachel; die Unterscheidung bleibt, weil ein neuer Raum
|
|
* zuerst als Zeile ohne Position entsteht und erst dann seinen Platz im
|
|
* Grundriss bekommt.
|
|
*
|
|
* Wo eine Kachel hingehört, misst tools/kachelpositionen.php aus: Grundriss
|
|
* mit Raster, Geisterkachel am Zeiger, fertige Zeile zum Abschreiben. x/y
|
|
* ist die linke obere Ecke der 24 x 19 grossen Kachel, nicht ihre Mitte.
|
|
*/
|
|
|
|
// Reihenfolge im Menü und in der Anzeige; OG ist die Standardetage.
|
|
//
|
|
// AG ist das Außengelände - eine Etage ohne Grundriss. Was draußen hängt,
|
|
// stand vorher als Pseudo-Raum "aussen" in automations.php und war damit ein
|
|
// einziger Topf: Carport, Pergola, Garage und die Wechselrichter alle
|
|
// nebeneinander. Als Etage bekommt jeder davon einen Raum, und die
|
|
// Geräteauswahl im Automatik-Editor gruppiert danach wie überall sonst.
|
|
$floors = ["OG", "EG", "UG", "AG"];
|
|
|
|
// Ausgeschriebene Namen für Beschriftungen, wo Platz ist. Das Kürzel steht in
|
|
// URLs (?floor=AG), in SVG-Element-Ids (AG_Info) und im MQTT-Baum - dort will
|
|
// man weder Umlaute noch Länge.
|
|
$floorLabels = [
|
|
"OG" => "Obergeschoss",
|
|
"EG" => "Erdgeschoss",
|
|
"UG" => "Untergeschoss",
|
|
"AG" => "Außengelände",
|
|
];
|
|
|
|
$rooms = [
|
|
["floor" => "OG", "mqtt" => "Wohnzimmer", "id" => "wozi", "x" => 180, "y" => 140],
|
|
["floor" => "OG", "mqtt" => "KiZi", "id" => "kizi", "x" => 130, "y" => 80],
|
|
["floor" => "OG", "mqtt" => "Schlafzimmer", "id" => "schlafen", "x" => 235, "y" => 70],
|
|
["floor" => "OG", "mqtt" => "Bad", "id" => "bad", "x" => 260, "y" => 120],
|
|
["floor" => "OG", "mqtt" => "Buero", "id" => "buero", "x" => 265, "y" => 175],
|
|
// Kein Thermostat, und noch kein Gerät zugeordnet - die Kachel trägt
|
|
// vorerst nur ihren Namen.
|
|
["floor" => "OG", "mqtt" => "Kueche", "id" => "kueche", "x" => 122, "y" => 138,
|
|
"werte" => []],
|
|
["floor" => "OG", "mqtt" => "Technik", "id" => "technik", "x" => 173, "y" => 67,
|
|
"werte" => []],
|
|
["floor" => "EG", "mqtt" => "Wohnzimmer", "id" => "wozi", "x" => 185, "y" => 150],
|
|
["floor" => "EG", "mqtt" => "Florian", "id" => "kizi", "x" => 130, "y" => 90],
|
|
["floor" => "EG", "mqtt" => "Schlafen", "id" => "schlafen", "x" => 235, "y" => 80],
|
|
["floor" => "EG", "mqtt" => "Bad", "id" => "bad", "x" => 253, "y" => 130],
|
|
["floor" => "EG", "mqtt" => "Magdalena", "id" => "buero", "x" => 258, "y" => 180],
|
|
["floor" => "EG", "mqtt" => "Kueche", "id" => "kueche", "x" => 126, "y" => 146,
|
|
"werte" => []],
|
|
// Der Shelly-Zähler des EG meldet seine Werte als JSON in einer einzigen
|
|
// Nachricht; "pfad" greift den Schlüssel daraus heraus.
|
|
["floor" => "EG", "mqtt" => "Technik", "id" => "technik", "x" => 172, "y" => 78,
|
|
"werte" => [
|
|
["topic" => "Power_EG/status/em:0", "pfad" => "total_act_power",
|
|
"einheit" => "W", "stellen" => 0, "gross" => true],
|
|
]],
|
|
["floor" => "UG", "mqtt" => "Buero", "id" => "buero", "x" => 140, "y" => 140],
|
|
["floor" => "UG", "mqtt" => "Bad", "id" => "bad", "x" => 210, "y" => 80],
|
|
["floor" => "UG", "mqtt" => "Kueche", "id" => "kueche", "x" => 190, "y" => 195],
|
|
["floor" => "UG", "mqtt" => "Technik", "id" => "technik", "x" => 238, "y" => 93,
|
|
"werte" => [
|
|
["topic" => "Power_UG/status/em:0", "pfad" => "total_act_power",
|
|
"einheit" => "W", "stellen" => 0, "gross" => true],
|
|
["topic" => "wasser/main/value", "einheit" => "m³", "stellen" => 1],
|
|
]],
|
|
// Außengelände: noch ohne x/y, es gibt ja keinen Lageplan, auf den eine
|
|
// Kachel zeigen könnte. Zuordnen und Automatiken anlegen geht trotzdem -
|
|
// dafür zählt allein, dass der Raum hier steht. Kommt der Lageplan, sind
|
|
// es zwei Zahlen je Zeile, gemessen mit tools/kachelpositionen.php.
|
|
//
|
|
// Die Namen stammen von den Geräten selbst: die Hoymiles heißen seit jeher
|
|
// "Carport 1-6" und "Veranda OG2-4", das Sonnensegel sitzt an der Pergola.
|
|
["floor" => "AG", "mqtt" => "Carport", "id" => "carport", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Veranda", "id" => "veranda", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Pergola", "id" => "pergola", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Terrasse", "id" => "terrasse", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Garage", "id" => "garage", "werte" => []],
|
|
// Der Garten ist kein Bauteil wie die anderen, aber ein Ort: hier haengen
|
|
// der Regensensor und die beiden Ventilsteuerungen.
|
|
["floor" => "AG", "mqtt" => "Garten", "id" => "garten", "werte" => []],
|
|
];
|
|
|
|
/**
|
|
* Räume einer Etage, die eine Kachel im Grundriss haben.
|
|
*
|
|
* Alles, was zeichnet oder aktualisiert, geht hierüber - ein Raum ohne
|
|
* Position hätte keine Stelle im SVG und keine Elemente, die sich füllen
|
|
* liessen.
|
|
*/
|
|
function roomsOnFloor($floor)
|
|
{
|
|
return array_values(array_filter($GLOBALS["rooms"], function ($room) use ($floor) {
|
|
return $room["floor"] == $floor && isset($room["x"]);
|
|
}));
|
|
}
|
|
|
|
/** Alle Räume, auch die ohne Kachel - für die Geräte-Zuordnung. */
|
|
function allRooms()
|
|
{
|
|
return $GLOBALS["rooms"];
|
|
}
|
|
|
|
/** Ausgeschriebener Name einer Etage; unbekannte bleiben ihr Kürzel. */
|
|
function floorLabel($floor)
|
|
{
|
|
return $GLOBALS["floorLabels"][$floor] ?? $floor;
|
|
}
|
|
|
|
/**
|
|
* Etagen mit Grundriss - die, die im Menü und im SVG auftauchen.
|
|
*
|
|
* Das Außengelände ist eine Etage ohne Bild: seine Räume haben keine Kachel,
|
|
* und ein Menüpunkt dorthin führte auf eine leere Zeichenfläche. Sobald der
|
|
* erste AG-Raum ein x/y bekommt, erscheint die Etage von selbst auch dort -
|
|
* es gibt keine zweite Liste, die man nachziehen müsste.
|
|
*
|
|
* Für die Zuordnung und die Automatiken zählt dagegen $floors: dort ist das
|
|
* Außengelände von Anfang an dabei.
|
|
*/
|
|
function floorsWithPlan()
|
|
{
|
|
return array_values(array_filter($GLOBALS["floors"], function ($floor) {
|
|
return count(roomsOnFloor($floor)) > 0;
|
|
}));
|
|
}
|
|
|
|
/** Nur die Räume mit Kachel, für die Aktualisierungsschleife im Browser. */
|
|
function roomsWithTile()
|
|
{
|
|
return array_values(array_map("mitWerten", array_filter($GLOBALS["rooms"], function ($room) {
|
|
return isset($room["x"]);
|
|
})));
|
|
}
|
|
|
|
/**
|
|
* Die Messwerte einer Kachel, fertig ausgeschrieben.
|
|
*
|
|
* Ohne eigene Angabe sind es die drei Thermostatwerte, die dort schon immer
|
|
* standen: Solltemperatur klein darüber, Isttemperatur gross, Feuchte klein
|
|
* darunter. Nur diese Räume bekommen auch die Symbole für "heizt" und
|
|
* "Pufferbetrieb" - anderswo gibt es nichts zu heizen.
|
|
*/
|
|
function mitWerten($room)
|
|
{
|
|
if (isset($room["werte"])) {
|
|
$room["heizung"] = false;
|
|
$room["werte"] = array_slice($room["werte"], 0, 3);
|
|
return $room;
|
|
}
|
|
$zweig = "Raumtemp/" . $room["floor"] . "/" . $room["mqtt"] . "/";
|
|
$room["heizung"] = true;
|
|
$room["werte"] = [
|
|
["topic" => $zweig . "Set Temp[degC]", "einheit" => "°C", "stellen" => 0],
|
|
["topic" => $zweig . "Temp[degC]", "einheit" => "°C", "gross" => true],
|
|
["topic" => $zweig . "rHum[%]", "einheit" => "%rF"],
|
|
];
|
|
return $room;
|
|
}
|
|
|
|
/**
|
|
* Was der Browser beim Broker anmelden muss, damit die Kacheln laufen.
|
|
*
|
|
* Nicht die einzelnen Topics, sondern ihre Zweige: aus vierzig Anmeldungen
|
|
* für Raumtemp werden so vier insgesamt. Der Zweig bringt ausserdem mit,
|
|
* was die Kachel nebenbei braucht - "Heating" und "mode" stehen in keiner
|
|
* Werteliste, das Heizsymbol lebt aber davon.
|
|
*/
|
|
function tileTopics()
|
|
{
|
|
$zweige = [];
|
|
foreach (roomsWithTile() as $room) {
|
|
foreach ($room["werte"] as $wert) {
|
|
$zweige[explode("/", $wert["topic"])[0] . "/#"] = true;
|
|
}
|
|
}
|
|
return array_keys($zweige);
|
|
}
|