Raum zum Bearbeiten * POST ?action=save JSON-Rumpf, legt an oder ueberschreibt * POST ?action=delete {"id": N} * POST ?action=toggle {"id": N} - pausieren / fortsetzen * POST ?action=saveRooms {"": "OG/Bad", ...} */ require_once("../helper.php"); require_once("../restricted/automations.php"); if (!checkLogin()) { http_response_code(403); exit; } function jsonAntwort($data, $code = 200) { http_response_code($code); header("Content-Type: application/json; charset=utf-8"); echo json_encode($data, JSON_UNESCAPED_UNICODE); exit; } $action = $_GET["action"] ?? "editor"; $floor = in_array($_GET["floor"] ?? "", $floors, true) ? $_GET["floor"] : ""; // --- Schreibende Aufrufe ------------------------------------------------- if ($_SERVER["REQUEST_METHOD"] === "POST") { $body = json_decode(file_get_contents("php://input"), true); if (!is_array($body)) { jsonAntwort(["error" => "Konnte die Anfrage nicht lesen."], 400); } try { switch ($action) { case "save": $id = saveAutomation($body); jsonAntwort(["id" => $id]); case "delete": deleteAutomation(intval($body["id"] ?? 0)); jsonAntwort(["ok" => true]); case "toggle": jsonAntwort(["enabled" => toggleAutomation(intval($body["id"] ?? 0))]); case "saveRooms": jsonAntwort(["gespeichert" => saveRooms($body["rooms"] ?? [])]); default: jsonAntwort(["error" => "Unbekannte Aktion."], 400); } } catch (InvalidArgumentException $e) { jsonAntwort(["error" => $e->getMessage()], 400); } catch (Throwable $e) { jsonAntwort(["error" => "Speichern fehlgeschlagen: " . $e->getMessage()], 500); } } // --- Uebersichtstabelle -------------------------------------------------- if ($action === "list") { $tage = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]; $list = listAutomations($floor); if (!$list) { echo "

Für diese Etage ist noch nichts eingerichtet.

"; } else { echo "
"; // Je schmaler der Schirm, desto weniger Spalten: auf dem Handy bleiben // Name, Ausloeser und die Knoepfe. Sonst muss man seitwaerts scrollen, // um eine Automatik ueberhaupt pausieren zu koennen. echo "" . "" . "" . ""; foreach ($list as $a) { // Nur die aktiven Tage nennen. Sieben Haekchen untereinander // haben die Zeile frueher unnoetig hoch gemacht. $aktiveTage = []; for ($i = 0; $i < 7; $i++) { if ($a["weekdays"] & (1 << $i)) { $aktiveTage[] = $tage[$i]; } } $wann = count($aktiveTage) === 7 ? "täglich" : implode(" ", $aktiveTage); if ($a["window_from"] !== "00:00" || $a["window_to"] !== "23:59") { $wann .= ", " . $a["window_from"] . "–" . $a["window_to"]; } if (!$a["on_holiday"]) { $wann .= ", nicht an Feiertagen"; } if (!$a["on_vacation"]) { $wann .= ", nicht in den Ferien"; } if ($a["lockout_secs"] > 0) { $wann .= ", frühestens alle " . ($a["lockout_secs"] >= 60 ? intdiv($a["lockout_secs"], 60) . " Min." : $a["lockout_secs"] . " Sek."); } $zuletzt = $a["last_run"] ? date("d.m. H:i", strtotime($a["last_run"])) : "–"; $klasse = $a["enabled"] ? "" : " class='opacity-50'"; $pause = $a["enabled"] ? "bi-pause-fill" : "bi-play-fill"; $titel = $a["enabled"] ? "Pausieren" : "Fortsetzen"; $id = intval($a["id"]); echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; } echo "
NameAuslöserAktionWannZuletzt
" . htmlspecialchars($a["name"]) . "" . htmlspecialchars($a["conditionText"]) . "" . htmlspecialchars($a["actionText"]) . "" . htmlspecialchars($wann) . "" . $zuletzt . "" . " " . " " . "" . "
"; } exit; } // --- Geraete den Raeumen zuordnen --------------------------------------- if ($action === "rooms") { $labels = geraeteBeschriftungen(); $raeume = geraeteRaeume(); $vorschlag = raumVorschlaege(); $auswahl = raumListe(); $res = meshDb()->query("SELECT id, type FROM actors"); $geraete = []; while ($row = $res->fetch_assoc()) { $id = intval($row["id"]); $geraete[] = [ "id" => $id, "label" => $labels[$id] ?? "?", "type" => $row["type"], "room" => $raeume[$id] ?? null, "tipp" => $vorschlag[$id] ?? null, ]; } // Unzugeordnete nach oben: das ist die Arbeit, die noch zu tun ist. usort($geraete, function ($a, $b) { $offen = (int)!empty($b["room"]) - (int)!empty($a["room"]); return $offen !== 0 ? $offen : strcoll($a["label"], $b["label"]); }); $offen = count(array_filter($geraete, function ($g) { return empty($g["room"]); })); $tipps = count(array_filter($geraete, function ($g) { return empty($g["room"]) && !empty($g["tipp"]); })); echo "
"; echo "

Die Zuordnung ordnet die Geräteliste im Editor " . "nach Räumen statt nach Geräteart. Sie wird von Hand vergeben und bleibt bei " . "einem neuen Geräte-Suchlauf erhalten.

"; if ($tipps) { echo "
" . "" . $tipps . " Gerät(e) verraten ihren Raum im MQTT-Topic." . "
"; } echo "
"; echo "" . "" . ""; foreach ($geraete as $g) { echo ""; echo ""; echo ""; echo ""; } echo "
GerätArtRaum
" . htmlspecialchars($g["label"]) . "" . htmlspecialchars($g["type"]) . "
"; echo "

" . $offen . " von " . count($geraete) . " Geräten noch ohne Raum.

"; echo "
"; exit; } // --- Editor -------------------------------------------------------------- $id = intval($_GET["id"] ?? 0); $auto = $id > 0 ? loadAutomation($id) : null; if (!$auto) { $auto = emptyAutomation($floor); } // Geraetekatalog und Datensatz reisen im selben Dokument mit. Frueher holte // der Editor die Geraeteliste und je Zeile noch einmal deren Details nach - // zwei zusaetzliche Anfragen pro Bedingung, und die Dropdowns fuellten sich // asynchron, weshalb ein gespeicherter Wert nicht zuverlaessig ankam. $nutzdaten = json_encode([ "automation" => $auto, "devices" => deviceCatalog(), "lockouts" => lockoutChoices(), // Nur fuer die Reihenfolge der Raum-Gruppen: rooms.php gibt sie vor, und // Etage fuer Etage liest sich besser als alphabetisch. "rooms" => raumListe(), ], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>

Aktiver Zeitraum
Aktive Wochentage:
Sperrzeit nach dem Auslösen