Im Raum-Modal wirkt jedes Bedienelement sofort - der Rollladen faehrt, der Schalter schaltet, die Lampe leuchtet. Nur die Solltemperatur wartete auf "Save changes". Sie schickt jetzt beim Loslassen des Reglers, wie die Rollladenregler daneben, und der Knopf ist im Raum-Modal weg. Einer, der nur fuer einen der Reiter gilt, waere in allen anderen eine Falle. Das Modal bleibt nach dem Schicken offen; frueher schloss es sich, weil der Knopf das Ende der Bedienung war. Wer dreimal nachjustiert, will nicht dreimal neu aufmachen. Die Heizungskarte traegt dafuer dieselbe Sperre wie die Geraetekarten - waehrend das Kommando unterwegs ist, ist sie gesperrt. Dabei einen Fehler mitgenommen: der Speichern-Knopf gehoert allen Modals gemeinsam, und wer ihn versteckt, versteckt ihn fuer alle. Ein Raum ohne Thermostat liess ihn schon bisher verschwinden - danach hatten der AutoAction-Editor und die Raumzuordnung bis zum Neuladen keinen Speichern-Knopf mehr. Beide holen ihn jetzt wieder hervor. Nachgemessen: im Raum-Modal versteckt, im Editor und in der Raumzuordnung sichtbar. Die Fahrknoepfe sind von 34 auf 50 Pixel gewachsen - sie sind das, was man im Vorbeigehen antippt, die Regler daneben braucht man seltener. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
143 lines
5.9 KiB
PHP
143 lines
5.9 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.
|
||
$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 ----------------------------------------------------
|
||
$schluessel = preg_replace('/[^A-Za-z0-9_]/', '', strval($_GET["room"] ?? ""));
|
||
$teile = explode("_", $schluessel, 2);
|
||
$etage = $teile[0] ?? "";
|
||
$name = $teile[1] ?? "";
|
||
$raumKey = $etage . "/" . $name;
|
||
|
||
// Hat der Raum ein Thermostat? Nur dann gibt es etwas zu heizen.
|
||
$hatThermostat = false;
|
||
foreach (allRooms() as $raum) {
|
||
if ($raum["floor"] === $etage && $raum["mqtt"] === $name) {
|
||
$hatThermostat = isset($raum["x"]); // Kachel = Thermostat vorhanden
|
||
}
|
||
}
|
||
|
||
// Alles im Raum, sortiert nach Bedienform. Das Thermostat selbst faellt
|
||
// heraus: es wird oben zum Schieberegler und braucht daneben keine zweite
|
||
// Bedienung - "Solltemperatur" stand vorher zweimal im Modal.
|
||
$katalog = deviceCatalog();
|
||
$gruppen = [];
|
||
foreach ($katalog as $d) {
|
||
if ($d["room"] !== $raumKey) {
|
||
continue;
|
||
}
|
||
$form = bedienform($d);
|
||
if ($form === "heizung") {
|
||
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>";
|