diff --git a/ajax/AutoAction.php b/ajax/AutoAction.php
index 5575ff8..7be0f04 100644
--- a/ajax/AutoAction.php
+++ b/ajax/AutoAction.php
@@ -469,18 +469,21 @@ $nutzdaten = json_encode([
benutzen - Zeit - und trotzdem entgegengesetzt wirken: die Sperre
bremst die Wiederholung, die Haltezeit den ersten Lauf.
- Freie Minuten statt einer Auswahlliste: Wonach man sucht, haengt am
- Messwert. Die Zeile über dem Rahmen rechnet beim Tippen mit und
- zeigt "erst nach 3,5 Std." - dort steht die lesbare Fassung, hier
- die eingebbare.
+ Frei einstellbar statt aus einer Auswahlliste: Wonach man sucht,
+ hängt am Messwert - eine offene Tür fällt nach zwei Minuten auf,
+ ein laufender Wasserhahn erst nach Stunden.
+
+ Dasselbe Zeitfeld wie von/bis darüber, nur als Dauer gelesen. Dass
+ es keine Uhrzeit meint, sagt der Text ringsum ("erst nach … am
+ Stück"); die Zeile über dem Rahmen schreibt es beim Tippen aus
+ ("erst nach 3,5 Std.").
-->
Auslösen erst nach
-
- Min. am Stück
+
+ am Stück
diff --git a/doku/automatiken.md b/doku/automatiken.md
index 1b2cfbf..5369e6e 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, frei in Minuten bis 24 Std.) | 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 als Stunden:Minuten bis 23:59) | 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 |
@@ -448,5 +448,5 @@ räumt der Runner selbst weg.
| … 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 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` |
+| … die Grenzen der Haltezeit ändern | `haltezeitPruefen()` in `restricted/automations.php` **und** `haltezeitAusUhr()` im Editor |
| … 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 d9505a9..31ffc85 100644
--- a/js/solar/autoActionFuncs.js
+++ b/js/solar/autoActionFuncs.js
@@ -1136,10 +1136,36 @@ function rahmenKurzText() {
return teile.join(" · ");
}
-/** Wie haltezeitKurz() in automations.php: Minuten bis eine Stunde, dann Stunden. */
+/**
+ * Haltezeit und Zeitfeld ineinander umrechnen.
+ *
+ * Das Feld ist ein gewoehnliches , sein Wert also "03:30".
+ * Gemeint ist keine Uhrzeit, sondern eine Dauer - deshalb hier zwei eigene
+ * Funktionen und nicht die Behandlung von window_from/window_to, die den Text
+ * unveraendert durchreichen.
+ */
+function haltezeitAlsUhr(sekunden) {
+ const min = Math.round(sekunden / 60);
+ return String(Math.floor(min / 60)).padStart(2, "0") + ":"
+ + String(min % 60).padStart(2, "0");
+}
+
+/** Leer oder unsinnig ist 0; die Obergrenze ist 23:59, siehe haltezeitPruefen(). */
+function haltezeitAusUhr(text) {
+ const teile = String(text || "").split(":");
+ const sek = ((Number(teile[0]) || 0) * 60 + (Number(teile[1]) || 0)) * 60;
+ return Math.min(Math.max(sek, 0), 86340);
+}
+
+/** Wie haltezeitKurz() in automations.php - dort steht, warum nicht gerundet wird. */
function haltezeitKurz(sekunden) {
- if (sekunden < 3600) return Math.floor(sekunden / 60) + " Min.";
- return (sekunden / 3600).toFixed(1).replace(/[.,]0$/, "").replace(".", ",") + " Std.";
+ const minuten = Math.floor(sekunden / 60);
+ if (minuten < 60) return minuten + " Min.";
+ const stunden = Math.floor(minuten / 60);
+ const rest = minuten % 60;
+ if (rest === 0) return stunden + " Std.";
+ if (rest === 30) return stunden + ",5 Std.";
+ return stunden + " Std. " + rest + " Min.";
}
function rahmenIstVorgabe() {
@@ -1181,7 +1207,7 @@ function fuelleFormular() {
const sperre = document.getElementById("lockoutSecs");
optionListe(sperre, autoLockouts.map(o => ({ value: o.value, text: o.label })),
autoModel.lockout_secs);
- document.getElementById("holdMin").value = autoModel.hold_secs / 60;
+ document.getElementById("holdZeit").value = haltezeitAlsUhr(autoModel.hold_secs);
document.getElementById("autoName").value = autoModel.name;
// Kennt die Liste die Etage nicht, bleibt eine Auswahlliste sonst leer
// stehen und schickt beim Speichern "" mit.
@@ -1216,10 +1242,7 @@ 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);
- // 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;
+ autoModel.hold_secs = haltezeitAusUhr(document.getElementById("holdZeit").value);
}
/**
@@ -1227,15 +1250,15 @@ function leseFormular() {
* Ohne das stuende dort "rund um die Uhr", waehrend darunter 08:45 steht.
*/
function rahmenFelderVerdrahten() {
- ["tSpanFrom", "tSpanTo", "lockoutSecs", "holdMin",
+ ["tSpanFrom", "tSpanTo", "lockoutSecs", "holdZeit",
"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")
+ // Bei der Haltezeit schon beim Tippen: aus "03:30" wird in der Kopfzeile
+ // "erst nach 3,5 Std.". "change" allein kaeme erst beim Verlassen des
+ // Feldes, und ein Zeitfeld verlaesst man ungern, um nachzusehen.
+ document.getElementById("holdZeit")
.addEventListener("input", () => { leseFormular(); renderRahmen(); });
// Zugeklappt steht in der Kopfzeile, was gilt; aufgeklappt stehen dort die
diff --git a/restricted/automations.php b/restricted/automations.php
index a179b5e..5d37372 100644
--- a/restricted/automations.php
+++ b/restricted/automations.php
@@ -232,11 +232,12 @@ function lockoutChoices()
* 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
+ * Grenzen gibt es trotzdem. Unter null ist keine Zeit; oberhalb von 23:59
* 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.
+ * anders abfragt - und das Zeitfenster schneidet sie ohnehin ab. Die Obergrenze
+ * ist genau das, was ein Zeitfeld hergibt. Gerechnet wird in ganzen Minuten,
+ * weil der Editor Stunden und Minuten anbietet und Sekunden hier nichts
+ * gewinnen: die Messwerte kommen viel seltener.
*/
function haltezeitPruefen($sekunden)
{
@@ -244,26 +245,36 @@ function haltezeitPruefen($sekunden)
if ($sekunden < 0) {
$sekunden = 0;
}
- if ($sekunden > 86400) {
- $sekunden = 86400;
+ if ($sekunden > 86340) { // 23:59, mehr kann das Zeitfeld nicht
+ $sekunden = 86340;
}
return intdiv($sekunden, 60) * 60;
}
/**
- * Eine Haltezeit in Worten - "45 Min.", "1 Std.", "3,5 Std."
+ * Eine Haltezeit in Worten - "45 Min.", "1 Std.", "3,5 Std.", "23 Std. 59 Min."
*
* Minuten bis eine Stunde, darueber Stunden: "210 Min." muss man umrechnen,
- * "3,5 Std." nicht. Die Nachkommastelle faellt weg, wenn sie null ist.
+ * "3,5 Std." nicht. Halbe Stunden bekommen ihr Komma, alles andere Krumme die
+ * Minuten ausgeschrieben - gerundet wird nicht. Aus 23:59 wurde sonst
+ * "24 Std.", und eine Haltezeit, die laenger aussieht als sie ist, waere
+ * genau die falsche Auskunft.
*/
function haltezeitKurz($sekunden)
{
- $sekunden = intval($sekunden);
- if ($sekunden < 3600) {
- return intdiv($sekunden, 60) . " Min.";
+ $minuten = intdiv(intval($sekunden), 60);
+ if ($minuten < 60) {
+ return $minuten . " Min.";
}
- $zahl = number_format($sekunden / 3600, 1, ",", "");
- return rtrim(rtrim($zahl, "0"), ",") . " Std.";
+ $stunden = intdiv($minuten, 60);
+ $rest = $minuten % 60;
+ if ($rest === 0) {
+ return $stunden . " Std.";
+ }
+ if ($rest === 30) {
+ return $stunden . ",5 Std.";
+ }
+ return $stunden . " Std. " . $rest . " Min.";
}
/**