Raeume ueber ihre feste Nummer: actors.room_id, Raum-Modal, Thermostat-Zweig

Geraete hingen ueber den Text "Etage/Raum" an ihrem Raum, das Raum-Modal
zerlegte "EG_Bad" am Unterstrich, und das Thermostat-Topic wurde aus Etage
und Name zusammengesetzt. Ein umbenannter Raum haette still alle Geraete,
seinen Temperaturregler und sein Heizsymbol verloren.

- actors.room (Text) -> actors.room_id, Fremdschluessel auf rooms, beim
  Loeschen eines Raums SET NULL. Alle 59 Zuordnungen uebernommen.
- automations.floor: ENUM(UG,EG,OG,AG) -> Verweis auf floors.
  Eine neue Etage kann damit auch Automatiken tragen.
- room.php nimmt ?room=<Nummer>; Solltemperatur geht an
  rooms.thermostat + /changeSetTemp.
- homeMQTT.js: Heizsymbol, Regler und Ist-Werte ueber mqttZweig(thermostat)
  statt mqttData.Raumtemp[Etage][Name].
- Raumvorschlaege in den Einstellungen: Messwerte unter dem Thermostat-
  Zweig eines Raums statt fest Raumtemp/<Etage>/<Name>.
- Schluessel in raumListe(), Katalog und Maske ist die Raumnummer als Text.

Geprueft gegen den Stand davor: Startseite gleich bis auf das Klickziel,
jede der 25 Kacheln oeffnet ihren eigenen Raum; Geraeteliste, Vorschlaege,
Editor-Katalog und die eigenen Geraete je Kachel gleich.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 10:58:30 +02:00
co-authored by Claude Opus 5
parent 3636a134d6
commit 62a8af3fc6
10 changed files with 170 additions and 86 deletions
+39 -23
View File
@@ -6,14 +6,29 @@
* 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").
* Welche Geraete zu einem Raum gehoeren, sagt die Spalte actors.room_id
* (Einstellungen -> Geraete).
*
* GET ?room=EG_Bad Inhalt des Modals
* GET ?room=12 Inhalt des Modals, 12 = rooms.id
* POST ?action=command {"command_id": N, "params": {...}} - ein Kommando
* POST ?action=temp {"room": "EG_Bad", "temp": 21} - Solltemperatur
* POST ?action=temp {"room": 12, "temp": 21} - Solltemperatur
*
* Frueher hiess der Raum "EG_Bad" und wurde am Unterstrich zerlegt. Das hielt
* weder ein Leerzeichen im Namen aus (erst nachgeruestet) noch eine
* Umbenennung - die feste Nummer haelt beides.
*/
/** Ein Raum nach seiner festen Nummer, oder null. */
function raumNachNummer($nr)
{
foreach (allRooms() as $r) {
if ($r["nr"] === intval($nr)) {
return $r;
}
}
return null;
}
// 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.
@@ -102,12 +117,14 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
exit;
}
if (($_GET["action"] ?? "") === "temp") {
// Bewusst derselbe Weg wie im alten Thermostat-Modal: das Topic
// Raumtemp/<Etage>/<Raum>/changeSetTemp.
// Das Leerzeichen muss durch: Raumnamen wie "Carport EG" haben
// eines, und ohne es findet der Vergleich weiter unten den Raum
// nicht mehr.
$raum = preg_replace('/[^A-Za-z0-9_ ]/', '', strval($body["room"] ?? ""));
// Derselbe Weg wie im alten Thermostat-Modal: <Zweig>/changeSetTemp.
// Der Zweig steht am Raum (rooms.thermostat) und wird nicht mehr
// aus Etage und Name zusammengesetzt - ein Thermostat in einem
// anderen Haus meldet vielleicht unter einem ganz anderen.
$raum = raumNachNummer($body["room"] ?? 0);
if (!$raum || empty($raum["thermostat"])) {
raumAntwort(["error" => "Dieser Raum hat kein Thermostat."], 400);
}
$temp = intval($body["temp"] ?? 0);
if ($temp < 10 || $temp > 30) {
raumAntwort(["error" => "Temperatur außerhalb 1030 °C."], 400);
@@ -116,7 +133,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
if (!$mqtt->connect(true, null, "", "")) {
raumAntwort(["error" => "Keine Verbindung zum MQTT-Broker."], 500);
}
$mqtt->publish("Raumtemp/" . str_replace("_", "/", $raum) . "/changeSetTemp",
$mqtt->publish(rtrim($raum["thermostat"], "/") . "/changeSetTemp",
strval($temp), 0, false);
$mqtt->close();
raumAntwort(["ok" => "Solltemperatur " . $temp . " °C"]);
@@ -156,17 +173,13 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
// --- Inhalt des Modals ----------------------------------------------------
// Das Leerzeichen gehoert zu den erlaubten Zeichen. Ohne es wurde aus
// "AG_Carport EG" ein "AG_CarportEG", und der Vergleich gegen die
// gespeicherte Zuordnung "AG/Carport EG" ging ins Leere - das Modal meldete
// dann "Diesem Raum ist noch nichts zugeordnet". Aufgefallen ist es erst mit
// dem Aussengelaende: dort haben sechs von neun Raeumen ein Leerzeichen im
// Namen, auf den anderen Etagen keiner von achtzehn.
$schluessel = preg_replace('/[^A-Za-z0-9_ ]/', '', strval($_GET["room"] ?? ""));
$teile = explode("_", $schluessel, 2);
$etage = $teile[0] ?? "";
$name = $teile[1] ?? "";
$raumKey = $etage . "/" . $name;
$raum = raumNachNummer($_GET["room"] ?? 0);
if (!$raum) {
echo "<p class='text-body-secondary mb-0'>Diesen Raum gibt es nicht (mehr).</p>";
exit;
}
// Derselbe Schluessel wie actors.room_id im Katalog - als Text, siehe raumListe().
$raumKey = strval($raum["nr"]);
// Alles im Raum, sortiert nach Bedienform. Das Thermostat selbst wird nicht
// als Karte gezeichnet: es wird zum Schieberegler im Heizungs-Reiter und
@@ -214,7 +227,10 @@ if ($hatThermostat) {
$fertig["heizung"] = $karte;
}
echo "<div class='p-1' id='roomView' data-room='" . htmlspecialchars($schluessel, ENT_QUOTES) . "'>";
// Der Thermostat-Zweig reist mit: das Modal fuellt Ist-Werte und Heizsymbol
// aus dem laufenden MQTT-Strom und muss wissen, wo es nachsehen soll.
echo "<div class='p-1' id='roomView' data-room='" . $raum["nr"] . "'"
. " data-thermostat='" . htmlspecialchars((string)$raum["thermostat"], ENT_QUOTES) . "'>";
if (!$gruppen && !$fertig) {
echo "<p class='text-body-secondary mb-0'>Diesem Raum ist noch nichts zugeordnet. "