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>
This commit is contained in:
+39
-84
@@ -22,6 +22,7 @@ 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()) {
|
||||
@@ -75,6 +76,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- Inhalt des Modals ----------------------------------------------------
|
||||
$schluessel = preg_replace('/[^A-Za-z0-9_]/', '', strval($_GET["room"] ?? ""));
|
||||
$teile = explode("_", $schluessel, 2);
|
||||
@@ -90,106 +92,59 @@ foreach (allRooms() as $raum) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
$geraete = array_values(array_filter($katalog, function ($d) use ($raumKey) {
|
||||
return $d["room"] === $raumKey && count($d["commands"]) > 0;
|
||||
}));
|
||||
$messwerte = array_values(array_filter($katalog, function ($d) use ($raumKey) {
|
||||
return $d["room"] === $raumKey && count($d["states"]) > 0 && count($d["commands"]) === 0;
|
||||
}));
|
||||
$gruppen = [];
|
||||
foreach ($katalog as $d) {
|
||||
if ($d["room"] !== $raumKey) {
|
||||
continue;
|
||||
}
|
||||
$form = bedienform($d);
|
||||
if ($form === "heizung") {
|
||||
continue;
|
||||
}
|
||||
$gruppen[$form][] = $d;
|
||||
}
|
||||
|
||||
echo "<div class='p-2' id='roomView' data-room='" . htmlspecialchars($schluessel, ENT_QUOTES) . "'>";
|
||||
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 "<label class='form-label mb-0'>Solltemperatur "
|
||||
. "<span id='modal-slider-label' style='font-size:15px;color:#297195;display:inline-block'>–</span></label>";
|
||||
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'>";
|
||||
echo "<div class='d-flex justify-content-between small text-body-secondary'>"
|
||||
. "<span>10 °C</span><span>30 °C</span></div>";
|
||||
// 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 (!$geraete && !$messwerte && !$hatThermostat) {
|
||||
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 ($geraete as $d) {
|
||||
echo "<div class='card mb-2'><div class='card-body py-2'>";
|
||||
echo "<div class='fw-bold small mb-2'>" . htmlspecialchars($d["label"])
|
||||
. " <span class='fw-normal text-body-secondary'>" . htmlspecialchars($d["type"]) . "</span></div>";
|
||||
|
||||
// Kommandos ohne Parameter werden Knoepfe nebeneinander - "Auf", "Zu",
|
||||
// "Stop" will man antippen und nicht erst einstellen. Die Reihenfolge in
|
||||
// der Datenbank ist die des Geraete-Suchlaufs und damit Zufall; die
|
||||
// haeufig gebrauchten kommen deshalb nach vorne.
|
||||
$knoepfe = array_values(array_filter($d["commands"], function ($c) { return !count($c["params"]); }));
|
||||
$vorne = ["Auf", "Zu", "Stop", "An", "Aus", "turn_on", "turn_off", "toggle"];
|
||||
usort($knoepfe, function ($a, $b) use ($vorne) {
|
||||
$ia = array_search($a["name"], $vorne, true);
|
||||
$ib = array_search($b["name"], $vorne, true);
|
||||
$ia = $ia === false ? 99 : $ia;
|
||||
$ib = $ib === false ? 99 : $ib;
|
||||
return $ia === $ib ? strcoll($a["name"], $b["name"]) : $ia - $ib;
|
||||
});
|
||||
if ($knoepfe) {
|
||||
echo "<div class='d-flex flex-wrap gap-1 mb-2'>";
|
||||
foreach ($knoepfe as $c) {
|
||||
echo "<button type='button' class='btn btn-outline-primary btn-sm' data-cmd='" . $c["id"] . "'>"
|
||||
. htmlspecialchars($c["name"]) . "</button>";
|
||||
}
|
||||
echo "</div>";
|
||||
foreach (raumGruppenTitel() as $form => $titel) {
|
||||
if (empty($gruppen[$form])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($d["commands"] as $c) {
|
||||
if (!count($c["params"])) {
|
||||
continue;
|
||||
}
|
||||
echo "<div class='input-group input-group-sm mb-1 raum-cmd' data-cmdrow='" . $c["id"] . "'>";
|
||||
echo "<span class='input-group-text'>" . htmlspecialchars($c["name"]) . "</span>";
|
||||
foreach ($c["params"] as $p) {
|
||||
if (count($c["params"]) > 1) {
|
||||
echo "<span class='input-group-text'>" . htmlspecialchars($p["name"]) . "</span>";
|
||||
}
|
||||
if ($p["options"]) {
|
||||
echo "<select class='form-select' data-param='" . $p["id"] . "'>";
|
||||
foreach ($p["options"] as $o) {
|
||||
echo "<option value='" . htmlspecialchars($o["value"], ENT_QUOTES) . "'>"
|
||||
. htmlspecialchars($o["label"]) . "</option>";
|
||||
}
|
||||
echo "</select>";
|
||||
} else {
|
||||
echo "<input type='" . ($p["input"] === "number" ? "number" : "text")
|
||||
. "' class='form-control' data-param='" . $p["id"] . "'"
|
||||
. ($p["min"] !== null ? " min='" . $p["min"] . "'" : "")
|
||||
. ($p["max"] !== null ? " max='" . $p["max"] . "'" : "")
|
||||
. " value='" . ($p["min"] !== null ? intval($p["min"]) : 0) . "'>";
|
||||
}
|
||||
}
|
||||
echo "<button type='button' class='btn btn-outline-primary' data-cmd='" . $c["id"] . "'>"
|
||||
. "<i class='bi bi-play-fill'></i></button>";
|
||||
echo "</div>";
|
||||
// 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]);
|
||||
}
|
||||
echo "</div></div>";
|
||||
}
|
||||
|
||||
// Geraete ohne Kommandos: nur anzeigen, was sie melden.
|
||||
foreach ($messwerte as $d) {
|
||||
echo "<div class='card mb-2'><div class='card-body py-2'>";
|
||||
echo "<div class='fw-bold small mb-1'>" . htmlspecialchars($d["label"])
|
||||
. " <span class='fw-normal text-body-secondary'>" . htmlspecialchars($d["type"]) . "</span></div>";
|
||||
echo "<div class='small text-body-secondary'>";
|
||||
$teile = [];
|
||||
foreach (array_slice($d["states"], 0, 8) as $s) {
|
||||
$teile[] = htmlspecialchars($s["name"]) . ": " . htmlspecialchars(strval($s["value"]))
|
||||
. ($s["unit"] ? " " . htmlspecialchars($s["unit"]) : "");
|
||||
foreach ($gruppen[$form] as $d) {
|
||||
echo zeichneGeraet($d, $form);
|
||||
}
|
||||
echo implode(" · ", $teile);
|
||||
if (count($d["states"]) > 8) {
|
||||
echo " · … " . (count($d["states"]) - 8) . " weitere";
|
||||
}
|
||||
echo "</div></div></div>";
|
||||
}
|
||||
|
||||
echo "<div id='roomFeedback' class='small mt-2'></div>";
|
||||
|
||||
Reference in New Issue
Block a user