Veranda und Terrasse gibt es zweimal, je Wohnpartei eine. In der Draufsicht
laege das obere Paar genau auf dem unteren: nicht auseinanderzuhalten und
schon gar nicht getrennt anzuklicken. Deshalb wird AG nicht flach von oben
gezeichnet, sondern schraeg von Suedwesten - von dort sieht man den Hof, auf
den die Veranden zeigen, und die Ostseite des Hauses traegt nichts, was
vorkommen muesste.
restricted/aussenplan.php haelt die Projektion und die Geometrie. Gezeichnet
wird in Militaerprojektion, nicht in echter Perspektive:
X = x·cos φ − y·sin φ
Y = (x·sin φ + y·cos φ)·STAUCHUNG − z·UEBERHOEHUNG
Der Grundriss bleibt dabei masshaltig - Geraden bleiben gerade, Rechtecke
Rechtecke. Genau deshalb diese und keine perspektivische: die Flaechen lassen
sich am Luftbild von oben abklicken, und dieselben Zahlen ergeben ohne Umweg
die Schraegsicht. Eine echte Perspektive muesste man neu vermessen.
Das Vorzeichen des Winkels bestimmt die Himmelsrichtung. Bei +35° zeigt der
Y-Gradient nach Osten und Sueden, die naechste Ecke waere die suedoestliche;
fuer Suedwesten muss er negativ sein. Was die Paare trennt, ist ohnehin nicht
die Drehung, sondern Stauchung und Ueberhoehung - bei 1,4 stehen Terrasse EG
und Terrasse OG rund 34 Einheiten auseinander, eine Kachel ist 19 hoch.
Hoehen: Traufe 6 m, First 9 m, Carport 2,5 m. Die OG-Ebene ist mit 3 m
angenommen - die halbe Traufhoehe bei zwei Geschossen.
tools/lageplan.php nimmt dafuer jetzt je Flaeche eine Hoehe entgegen und
zeigt die Schraegsicht live neben dem Luftbild: nur dort sieht man beim
Abklicken, ob die Paare wirklich auseinandergehen. Die Vorschau rechnet
dieselben Formeln im Browser nach; massgeblich bleibt der PHP-Code, von dem
spaeter die gezeichnete Etage und die Kachelpositionen kommen.
Dazu die Raeume: aus "Veranda" und "Terrasse" werden je zwei, eine pro
Partei. Welche der vier Geraete zu welcher gehoeren, sagt kein Name - die
beiden Wechselrichter tragen immerhin "OG" ("Veranda OG2-4", "Veranda
UG/OG1"), Licht und Rollladen der Terrasse gar nichts. Sie stehen deshalb
vorlaeufig auf Veranda OG und Terrasse EG.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
209 lines
9.6 KiB
PHP
209 lines
9.6 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.
|
|
// 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", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Carport OG", "id" => "carport_og", "werte" => []],
|
|
// 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", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Veranda OG", "id" => "veranda_og", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Terrasse EG", "id" => "terrasse_eg", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Terrasse OG", "id" => "terrasse_og", "werte" => []],
|
|
["floor" => "AG", "mqtt" => "Pergola", "id" => "pergola", "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);
|
|
}
|