Das Modal zeigte jedes Kommando so, wie es in der Datenbank steht: eine
Jalousie kam mit acht Rohbefehlen daher, darunter "Winken", die Farbe einer
Lampe als drei Zahlenfelder 0-255, ein Stromzaehler als abgeschnittene
Textzeile. Die Solltemperatur stand doppelt da - einmal als Schieberegler,
einmal als Zahlenfeld der Thermostat-Karte.
Jetzt entscheidet restricted/roomControls.php, welches Geraet welches
Bedienelement bekommt, und zwar an seinen Faehigkeiten statt am Typnamen:
hat es ein "setClosure"? ein An/Aus-Paar? drei Parameter rot/gruen/blau?
Damit bekommt auch ein neu gefundenes Geraet sein passendes Element, ohne
dass jemand einen Typnamen nachtraegt.
Beschattung Auf/Stop/Zu, darunter Position und Neigung als Schieber
auf dem zuletzt gemeldeten Stand; "Winken" und
"my-Position" im Mehr-Menue
Licht Kippschalter, Helligkeit, Farbwaehler des Browsers,
Effekt und Preset als Auswahl
Schalten ein Kippschalter statt dreier Knoepfe
Messwerte Wertegitter, die sechs wichtigsten sichtbar - beim
Wasserzaehler standen vorher MAC-Adresse und freier
Speicher vorn und der Verbrauch hinter "12 weitere"
Heizung nur noch der Schieberegler, daneben Ist-Temperatur und
Feuchte live aus dem MQTT-Strom
Ab zwei Geraeten einer Art ordnet eine Ueberschrift; darunter waere sie
nur Aufwand. Schieberegler schicken beim Loslassen, nicht beim Ziehen -
sonst bekaeme eine Jalousie bei jedem Pixel ein neues Ziel. Waehrend ein
Kommando unterwegs ist, sperrt sich die Karte sichtbar: ein "Zu" dauert
ueber eine Minute, und ohne Sperre tippt man in der Zeit nach.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
152 lines
6.0 KiB
PHP
152 lines
6.0 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;
|
||
}
|
||
|
||
echo "<div class='p-1' id='roomView' data-room='" . htmlspecialchars($schluessel, ENT_QUOTES) . "'>";
|
||
|
||
if ($hatThermostat) {
|
||
echo "<div class='card mb-2'><div class='card-body py-2'>";
|
||
echo "<div class='d-flex justify-content-between align-items-baseline'>";
|
||
echo "<label class='form-label mb-0 small' for='modal-slider'>Solltemperatur</label>";
|
||
echo "<span id='modal-slider-label' class='fw-semibold text-nowrap ms-2' style='color:#297195'>–</span>";
|
||
echo "</div>";
|
||
echo "<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.
|
||
echo "<div class='d-flex flex-wrap gap-3 small text-body-secondary'>";
|
||
echo "<span><i class='bi bi-thermometer-half me-1'></i><span data-mqtt='Temp[degC]'>–</span> °C</span>";
|
||
echo "<span><i class='bi bi-droplet me-1'></i><span data-mqtt='rHum[%]'>–</span> %rF</span>";
|
||
echo "<span data-mqtt-heizung class='d-none'><i class='bi bi-fire text-danger me-1'></i>heizt</span>";
|
||
echo "</div>";
|
||
echo "</div></div>";
|
||
}
|
||
|
||
if (!$gruppen && !$hatThermostat) {
|
||
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>";
|
||
}
|
||
|
||
foreach (raumGruppenTitel() as $form => $titel) {
|
||
if (empty($gruppen[$form])) {
|
||
continue;
|
||
}
|
||
// Eine einzelne Lampe braucht keine Ueberschrift "Licht" - der Name der
|
||
// Lampe steht direkt darunter. Ueberschriften ordnen erst ab zwei.
|
||
if (count($gruppen[$form]) > 1) {
|
||
echo gruppenTitel($titel[0], $titel[1]);
|
||
}
|
||
foreach ($gruppen[$form] as $d) {
|
||
echo zeichneGeraet($d, $form);
|
||
}
|
||
}
|
||
|
||
echo "<div id='roomFeedback' class='small mt-2'></div>";
|
||
echo "</div>";
|