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:
2026-08-31 21:18:46 +02:00
co-authored by Claude Opus 5
parent 131d7cc46d
commit 17f972d081
5 changed files with 780 additions and 91 deletions
+100 -6
View File
@@ -208,13 +208,95 @@ function switchFloor(floor){
// eine Falle.
btn.hidden = !slider;
koerper.querySelectorAll("button[data-cmd]").forEach(k => {
k.addEventListener("click", () => schalte(k));
});
raumBedienungAktivieren(koerper);
raumMesswerteAktualisieren(teile[0], teile[1]);
})
.catch(fehler => { koerper.innerHTML = "Konnte den Raum nicht laden: " + fehler.message; });
}
/**
* Die Bedienelemente des Raum-Modals mit ihren Kommandos verbinden.
*
* Welches Geraet welches Element bekommt, entscheidet der Server
* (restricted/roomControls.php); hier haengen nur die Zuhoerer an den
* data-Attributen, die er dabei setzt.
*/
function raumBedienungAktivieren(koerper) {
koerper.querySelectorAll("button[data-cmd]").forEach(k => {
k.addEventListener("click", () => schalte(k));
});
// Schieberegler: waehrend des Ziehens nur die Beschriftung, geschickt
// wird erst beim Loslassen. Sonst bekaeme eine Jalousie bei jedem Pixel
// ein neues Ziel und kaeme aus dem Anfahren nicht mehr heraus.
koerper.querySelectorAll(".raum-regler").forEach(regler => {
const schieber = regler.querySelector("input[type=range]");
const anzeige = regler.querySelector(".raum-regler-wert");
const einheit = schieber.dataset.einheit || "";
schieber.addEventListener("input", () => { anzeige.textContent = schieber.value + einheit; });
schieber.addEventListener("change", () => {
const params = {};
params[regler.dataset.param] = schieber.value;
raumKommando(Number(regler.dataset.cmd), params, regler);
});
});
// Kippschalter: An und Aus sind zwei getrennte Kommandos. Kennt das
// Geraet nur "umschalten", tut es auch das - dann stimmt die Stellung
// hinterher, weil wir sie selbst gesetzt haben.
koerper.querySelectorAll(".raum-schalter").forEach(schalter => {
const kasten = schalter.querySelector("input[type=checkbox]");
const beschriftung = schalter.querySelector("label");
kasten.addEventListener("change", () => {
const ziel = kasten.checked ? schalter.dataset.ein : schalter.dataset.aus;
const cmd = ziel || schalter.dataset.um;
beschriftung.textContent = kasten.checked ? "An" : "Aus";
if (!cmd) return;
raumKommando(Number(cmd), {}, schalter);
});
});
// Farbe: der Browser bringt seinen Farbwaehler mit, das Geraet will drei
// Zahlen 0-255. Zerlegt wird deshalb erst hier.
koerper.querySelectorAll(".raum-farbe").forEach(feld => {
const eingabe = feld.querySelector("input[type=color]");
eingabe.addEventListener("change", () => {
const hex = eingabe.value;
const params = {};
params[feld.dataset.red] = parseInt(hex.substr(1, 2), 16);
params[feld.dataset.green] = parseInt(hex.substr(3, 2), 16);
params[feld.dataset.blue] = parseInt(hex.substr(5, 2), 16);
raumKommando(Number(feld.dataset.cmd), params, feld);
});
});
// Auswahllisten, die ohne zusaetzlichen Knopf schalten - Effekt, Preset.
koerper.querySelectorAll("select.raum-sofort").forEach(auswahl => {
auswahl.addEventListener("change", () => {
const params = {};
params[auswahl.dataset.param] = auswahl.value;
raumKommando(Number(auswahl.dataset.cmd), params, auswahl);
});
});
}
/**
* Die Ist-Werte im Heizungsteil aus dem laufenden MQTT-Strom fuellen.
*
* Der Browser hoert ohnehin schon auf Raumtemp/#, und ein Wert aus der
* Datenbank waere neben dem Regler nur ein Stand von vorhin.
*/
function raumMesswerteAktualisieren(etage, raum) {
const werte = mqttData["Raumtemp"]?.[etage]?.[raum];
if (!werte) return;
document.querySelectorAll("#roomView [data-mqtt]").forEach(feld => {
const wert = werte[feld.dataset.mqtt];
if (typeof wert !== "undefined") feld.textContent = wert;
});
const heizt = document.querySelector("#roomView [data-mqtt-heizung]");
if (heizt) heizt.classList.toggle("d-none", String(werte["Heating"]) !== "true");
}
/** Ein Kommando des angeklickten Knopfes ausfuehren. */
function schalte(knopf) {
const zeile = knopf.closest("[data-cmdrow]");
@@ -222,10 +304,22 @@ function switchFloor(floor){
if (zeile) {
zeile.querySelectorAll("[data-param]").forEach(f => { params[f.dataset.param] = f.value; });
}
knopf.disabled = true;
raumRequest("command", { command_id: Number(knopf.dataset.cmd), params: params })
raumKommando(Number(knopf.dataset.cmd), params, knopf);
}
/**
* Ein Kommando schicken und die Karte so lange sperren.
*
* Ein "Zu" an einer Jalousie dauert ueber eine Minute (die Mechanik will
* zweimal geschwenkt werden, siehe restricted/commands.php). Ohne sichtbare
* Sperre tippt man in der Zeit dreimal nach.
*/
function raumKommando(cmdId, params, element) {
const karte = element.closest(".raum-geraet") || element;
karte.classList.add("raum-warte");
raumRequest("command", { command_id: cmdId, params: params })
.then(antwort => {
knopf.disabled = false;
karte.classList.remove("raum-warte");
raumMeldung(antwort);
});
}