Files
Smart-Dashboard/ajax/room.php
T
adminandClaude Opus 5 17f972d081 Raum-Modal: Bedienelemente statt Kommandoliste
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>
2026-08-31 21:18:46 +02:00

152 lines
6.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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 1030 °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>";