Automatiken als Ausloeser: Editor, Uebersicht, Loeschen
Gegenstueck zur Runner-Seite. Der Editor selbst braucht nichts: das
gerechnete Geraet "Automatiken" fuehrt jede Automatik als Messwert, und
der Geraetewaehler listet ohnehin Geraete und deren Messwerte. Neu sind
nur die Operatoren des Datentyps `elapsed` ("+", "ab +", "vor +") und
sein Eingabefeld.
Drei Stellen wissen trotzdem davon:
pruefeKreis() lehnt beim Speichern ab, was sich mittelbar selbst
ausloesen wuerde. Im Betrieb waere ein Kreis kaum zu bemerken - die
Sperrzeit begrenzt ihn auf eine Ausloesung je lockout_secs, und im
Protokoll sieht das aus wie eine Automatik, die halt oft laeuft.
deleteAutomation() fragt vorher. fk_cond_state steht auf ON DELETE
CASCADE, mit dem Messwert verschwaende also still die Bedingung des
Nachfolgers: aus "zehn Minuten nach dem Wecker UND es ist hell" wuerde
ein blosses "es ist hell", und der Rollladen fuehre ab morgen jeden Tag
bei Sonnenaufgang hoch. Bei der letzten Bedingung faengt der Runner das
ab, bei einer von zweien niemand. Zur Wahl stehen Abhaengen (Bedingung
raus, Nachfolger pausiert) und Mitloeschen; automatisch mitzuloeschen
waere die falsche Vorgabe.
Die Uebersicht rueckt Nachfolger unter ihren Ausloeser ein und zeigt sie
eingeklappt. Das ist nur die Darstellung - jeder Nachfolger bleibt eine
vollwertige Automatik mit eigener Pause, eigenen Rahmenbedingungen und
eigener Zeile im Protokoll. Sobald "gruppiert" auch "gehoert dazu"
hiesse, waere zu klaeren, wem die Pause gehoert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+118
-5
@@ -10,7 +10,8 @@
|
||||
* GET ?action=editor&floor=OG Editor fuer eine neue Automatik
|
||||
* GET ?action=list&floor=OG Tabelle fuer die Karte "Automatismen"
|
||||
* POST ?action=save JSON-Rumpf, legt an oder ueberschreibt
|
||||
* POST ?action=delete {"id": N}
|
||||
* GET ?action=followers&id=N JSON: wer haengt an dieser Automatik?
|
||||
* POST ?action=delete {"id": N, "mode": "abhaengen"|"mitloeschen"}
|
||||
* POST ?action=toggle {"id": N} - pausieren / fortsetzen
|
||||
*/
|
||||
|
||||
@@ -45,7 +46,12 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$id = saveAutomation($body);
|
||||
jsonAntwort(["id" => $id]);
|
||||
case "delete":
|
||||
deleteAutomation(intval($body["id"] ?? 0));
|
||||
// Der Modus kommt aus der Rueckfrage im Browser. Unbekanntes
|
||||
// faellt auf das schonendere "abhaengen" zurueck - lieber eine
|
||||
// pausierte Automatik zu viel als eine geloeschte zu viel.
|
||||
$modus = ($body["mode"] ?? "") === "mitloeschen"
|
||||
? "mitloeschen" : "abhaengen";
|
||||
deleteAutomation(intval($body["id"] ?? 0), $modus);
|
||||
jsonAntwort(["ok" => true]);
|
||||
case "toggle":
|
||||
jsonAntwort(["enabled" => toggleAutomation(intval($body["id"] ?? 0))]);
|
||||
@@ -59,6 +65,13 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Wer haengt an dieser Automatik? -------------------------------------
|
||||
// Wird vor dem Loeschen gefragt, damit die Rueckfrage im Browser die Namen
|
||||
// nennen kann und nicht nur eine Zahl.
|
||||
if ($action === "followers") {
|
||||
jsonAntwort(["followers" => automationFollowers(intval($_GET["id"] ?? 0))]);
|
||||
}
|
||||
|
||||
// --- Uebersichtstabelle --------------------------------------------------
|
||||
if ($action === "list") {
|
||||
$tage = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
@@ -74,7 +87,77 @@ if ($action === "list") {
|
||||
. "<th class='d-none d-sm-table-cell'>Aktion</th>"
|
||||
. "<th class='d-none d-md-table-cell'>Wann</th>"
|
||||
. "<th class='d-none d-md-table-cell'>Zuletzt</th><th></th></tr></thead><tbody>";
|
||||
|
||||
/*
|
||||
* Eine Automatik, die von einer anderen ausgeloest wird, steht unter
|
||||
* ihr statt alphabetisch irgendwo dazwischen - sonst liest man
|
||||
* "Rollladen Magdalena morgens" und sieht nicht, woran es haengt.
|
||||
*
|
||||
* Eingeklappt, weil die Uebersicht sonst mit jeder Verkettung laenger
|
||||
* wird, ohne mehr zu sagen: der Ausloeser ist das, was man sucht. Der
|
||||
* Zaehler am Pfeil verraet, dass da noch etwas ist.
|
||||
*
|
||||
* Gruppiert wird nur die Darstellung. Jeder Nachfolger bleibt eine
|
||||
* vollwertige Automatik mit eigener Pause, eigenen Rahmenbedingungen
|
||||
* und eigener Zeile im Protokoll - sonst waere zu klaeren, wem die
|
||||
* Pause gehoert und was das Protokoll zeigt.
|
||||
*/
|
||||
$nachFolgern = [];
|
||||
foreach ($list as $a) {
|
||||
foreach ($a["triggers"] as $davor) {
|
||||
$nachFolgern[intval($davor)][] = intval($a["id"]);
|
||||
}
|
||||
}
|
||||
$vorhanden = [];
|
||||
foreach ($list as $a) {
|
||||
$vorhanden[intval($a["id"])] = $a;
|
||||
}
|
||||
// Wessen Ausloeser nicht in dieser Etage steht, faengt selbst oben an.
|
||||
$wurzeln = [];
|
||||
foreach ($list as $a) {
|
||||
$hatVater = false;
|
||||
foreach ($a["triggers"] as $davor) {
|
||||
if (isset($vorhanden[intval($davor)])) {
|
||||
$hatVater = true;
|
||||
}
|
||||
}
|
||||
if (!$hatVater) {
|
||||
$wurzeln[] = intval($a["id"]);
|
||||
}
|
||||
}
|
||||
$reihe = [];
|
||||
$einordnen = function ($id, $tiefe, $vater) use (&$einordnen, &$reihe, $nachFolgern, $vorhanden) {
|
||||
if (!isset($vorhanden[$id]) || isset($reihe[$id])) {
|
||||
return;
|
||||
}
|
||||
$reihe[$id] = ["auto" => $vorhanden[$id], "tiefe" => $tiefe,
|
||||
"vater" => $vater, "kinder" => 0];
|
||||
foreach ($nachFolgern[$id] ?? [] as $kind) {
|
||||
$einordnen($kind, $tiefe + 1, $id);
|
||||
}
|
||||
};
|
||||
foreach ($wurzeln as $id) {
|
||||
$einordnen($id, 0, 0);
|
||||
}
|
||||
// Was durch einen Kreis nicht erreicht wurde, kommt trotzdem mit.
|
||||
foreach ($list as $a) {
|
||||
$einordnen(intval($a["id"]), 0, 0);
|
||||
}
|
||||
// Gezaehlt wird erst jetzt, und nur was wirklich eingeordnet wurde:
|
||||
// wer zwei Ausloeser hat, steht nur unter dem ersten. Aus der rohen
|
||||
// Nachfolgerliste gezaehlt verspraeche der zweite Pfeil eine Zeile,
|
||||
// die er nicht aufklappen kann.
|
||||
foreach ($reihe as $eintrag) {
|
||||
if ($eintrag["vater"]) {
|
||||
$reihe[$eintrag["vater"]]["kinder"]++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($reihe as $eintrag) {
|
||||
$a = $eintrag["auto"];
|
||||
$tiefe = $eintrag["tiefe"];
|
||||
$kinder = $eintrag["kinder"];
|
||||
$vater = $eintrag["vater"];
|
||||
// Nur die aktiven Tage nennen. Sieben Haekchen untereinander
|
||||
// haben die Zeile frueher unnoetig hoch gemacht.
|
||||
$aktiveTage = [];
|
||||
@@ -101,13 +184,43 @@ if ($action === "list") {
|
||||
}
|
||||
|
||||
$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 "<tr" . $klasse . ">";
|
||||
echo "<td>" . htmlspecialchars($a["name"]) . "</td>";
|
||||
$klassen = [];
|
||||
if (!$a["enabled"]) {
|
||||
$klassen[] = "opacity-50";
|
||||
}
|
||||
if ($tiefe > 0) {
|
||||
// Eingeklappt starten: die Zeile gehoert dem Ausloeser, und
|
||||
// der steht direkt darueber.
|
||||
$klassen[] = "auto-folge";
|
||||
$klassen[] = "auto-von-" . $vater;
|
||||
$klassen[] = "d-none";
|
||||
}
|
||||
$klasse = $klassen ? " class='" . implode(" ", $klassen) . "'" : "";
|
||||
|
||||
// Der Pfeil klappt die Nachfolger auf. Er steht nur da, wo es
|
||||
// welche gibt - ein Pfeil ohne Inhalt waere eine Einladung ins
|
||||
// Leere.
|
||||
$griff = "";
|
||||
if ($kinder > 0) {
|
||||
$griff = "<button type='button' class='btn btn-link btn-sm p-0 me-1 text-decoration-none'"
|
||||
. " title='Abhängige Automatiken zeigen'"
|
||||
. " data-gruppe='" . $id . "'"
|
||||
. " onclick='toggleAutomationGroup(" . $id . ", this)'>"
|
||||
. "<i class='bi bi-chevron-right'></i> " . $kinder . "</button>";
|
||||
}
|
||||
$einzug = $tiefe > 0
|
||||
? "<span class='text-body-secondary me-1'>"
|
||||
. str_repeat(" ", $tiefe)
|
||||
. "<i class='bi bi-arrow-return-right'></i></span> "
|
||||
: "";
|
||||
|
||||
echo "<tr data-auto='" . $id . "'" . $klasse . ">";
|
||||
echo "<td class='text-nowrap'>" . $einzug . $griff
|
||||
. htmlspecialchars($a["name"]) . "</td>";
|
||||
echo "<td>" . htmlspecialchars($a["conditionText"]) . "</td>";
|
||||
echo "<td class='d-none d-sm-table-cell'>" . htmlspecialchars($a["actionText"]) . "</td>";
|
||||
echo "<td class='text-nowrap d-none d-md-table-cell'>" . htmlspecialchars($wann) . "</td>";
|
||||
|
||||
Reference in New Issue
Block a user