Das Raum-Modal fand die Geraete von "Carport EG" und den fuenf anderen AG-Raeumen mit Leerzeichen nicht - es meldete stattdessen, dem Raum sei nichts zugeordnet. ajax/room.php bereinigte den Schluessel mit [^A-Za-z0-9_] und warf dabei das Leerzeichen weg: aus "AG_Carport EG" wurde "AG_CarportEG", der Vergleich gegen die gespeicherte Zuordnung "AG/Carport EG" ging ins Leere. Aufgefallen ist es erst jetzt, weil sechs von neun AG-Raeumen ein Leerzeichen im Namen haben und keiner der achtzehn auf den anderen Etagen. Durchgespielt: vorher 6 von 27 Raeumen kaputt, danach 0. Messwerte auf den AG-Kacheln, wo es etwas zu messen gibt: Garage solarManager/evPower - die Wattpilot der EG-Partei Carport OG solarManager/evPowerOG - der go-e Charger Garten Wetter/Regen [tage1] - Regen des laufenden Tages Veranda, Terrasse und Pergola behalten ihren Namen: dort haengen Licht und Rollladen, und die zeigt das Raum-Modal. Carport EG bleibt vorerst ohne - die Hoymiles liegen auf solarManager/invertersN, positionsnummeriert, und welcher Strang der Carport ist, steht nirgends im Code. Geraten wird das nicht. tileTopics() zieht Wetter/# von selbst nach, das Abonnement folgt aus dem Topic. Ausserdem der weisse Saum am freigestellten Rendering. Die feste Schwelle liess den ein bis zwei Punkte breiten Uebergang stehen - gemessen 247, 250, 241, 204, 145, und die 241 blieb knapp haengen und leuchtete vor dem schwarzen Grund. Die Flutfuellung frisst jetzt entlang der Kante in immer dunklere Punkte nach und legt zuletzt einen Punkt mit halber Deckung an, damit die Kante nicht treppig wird. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
153 lines
6.6 KiB
PHP
153 lines
6.6 KiB
PHP
<?php
|
||
/**
|
||
* Raum-Modal: alles, was in einem Raum haengt, auf einer Seite.
|
||
*
|
||
* Loest das frühere Thermostat-Modal ab. Der Grundriss zeigt Raeume, also
|
||
* sollte ein Klick darauf den Raum oeffnen und nicht nur seine Heizung - die
|
||
* Rollladen im selben Zimmer waren bisher gar nicht erreichbar.
|
||
*
|
||
* Welche Geraete zu einem Raum gehoeren, sagt die Spalte actors.room (siehe
|
||
* den Zuordnungsdialog in der Karte "Automatismen").
|
||
*
|
||
* GET ?room=EG_Bad Inhalt des Modals
|
||
* POST ?action=command {"command_id": N, "params": {...}} - ein Kommando
|
||
* POST ?action=temp {"room": "EG_Bad", "temp": 21} - Solltemperatur
|
||
*/
|
||
|
||
// Ein "Zu" an einer Jalousie faehrt herunter, wartet und neigt danach - das
|
||
// dauert in der Summe ueber eine Minute. Die Vorgabe von PHP reicht dafuer
|
||
// nicht; ajax/tahoma.php macht es an derselben Stelle genauso.
|
||
set_time_limit(180);
|
||
|
||
require_once("../helper.php");
|
||
require_once("../restricted/automations.php");
|
||
require_once("../restricted/commands.php");
|
||
require_once("../restricted/roomControls.php");
|
||
require_once("./phpMQTT.php");
|
||
|
||
if (!checkLogin()) {
|
||
http_response_code(403);
|
||
exit;
|
||
}
|
||
|
||
function raumAntwort($data, $code = 200)
|
||
{
|
||
http_response_code($code);
|
||
header("Content-Type: application/json; charset=utf-8");
|
||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
// --- Schaltende Aufrufe ---------------------------------------------------
|
||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||
$body = json_decode(file_get_contents("php://input"), true);
|
||
if (!is_array($body)) {
|
||
raumAntwort(["error" => "Konnte die Anfrage nicht lesen."], 400);
|
||
}
|
||
try {
|
||
if (($_GET["action"] ?? "") === "command") {
|
||
$text = executeCommand(intval($body["command_id"] ?? 0),
|
||
is_array($body["params"] ?? null) ? $body["params"] : [],
|
||
!empty($body["probe"]));
|
||
raumAntwort(["ok" => $text]);
|
||
}
|
||
if (($_GET["action"] ?? "") === "temp") {
|
||
// Bewusst derselbe Weg wie im alten Thermostat-Modal: das Topic
|
||
// Raumtemp/<Etage>/<Raum>/changeSetTemp.
|
||
// Das Leerzeichen muss durch: Raumnamen wie "Carport EG" haben
|
||
// eines, und ohne es findet der Vergleich weiter unten den Raum
|
||
// nicht mehr.
|
||
$raum = preg_replace('/[^A-Za-z0-9_ ]/', '', strval($body["room"] ?? ""));
|
||
$temp = intval($body["temp"] ?? 0);
|
||
if ($temp < 10 || $temp > 30) {
|
||
raumAntwort(["error" => "Temperatur außerhalb 10–30 °C."], 400);
|
||
}
|
||
$mqtt = new Bluerhinos\phpMQTT(MQTT_BROKER, MQTT_PORT, "RoomTempSetter_" . getmypid());
|
||
if (!$mqtt->connect(true, null, "", "")) {
|
||
raumAntwort(["error" => "Keine Verbindung zum MQTT-Broker."], 500);
|
||
}
|
||
$mqtt->publish("Raumtemp/" . str_replace("_", "/", $raum) . "/changeSetTemp",
|
||
strval($temp), 0, false);
|
||
$mqtt->close();
|
||
raumAntwort(["ok" => "Solltemperatur " . $temp . " °C"]);
|
||
}
|
||
raumAntwort(["error" => "Unbekannte Aktion."], 400);
|
||
} catch (InvalidArgumentException $e) {
|
||
raumAntwort(["error" => $e->getMessage()], 400);
|
||
} catch (Throwable $e) {
|
||
raumAntwort(["error" => $e->getMessage()], 500);
|
||
}
|
||
}
|
||
|
||
|
||
// --- Inhalt des Modals ----------------------------------------------------
|
||
// Das Leerzeichen gehoert zu den erlaubten Zeichen. Ohne es wurde aus
|
||
// "AG_Carport EG" ein "AG_CarportEG", und der Vergleich gegen die
|
||
// gespeicherte Zuordnung "AG/Carport EG" ging ins Leere - das Modal meldete
|
||
// dann "Diesem Raum ist noch nichts zugeordnet". Aufgefallen ist es erst mit
|
||
// dem Aussengelaende: dort haben sechs von neun Raeumen ein Leerzeichen im
|
||
// Namen, auf den anderen Etagen keiner von achtzehn.
|
||
$schluessel = preg_replace('/[^A-Za-z0-9_ ]/', '', strval($_GET["room"] ?? ""));
|
||
$teile = explode("_", $schluessel, 2);
|
||
$etage = $teile[0] ?? "";
|
||
$name = $teile[1] ?? "";
|
||
$raumKey = $etage . "/" . $name;
|
||
|
||
// Alles im Raum, sortiert nach Bedienform. Das Thermostat selbst wird nicht
|
||
// als Karte gezeichnet: es wird zum Schieberegler im Heizungs-Reiter und
|
||
// braucht daneben keine zweite Bedienung - "Solltemperatur" stand sonst
|
||
// zweimal im Modal.
|
||
//
|
||
// Ob es einen Heizungs-Reiter gibt, entscheidet genau das: haengt in diesem
|
||
// Raum ein Thermostat? Frueher stand hier "hat der Raum eine Kachel" - das
|
||
// stimmte, solange nur Raeume mit Thermostat eine bekamen. Seit auch Kueche
|
||
// und Technik eine haben, bekamen sie einen Heizungsregler fuer eine
|
||
// Heizung, die es dort nicht gibt.
|
||
$katalog = deviceCatalog();
|
||
$hatThermostat = false;
|
||
$gruppen = [];
|
||
foreach ($katalog as $d) {
|
||
if ($d["room"] !== $raumKey) {
|
||
continue;
|
||
}
|
||
$form = bedienform($d);
|
||
if ($form === "heizung") {
|
||
$hatThermostat = true;
|
||
continue;
|
||
}
|
||
$gruppen[$form][] = $d;
|
||
}
|
||
|
||
// Die Heizung ist eine Gruppe wie die anderen, nur baut sie sich nicht aus
|
||
// einer Geraeteliste: ein Regler fuer den Raum statt einer Karte je Geraet.
|
||
$fertig = [];
|
||
if ($hatThermostat) {
|
||
$karte = "<div class='card mb-2 raum-geraet'><div class='card-body py-2'>"
|
||
. "<div class='d-flex justify-content-between align-items-baseline'>"
|
||
. "<label class='form-label mb-0 small' for='modal-slider'>Solltemperatur</label>"
|
||
. "<span id='modal-slider-label' class='fw-semibold text-nowrap ms-2' style='color:#297195'>–</span>"
|
||
. "</div>"
|
||
. "<input type='range' id='modal-slider' class='form-range range-color-track' min='10' max='30'>"
|
||
// Der Ist-Zustand kommt live ueber MQTT, nicht aus der Datenbank -
|
||
// der Browser hoert ohnehin schon auf Raumtemp/#, und ein Wert von
|
||
// vorhin waere neben dem Regler nur irrefuehrend.
|
||
. "<div class='d-flex flex-wrap gap-3 small text-body-secondary'>"
|
||
. "<span><i class='bi bi-thermometer-half me-1'></i><span data-mqtt='Temp[degC]'>–</span> °C</span>"
|
||
. "<span><i class='bi bi-droplet me-1'></i><span data-mqtt='rHum[%]'>–</span> %rF</span>"
|
||
. "<span data-mqtt-heizung class='d-none'><i class='bi bi-fire text-danger me-1'></i>heizt</span>"
|
||
. "</div></div></div>";
|
||
$fertig["heizung"] = $karte;
|
||
}
|
||
|
||
echo "<div class='p-1' id='roomView' data-room='" . htmlspecialchars($schluessel, ENT_QUOTES) . "'>";
|
||
|
||
if (!$gruppen && !$fertig) {
|
||
echo "<p class='text-body-secondary mb-0'>Diesem Raum ist noch nichts zugeordnet. "
|
||
. "Das geht über das Haus-Symbol in der Karte „Automatismen“.</p>";
|
||
}
|
||
|
||
echo zeichneGruppen($gruppen, $fertig);
|
||
|
||
echo "<div id='roomFeedback' class='small mt-2'></div>";
|
||
echo "</div>";
|