diff --git a/ajax/AutoAction.php b/ajax/AutoAction.php index c582852..5575ff8 100644 --- a/ajax/AutoAction.php +++ b/ajax/AutoAction.php @@ -312,7 +312,6 @@ $nutzdaten = json_encode([ "automation" => $auto, "devices" => deviceCatalog(), "lockouts" => lockoutChoices(), - "haltezeiten" => haltezeitChoices(), "ferien" => kalendertagChoices("Ferien", "in den Ferien"), "feiertage" => kalendertagChoices("Feiertage", "an Feiertagen"), "tagvorlagen" => tagVorlagen(), @@ -467,14 +466,21 @@ $nutzdaten = json_encode([
- Auslösen - + Auslösen erst nach + + Min. am Stück
diff --git a/doku/automatiken.md b/doku/automatiken.md index 9d4d0b2..1b2cfbf 100644 --- a/doku/automatiken.md +++ b/doku/automatiken.md @@ -170,7 +170,7 @@ sonst steht in der Übersicht etwas anderes als im Editor. | `next_day` | Der Rahmen gilt für **morgen** (Vorabend-Form) | Uhrzeit und Fenster bleiben bei heute | | `window_from`/`window_to` | Zeitfenster; `von > bis` heißt über Mitternacht | | | `lockout_secs` | Sperrzeit nach einem Lauf (0 / 60 / 900) | | -| `hold_secs` | Haltezeit: so lange muss die Bedingung **ununterbrochen** erfüllt sein (0 = sofort) | Zählt im Speicher des Runners — ein Neustart fängt die Zeit von vorn an | +| `hold_secs` | Haltezeit: so lange muss die Bedingung **ununterbrochen** erfüllt sein (0 = sofort, frei in Minuten bis 24 Std.) | Zählt im Speicher des Runners — ein Neustart fängt die Zeit von vorn an | | `once_per_day` | höchstens ein Lauf je Kalendertag | | | `force_once` | am Ende des Fensters notfalls doch ausführen | greift nur, wenn im Fenster gar nichts lief | @@ -447,5 +447,6 @@ räumt der Runner selbst weg. | … eine neue Geräteart anschließen | eine Klasse in `SolarManager/autoActions/transports.py` (Lesen + Senden), Erkennung an der Aktor-URL | | … verstehen, warum etwas lief | `automation_log`, dann die Zeitleiste des Tages (→ [zeitleiste.md](zeitleiste.md)) | | … das Nachholfenster ändern | `catchup_minutes` in `config.ini` des Runners | -| … eine weitere Stufe für Sperre oder Haltezeit | `lockoutChoices()` / `haltezeitChoices()` in `restricted/automations.php` — eine Zeile, der Rest zieht nach | +| … eine weitere Stufe für die Sperre | `lockoutChoices()` in `restricted/automations.php` — eine Zeile, der Rest zieht nach | +| … die Grenzen der Haltezeit ändern | `haltezeitPruefen()` in `restricted/automations.php` **und** `max` am Feld `holdMin` | | … auf Dauerzustände reagieren (Wasser läuft, Tür steht offen) | Haltezeit im Rahmen, dazu eine Aktion des Geräts „Benachrichtigungen“ (→ [benachrichtigungen.md](benachrichtigungen.md)) | diff --git a/js/solar/autoActionFuncs.js b/js/solar/autoActionFuncs.js index 565ae9a..d9505a9 100644 --- a/js/solar/autoActionFuncs.js +++ b/js/solar/autoActionFuncs.js @@ -26,7 +26,6 @@ let autoModel = null; let autoDevices = []; let autoLockouts = []; -let autoHaltezeiten = []; let autoFerien = []; let autoFeiertage = []; let autoTagVorlagen = []; @@ -1182,9 +1181,7 @@ function fuelleFormular() { const sperre = document.getElementById("lockoutSecs"); optionListe(sperre, autoLockouts.map(o => ({ value: o.value, text: o.label })), autoModel.lockout_secs); - optionListe(document.getElementById("holdSecs"), - autoHaltezeiten.map(o => ({ value: o.value, text: o.label })), - autoModel.hold_secs); + document.getElementById("holdMin").value = autoModel.hold_secs / 60; document.getElementById("autoName").value = autoModel.name; // Kennt die Liste die Etage nicht, bleibt eine Auswahlliste sonst leer // stehen und schickt beim Speichern "" mit. @@ -1219,7 +1216,10 @@ function leseFormular() { autoModel.once_per_day = document.getElementById("oncePerDay").checked; autoModel.next_day = document.getElementById("nextDay").checked; autoModel.lockout_secs = Number(document.getElementById("lockoutSecs").value); - autoModel.hold_secs = Number(document.getElementById("holdSecs").value); + // Minuten im Feld, Sekunden im Modell - und nichts Unmoegliches: ein + // leeres Feld ist 0, ein Tag ist die Obergrenze (siehe haltezeitPruefen()). + const halten = Math.round(Number(document.getElementById("holdMin").value) || 0); + autoModel.hold_secs = Math.min(Math.max(halten, 0), 1440) * 60; } /** @@ -1227,11 +1227,16 @@ function leseFormular() { * Ohne das stuende dort "rund um die Uhr", waehrend darunter 08:45 steht. */ function rahmenFelderVerdrahten() { - ["tSpanFrom", "tSpanTo", "lockoutSecs", "holdSecs", + ["tSpanFrom", "tSpanTo", "lockoutSecs", "holdMin", "runOnce", "oncePerDay", "nextDay"].forEach(id => { const feld = document.getElementById(id); if (feld) feld.addEventListener("change", () => { leseFormular(); renderRahmen(); }); }); + // Bei der Haltezeit schon beim Tippen: aus "210" wird in der Kopfzeile + // "erst nach 3,5 Std." - genau dafuer gibt man die Zahl ein. "change" + // allein kaeme erst beim Verlassen des Feldes. + document.getElementById("holdMin") + .addEventListener("input", () => { leseFormular(); renderRahmen(); }); // Zugeklappt steht in der Kopfzeile, was gilt; aufgeklappt stehen dort die // drei Vorlagen. Beides gleichzeitig waere eine volle Zeile aus @@ -1256,7 +1261,6 @@ function loadAutomatic() { const daten = JSON.parse(document.getElementById("autoActionData").textContent); autoDevices = daten.devices; autoLockouts = daten.lockouts; - autoHaltezeiten = daten.haltezeiten || []; autoFerien = daten.ferien || []; autoFeiertage = daten.feiertage || []; autoTagVorlagen = daten.tagvorlagen || []; diff --git a/restricted/automations.php b/restricted/automations.php index b9d2397..a179b5e 100644 --- a/restricted/automations.php +++ b/restricted/automations.php @@ -224,21 +224,30 @@ function lockoutChoices() * * 0 ist die Vorgabe und heisst: wie bisher, sofort. */ -function haltezeitChoices() +/** + * Eine eingegebene Haltezeit auf etwas Brauchbares zurechtstutzen. + * + * Anders als die Sperre ist sie frei einstellbar: Wonach man sucht, haengt am + * Messwert - eine offene Tuer faellt nach zwei Minuten auf, ein laufender + * Wasserhahn erst nach Stunden. Eine Stufenliste traefe immer nur die Haelfte + * der Faelle. + * + * Grenzen gibt es trotzdem. Unter null ist keine Zeit; oberhalb eines Tages + * waere es keine Haltezeit mehr, sondern ein Dauerzustand, den man besser + * anders abfragt - und das Zeitfenster schneidet sie ohnehin ab. Gerechnet + * wird in ganzen Minuten, weil der Editor Minuten anbietet und Sekunden hier + * nichts gewinnen: die Messwerte kommen viel seltener. + */ +function haltezeitPruefen($sekunden) { - // Die 3,5 Stunden fallen aus der Reihe, weil sie aus einem konkreten Fall - // stammen: dauerhafter Wasserverbrauch. Kuerzer waere Fehlalarm (eine - // lange Dusche, eine Waschmaschine), laenger liefe ein offener Hahn eine - // halbe Nacht. - $stufen = [0, 300, 900, 1800, 3600, 7200, 12600]; - $aus = []; - foreach ($stufen as $sek) { - $aus[] = [ - "value" => $sek, - "label" => $sek ? "nach " . haltezeitKurz($sek) . " ununterbrochen" : "sofort", - ]; + $sekunden = intval($sekunden); + if ($sekunden < 0) { + $sekunden = 0; } - return $aus; + if ($sekunden > 86400) { + $sekunden = 86400; + } + return intdiv($sekunden, 60) * 60; } /** @@ -985,10 +994,7 @@ function saveAutomation($data) if (!in_array($lockout, array_column(lockoutChoices(), "value"), true)) { $lockout = 60; } - $halten = intval($data["hold_secs"] ?? 0); - if (!in_array($halten, array_column(haltezeitChoices(), "value"), true)) { - $halten = 0; - } + $halten = haltezeitPruefen($data["hold_secs"] ?? 0); $db->begin_transaction(); try {