Haltezeit im Rahmen: "Auslösen erst nach X ununterbrochen"
Der Gegenspieler zur Sperre, direkt daneben: die bremst die Wiederholung, die Haltezeit den ersten Lauf. Damit lassen sich Dauerzustände abfragen, die in einem einzelnen Messwert nicht zu sehen sind - "der Wasserzähler läuft seit einer halben Stunde ohne Pause". Gespeichert wird in automations.hold_secs (haltezeit.sql im SolarManager), gezählt wird im Runner. Angeboten werden feste Stufen, wie bei der Sperre: eine weitere ist eine Zeile in haltezeitChoices(). 0 ist die Vorgabe und heißt "sofort" - vorhandene Automatiken ändern sich nicht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -228,6 +228,12 @@ if ($action === "list") {
|
|||||||
if ($a["once_per_day"]) {
|
if ($a["once_per_day"]) {
|
||||||
$wann .= ", nur einmal am Tag";
|
$wann .= ", nur einmal am Tag";
|
||||||
}
|
}
|
||||||
|
// Vor der Sperre, weil es auch zeitlich davor liegt: erst muss
|
||||||
|
// die Bedingung stehen bleiben, dann wird ausgeloest.
|
||||||
|
if (!empty($a["hold_secs"])) {
|
||||||
|
$wann .= ", erst nach "
|
||||||
|
. intdiv($a["hold_secs"], 60) . " Min. ununterbrochen";
|
||||||
|
}
|
||||||
if ($a["lockout_secs"] > 0) {
|
if ($a["lockout_secs"] > 0) {
|
||||||
$wann .= ", frühestens alle "
|
$wann .= ", frühestens alle "
|
||||||
. ($a["lockout_secs"] >= 60
|
. ($a["lockout_secs"] >= 60
|
||||||
@@ -306,6 +312,7 @@ $nutzdaten = json_encode([
|
|||||||
"automation" => $auto,
|
"automation" => $auto,
|
||||||
"devices" => deviceCatalog(),
|
"devices" => deviceCatalog(),
|
||||||
"lockouts" => lockoutChoices(),
|
"lockouts" => lockoutChoices(),
|
||||||
|
"haltezeiten" => haltezeitChoices(),
|
||||||
"ferien" => kalendertagChoices("Ferien", "in den Ferien"),
|
"ferien" => kalendertagChoices("Ferien", "in den Ferien"),
|
||||||
"feiertage" => kalendertagChoices("Feiertage", "an Feiertagen"),
|
"feiertage" => kalendertagChoices("Feiertage", "an Feiertagen"),
|
||||||
"tagvorlagen" => tagVorlagen(),
|
"tagvorlagen" => tagVorlagen(),
|
||||||
@@ -458,6 +465,18 @@ $nutzdaten = json_encode([
|
|||||||
<select class="form-select" id="lockoutSecs"></select>
|
<select class="form-select" id="lockoutSecs"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
|
Haltezeit. Sie steht neben der Sperre, weil beide dasselbe Mittel
|
||||||
|
benutzen - Sekunden - und trotzdem entgegengesetzt wirken: die
|
||||||
|
Sperre bremst die Wiederholung, die Haltezeit den ersten Lauf.
|
||||||
|
"Auslösen" davor gelesen ergibt den Satz.
|
||||||
|
-->
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<span class="input-group-text">Auslösen</span>
|
||||||
|
<select class="form-select" id="holdSecs"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
|||||||
+47
-5
@@ -59,6 +59,7 @@ erDiagram
|
|||||||
tinyint force_once "am Fensterende notfalls doch"
|
tinyint force_once "am Fensterende notfalls doch"
|
||||||
tinyint once_per_day "höchstens einmal am Tag"
|
tinyint once_per_day "höchstens einmal am Tag"
|
||||||
int lockout_secs "Sperrzeit nach einem Lauf"
|
int lockout_secs "Sperrzeit nach einem Lauf"
|
||||||
|
int hold_secs "Haltezeit vor dem Auslösen"
|
||||||
tinyint cond_met "Laufzustand: war die Bedingung zuletzt wahr?"
|
tinyint cond_met "Laufzustand: war die Bedingung zuletzt wahr?"
|
||||||
datetime last_run "Laufzustand: wann zuletzt ausgelöst"
|
datetime last_run "Laufzustand: wann zuletzt ausgelöst"
|
||||||
timestamp changed "Signal an den Runner"
|
timestamp changed "Signal an den Runner"
|
||||||
@@ -72,13 +73,13 @@ erDiagram
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Drei Felder sind **nachgerüstet** und stehen deshalb nicht in der
|
Vier Felder sind **nachgerüstet** und stehen deshalb nicht in der
|
||||||
Schema-Datei, sondern in eigenen Skripten im SolarManager-Repo
|
Schema-Datei, sondern in eigenen Skripten im SolarManager-Repo
|
||||||
(`autoActions/`): `next_day` (`vorabend.sql`), `once_per_day` und die
|
(`autoActions/`): `next_day` (`vorabend.sql`), `once_per_day` und die
|
||||||
Dreiwertigkeit von `on_vacation`/`on_holiday` (`rahmen_erweitern.sql`),
|
Dreiwertigkeit von `on_vacation`/`on_holiday` (`rahmen_erweitern.sql`),
|
||||||
sowie der Datentyp `elapsed` samt gerechnetem Gerät „Automatiken“
|
`hold_secs` (`haltezeit.sql`), sowie der Datentyp `elapsed` samt gerechnetem
|
||||||
(`automatik_ausloeser.sql`). Wer die Datenbank neu aufsetzt, spielt sie nach
|
Gerät „Automatiken“ (`automatik_ausloeser.sql`). Wer die Datenbank neu
|
||||||
`homeMesh_automations.sql` ein.
|
aufsetzt, spielt sie nach `homeMesh_automations.sql` ein.
|
||||||
|
|
||||||
Drei Spalten in `automations` sind **kein Regelwerk, sondern Laufzustand**:
|
Drei Spalten in `automations` sind **kein Regelwerk, sondern Laufzustand**:
|
||||||
`cond_met`, `last_run` und `changed`. Sie werden vom Runner geschrieben.
|
`cond_met`, `last_run` und `changed`. Sie werden vom Runner geschrieben.
|
||||||
@@ -169,6 +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 |
|
| `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 | |
|
| `window_from`/`window_to` | Zeitfenster; `von > bis` heißt über Mitternacht | |
|
||||||
| `lockout_secs` | Sperrzeit nach einem Lauf (0 / 60 / 900) | |
|
| `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 |
|
||||||
| `once_per_day` | höchstens ein Lauf je Kalendertag | |
|
| `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 |
|
| `force_once` | am Ende des Fensters notfalls doch ausführen | greift nur, wenn im Fenster gar nichts lief |
|
||||||
|
|
||||||
@@ -282,7 +284,9 @@ flowchart TB
|
|||||||
C -- nein --> Z1["Fenster zu:<br/>force_once prüfen,<br/>Flanke zurücksetzen"]
|
C -- nein --> Z1["Fenster zu:<br/>force_once prüfen,<br/>Flanke zurücksetzen"]
|
||||||
C -- ja --> D{"Bedingung erfüllt?<br/><small>any(all(gruppe))</small>"}
|
C -- ja --> D{"Bedingung erfüllt?<br/><small>any(all(gruppe))</small>"}
|
||||||
D -- nein --> E["cond_met = 0"]
|
D -- nein --> E["cond_met = 0"]
|
||||||
D -- ja --> F{"cond_met schon 1?"}
|
D -- ja --> HZ{"hold_secs erfüllt?<br/><small>lange genug am Stück</small>"}
|
||||||
|
HZ -- nein --> G
|
||||||
|
HZ -- ja --> F{"cond_met schon 1?"}
|
||||||
F -- ja --> G["nichts tun<br/><small>keine neue Flanke</small>"]
|
F -- ja --> G["nichts tun<br/><small>keine neue Flanke</small>"]
|
||||||
F -- nein --> H{"once_per_day<br/>und lief heute?"}
|
F -- nein --> H{"once_per_day<br/>und lief heute?"}
|
||||||
H -- ja --> G
|
H -- ja --> G
|
||||||
@@ -297,6 +301,40 @@ flowchart TB
|
|||||||
`cond_met` hält fest, ob die Bedingung beim letzten Durchlauf schon erfüllt
|
`cond_met` hält fest, ob die Bedingung beim letzten Durchlauf schon erfüllt
|
||||||
war. Ohne das würde „Temperatur über 22 °C“ in jedem Takt erneut feuern.
|
war. Ohne das würde „Temperatur über 22 °C“ in jedem Takt erneut feuern.
|
||||||
|
|
||||||
|
### Haltezeit: erst, wenn es dabei bleibt
|
||||||
|
|
||||||
|
Manches zeigt sich nicht in einem einzelnen Messwert, sondern erst in seiner
|
||||||
|
Dauer. „Der Wasserzähler läuft“ ist jedes Händewaschen; „läuft seit einer
|
||||||
|
halben Stunde ohne Pause“ ist ein offener Hahn. `hold_secs` schiebt deshalb
|
||||||
|
die Flanke nach hinten: Erst wenn die Bedingung so viele Sekunden **am
|
||||||
|
Stück** erfüllt war, gilt sie als erfüllt.
|
||||||
|
|
||||||
|
```
|
||||||
|
Bedingung ──┐ ┌──────────────────────────────────┐
|
||||||
|
└───┘ └────
|
||||||
|
↑ ↑ ↑
|
||||||
|
angefangen nach 30 min: Bedingung weg,
|
||||||
|
(Zeit läuft) ausgelöst Zeit verfällt
|
||||||
|
```
|
||||||
|
|
||||||
|
Entscheidend ist, dass `cond_met` bis dahin auf 0 bleibt: Die Flanke wird
|
||||||
|
**aufgeschoben, nicht verbraucht**. Alles danach — Tagessperre, Sperrzeit,
|
||||||
|
Protokoll — bleibt unverändert.
|
||||||
|
|
||||||
|
Was man dazu wissen sollte:
|
||||||
|
|
||||||
|
* **Ein Aussetzer setzt zurück.** Auch ein einzelner Takt genügt; genau das
|
||||||
|
ist mit „ununterbrochen“ gemeint.
|
||||||
|
* **Gezählt wird im Speicher** (`erfuellt_seit`), nicht in der Datenbank. Es
|
||||||
|
ist Laufzustand wie `war_aktiv` — und nach einem Neustart weiß niemand, ob
|
||||||
|
die Bedingung zwischendurch angelegen hat. Die Zeit beginnt dann von vorn.
|
||||||
|
* **Die Messrate ist die Untergrenze der Genauigkeit.** Ein Zähler, der alle
|
||||||
|
fünf Minuten meldet, macht die Haltezeit auf fünf Minuten genau. Mit einem
|
||||||
|
punktgenauen Zeit-Auslöser („um 16:30“, nur im Nachholfenster wahr) ist sie
|
||||||
|
gar nicht sinnvoll zu kombinieren — sie würde nie reif.
|
||||||
|
* **Das Zeitfenster schneidet sie ab.** Geht das Fenster zu, verfällt eine
|
||||||
|
angefangene Haltezeit: Sie soll innerhalb des Fensters voll gelaufen sein.
|
||||||
|
|
||||||
### Die drei Zeitformen
|
### Die drei Zeitformen
|
||||||
|
|
||||||
| Schreibweise | Operator | Bedeutung | Gedacht für |
|
| Schreibweise | Operator | Bedeutung | Gedacht für |
|
||||||
@@ -339,6 +377,7 @@ heute um 06:00 wahr, an einem Tag, an dem der Auslöser gar nicht lief.
|
|||||||
|
|
||||||
| Mittel | Wogegen | Verhalten |
|
| Mittel | Wogegen | Verhalten |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| `hold_secs` | Zustände, die erst durch ihre Dauer auffallen (Wasser läuft, Fenster steht offen) | Die Flanke wird **aufgeschoben**, bis die Bedingung lange genug am Stück steht. Der Gegenspieler zur Sperrzeit: die bremst die Wiederholung, diese den ersten Lauf |
|
||||||
| `lockout_secs` | Messwerte, die um die Schwelle pendeln (22,1 / 21,9 / 22,1) | Die Flanke wird **verworfen**, nicht aufgehoben — ein Rollladen, der eine Viertelstunde später doch losfährt, wäre unangenehmer als einer, der gar nicht fährt |
|
| `lockout_secs` | Messwerte, die um die Schwelle pendeln (22,1 / 21,9 / 22,1) | Die Flanke wird **verworfen**, nicht aufgehoben — ein Rollladen, der eine Viertelstunde später doch losfährt, wäre unangenehmer als einer, der gar nicht fährt |
|
||||||
| `once_per_day` | Dinge, die man hinterher von Hand anders stellt | Nach dem ersten Lauf ist bis Mitternacht Ruhe. Die Sperrzeit taugt dafür nicht: sie zählt Sekunden und verfehlte am nächsten Morgen den Termin |
|
| `once_per_day` | Dinge, die man hinterher von Hand anders stellt | Nach dem ersten Lauf ist bis Mitternacht Ruhe. Die Sperrzeit taugt dafür nicht: sie zählt Sekunden und verfehlte am nächsten Morgen den Termin |
|
||||||
| `force_once` | verpasste Fenster | Beim Zugehen des Fensters, aber nur, wenn darin nichts lief (zusätzlich gegen `last_run` geprüft, damit ein Neustart nicht doppelt auslöst) |
|
| `force_once` | verpasste Fenster | Beim Zugehen des Fensters, aber nur, wenn darin nichts lief (zusätzlich gegen `last_run` geprüft, damit ein Neustart nicht doppelt auslöst) |
|
||||||
@@ -392,6 +431,7 @@ räumt der Runner selbst weg.
|
|||||||
| Automatik läuft gar nicht, ohne Protokolleintrag | Rahmen: falscher Wochentag, Ferien/Feiertag auf „nie“, Fenster zu eng — oder die Bedingung war nie *neu* erfüllt (`cond_met` stand schon auf 1) |
|
| Automatik läuft gar nicht, ohne Protokolleintrag | Rahmen: falscher Wochentag, Ferien/Feiertag auf „nie“, Fenster zu eng — oder die Bedingung war nie *neu* erfüllt (`cond_met` stand schon auf 1) |
|
||||||
| Läuft einmal und dann nicht mehr am selben Tag | `once_per_day` |
|
| Läuft einmal und dann nicht mehr am selben Tag | `once_per_day` |
|
||||||
| Läuft kurz nacheinander nicht erneut | `lockout_secs` |
|
| Läuft kurz nacheinander nicht erneut | `lockout_secs` |
|
||||||
|
| Läuft gar nicht, obwohl die Bedingung sichtbar zutrifft | `hold_secs`: sie war noch nicht lange genug am Stück erfüllt, oder ein Aussetzer hat die Zeit zurückgesetzt |
|
||||||
| Kette bleibt stehen | Auslöser pausiert oder an dem Tag nicht dran |
|
| Kette bleibt stehen | Auslöser pausiert oder an dem Tag nicht dran |
|
||||||
| Änderung im Editor wirkt nicht | Regelwerk-Signatur: erst beim nächsten `reload_seconds`-Fenster (30 s) |
|
| Änderung im Editor wirkt nicht | Regelwerk-Signatur: erst beim nächsten `reload_seconds`-Fenster (30 s) |
|
||||||
| `error` im Protokoll | Gerät nicht erreichbar, Kommando oder Transport gibt es nicht mehr |
|
| `error` im Protokoll | Gerät nicht erreichbar, Kommando oder Transport gibt es nicht mehr |
|
||||||
@@ -407,3 +447,5 @@ 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 |
|
| … 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)) |
|
| … 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 |
|
| … 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 |
|
||||||
|
| … 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)) |
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
let autoModel = null;
|
let autoModel = null;
|
||||||
let autoDevices = [];
|
let autoDevices = [];
|
||||||
let autoLockouts = [];
|
let autoLockouts = [];
|
||||||
|
let autoHaltezeiten = [];
|
||||||
let autoFerien = [];
|
let autoFerien = [];
|
||||||
let autoFeiertage = [];
|
let autoFeiertage = [];
|
||||||
let autoTagVorlagen = [];
|
let autoTagVorlagen = [];
|
||||||
@@ -1125,6 +1126,9 @@ function rahmenKurzText() {
|
|||||||
teile.push(autoModel.window_from === "00:00" && autoModel.window_to === "23:59"
|
teile.push(autoModel.window_from === "00:00" && autoModel.window_to === "23:59"
|
||||||
? "rund um die Uhr"
|
? "rund um die Uhr"
|
||||||
: autoModel.window_from + "–" + autoModel.window_to);
|
: autoModel.window_from + "–" + autoModel.window_to);
|
||||||
|
if (autoModel.hold_secs > 0) {
|
||||||
|
teile.push("erst nach " + Math.floor(autoModel.hold_secs / 60) + " Min.");
|
||||||
|
}
|
||||||
if (autoModel.lockout_secs > 0) {
|
if (autoModel.lockout_secs > 0) {
|
||||||
teile.push("Sperre " + (autoModel.lockout_secs >= 60
|
teile.push("Sperre " + (autoModel.lockout_secs >= 60
|
||||||
? Math.floor(autoModel.lockout_secs / 60) + " Min."
|
? Math.floor(autoModel.lockout_secs / 60) + " Min."
|
||||||
@@ -1143,6 +1147,7 @@ function rahmenIstVorgabe() {
|
|||||||
&& autoModel.window_from === "00:00"
|
&& autoModel.window_from === "00:00"
|
||||||
&& autoModel.window_to === "23:59"
|
&& autoModel.window_to === "23:59"
|
||||||
&& !autoModel.once_per_day
|
&& !autoModel.once_per_day
|
||||||
|
&& !autoModel.hold_secs
|
||||||
&& !autoModel.force_once;
|
&& !autoModel.force_once;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1173,6 +1178,9 @@ function fuelleFormular() {
|
|||||||
const sperre = document.getElementById("lockoutSecs");
|
const sperre = document.getElementById("lockoutSecs");
|
||||||
optionListe(sperre, autoLockouts.map(o => ({ value: o.value, text: o.label })),
|
optionListe(sperre, autoLockouts.map(o => ({ value: o.value, text: o.label })),
|
||||||
autoModel.lockout_secs);
|
autoModel.lockout_secs);
|
||||||
|
optionListe(document.getElementById("holdSecs"),
|
||||||
|
autoHaltezeiten.map(o => ({ value: o.value, text: o.label })),
|
||||||
|
autoModel.hold_secs);
|
||||||
document.getElementById("autoName").value = autoModel.name;
|
document.getElementById("autoName").value = autoModel.name;
|
||||||
// Kennt die Liste die Etage nicht, bleibt eine Auswahlliste sonst leer
|
// Kennt die Liste die Etage nicht, bleibt eine Auswahlliste sonst leer
|
||||||
// stehen und schickt beim Speichern "" mit.
|
// stehen und schickt beim Speichern "" mit.
|
||||||
@@ -1207,6 +1215,7 @@ function leseFormular() {
|
|||||||
autoModel.once_per_day = document.getElementById("oncePerDay").checked;
|
autoModel.once_per_day = document.getElementById("oncePerDay").checked;
|
||||||
autoModel.next_day = document.getElementById("nextDay").checked;
|
autoModel.next_day = document.getElementById("nextDay").checked;
|
||||||
autoModel.lockout_secs = Number(document.getElementById("lockoutSecs").value);
|
autoModel.lockout_secs = Number(document.getElementById("lockoutSecs").value);
|
||||||
|
autoModel.hold_secs = Number(document.getElementById("holdSecs").value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1214,7 +1223,8 @@ function leseFormular() {
|
|||||||
* Ohne das stuende dort "rund um die Uhr", waehrend darunter 08:45 steht.
|
* Ohne das stuende dort "rund um die Uhr", waehrend darunter 08:45 steht.
|
||||||
*/
|
*/
|
||||||
function rahmenFelderVerdrahten() {
|
function rahmenFelderVerdrahten() {
|
||||||
["tSpanFrom", "tSpanTo", "lockoutSecs", "runOnce", "oncePerDay", "nextDay"].forEach(id => {
|
["tSpanFrom", "tSpanTo", "lockoutSecs", "holdSecs",
|
||||||
|
"runOnce", "oncePerDay", "nextDay"].forEach(id => {
|
||||||
const feld = document.getElementById(id);
|
const feld = document.getElementById(id);
|
||||||
if (feld) feld.addEventListener("change", () => { leseFormular(); renderRahmen(); });
|
if (feld) feld.addEventListener("change", () => { leseFormular(); renderRahmen(); });
|
||||||
});
|
});
|
||||||
@@ -1242,6 +1252,7 @@ function loadAutomatic() {
|
|||||||
const daten = JSON.parse(document.getElementById("autoActionData").textContent);
|
const daten = JSON.parse(document.getElementById("autoActionData").textContent);
|
||||||
autoDevices = daten.devices;
|
autoDevices = daten.devices;
|
||||||
autoLockouts = daten.lockouts;
|
autoLockouts = daten.lockouts;
|
||||||
|
autoHaltezeiten = daten.haltezeiten || [];
|
||||||
autoFerien = daten.ferien || [];
|
autoFerien = daten.ferien || [];
|
||||||
autoFeiertage = daten.feiertage || [];
|
autoFeiertage = daten.feiertage || [];
|
||||||
autoTagVorlagen = daten.tagvorlagen || [];
|
autoTagVorlagen = daten.tagvorlagen || [];
|
||||||
@@ -1263,6 +1274,7 @@ function loadAutomatic() {
|
|||||||
force_once: a.force_once,
|
force_once: a.force_once,
|
||||||
once_per_day: a.once_per_day,
|
once_per_day: a.once_per_day,
|
||||||
lockout_secs: a.lockout_secs,
|
lockout_secs: a.lockout_secs,
|
||||||
|
hold_secs: a.hold_secs || 0,
|
||||||
groups: [],
|
groups: [],
|
||||||
actions: a.actions.map(x => ({ command_id: x.command_id, params: Object.assign({}, x.params) }))
|
actions: a.actions.map(x => ({ command_id: x.command_id, params: Object.assign({}, x.params) }))
|
||||||
};
|
};
|
||||||
@@ -1352,6 +1364,7 @@ function submitAutoAction() {
|
|||||||
force_once: autoModel.force_once,
|
force_once: autoModel.force_once,
|
||||||
once_per_day: autoModel.once_per_day,
|
once_per_day: autoModel.once_per_day,
|
||||||
lockout_secs: autoModel.lockout_secs,
|
lockout_secs: autoModel.lockout_secs,
|
||||||
|
hold_secs: autoModel.hold_secs,
|
||||||
conditions: [],
|
conditions: [],
|
||||||
actions: autoModel.actions
|
actions: autoModel.actions
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -211,6 +211,31 @@ function lockoutChoices()
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wie lange die Bedingung anliegen muss, bevor ausgeloest wird.
|
||||||
|
*
|
||||||
|
* Der Gegenspieler zur Sperrzeit: die begrenzt, wie oft ausgeloest wird,
|
||||||
|
* diese, wie lange etwas anliegen muss, bevor es ueberhaupt dazu kommt.
|
||||||
|
* "Der Wasserzaehler laeuft" ist jedes Haendewaschen - "laeuft seit einer
|
||||||
|
* halben Stunde ohne Pause" ist ein offener Hahn.
|
||||||
|
*
|
||||||
|
* Faellt die Bedingung zwischendurch aus, faengt die Zeit von vorn an; genau
|
||||||
|
* das ist mit "ununterbrochen" gemeint. Gezaehlt wird im Runner.
|
||||||
|
*
|
||||||
|
* 0 ist die Vorgabe und heisst: wie bisher, sofort.
|
||||||
|
*/
|
||||||
|
function haltezeitChoices()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
["value" => 0, "label" => "sofort"],
|
||||||
|
["value" => 300, "label" => "nach 5 Min. ununterbrochen"],
|
||||||
|
["value" => 900, "label" => "nach 15 Min. ununterbrochen"],
|
||||||
|
["value" => 1800, "label" => "nach 30 Min. ununterbrochen"],
|
||||||
|
["value" => 3600, "label" => "nach 1 Std. ununterbrochen"],
|
||||||
|
["value" => 10800, "label" => "nach 3 Std. ununterbrochen"],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auswahlwerte aus possible_values in eine einheitliche Form bringen.
|
* Auswahlwerte aus possible_values in eine einheitliche Form bringen.
|
||||||
*
|
*
|
||||||
@@ -805,6 +830,7 @@ function emptyAutomation($floor)
|
|||||||
"force_once" => false,
|
"force_once" => false,
|
||||||
"once_per_day" => false,
|
"once_per_day" => false,
|
||||||
"lockout_secs" => 60,
|
"lockout_secs" => 60,
|
||||||
|
"hold_secs" => 0,
|
||||||
"last_run" => null,
|
"last_run" => null,
|
||||||
"conditions" => [],
|
"conditions" => [],
|
||||||
"actions" => [],
|
"actions" => [],
|
||||||
@@ -842,6 +868,8 @@ function loadAutomation($id)
|
|||||||
// rahmen_erweitern.sql legt die Spalte erst an.
|
// rahmen_erweitern.sql legt die Spalte erst an.
|
||||||
"once_per_day" => (bool)($row["once_per_day"] ?? 0),
|
"once_per_day" => (bool)($row["once_per_day"] ?? 0),
|
||||||
"lockout_secs" => intval($row["lockout_secs"]),
|
"lockout_secs" => intval($row["lockout_secs"]),
|
||||||
|
// ?? 0 wie oben: haltezeit.sql legt die Spalte erst an.
|
||||||
|
"hold_secs" => intval($row["hold_secs"] ?? 0),
|
||||||
"last_run" => $row["last_run"],
|
"last_run" => $row["last_run"],
|
||||||
"conditions" => [],
|
"conditions" => [],
|
||||||
"actions" => [],
|
"actions" => [],
|
||||||
@@ -936,6 +964,10 @@ function saveAutomation($data)
|
|||||||
if (!in_array($lockout, array_column(lockoutChoices(), "value"), true)) {
|
if (!in_array($lockout, array_column(lockoutChoices(), "value"), true)) {
|
||||||
$lockout = 60;
|
$lockout = 60;
|
||||||
}
|
}
|
||||||
|
$halten = intval($data["hold_secs"] ?? 0);
|
||||||
|
if (!in_array($halten, array_column(haltezeitChoices(), "value"), true)) {
|
||||||
|
$halten = 0;
|
||||||
|
}
|
||||||
|
|
||||||
$db->begin_transaction();
|
$db->begin_transaction();
|
||||||
try {
|
try {
|
||||||
@@ -961,11 +993,12 @@ function saveAutomation($data)
|
|||||||
$stmt = $db->prepare("UPDATE automations SET name = ?, floor = ?, enabled = ?,
|
$stmt = $db->prepare("UPDATE automations SET name = ?, floor = ?, enabled = ?,
|
||||||
window_from = ?, window_to = ?, weekdays = ?,
|
window_from = ?, window_to = ?, weekdays = ?,
|
||||||
on_vacation = ?, on_holiday = ?, next_day = ?, force_once = ?,
|
on_vacation = ?, on_holiday = ?, next_day = ?, force_once = ?,
|
||||||
once_per_day = ?, lockout_secs = ?, changed = NOW()
|
once_per_day = ?, lockout_secs = ?, hold_secs = ?,
|
||||||
|
changed = NOW()
|
||||||
WHERE id = ?");
|
WHERE id = ?");
|
||||||
$stmt->bind_param("ssissiiiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo,
|
$stmt->bind_param("ssissiiiiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo,
|
||||||
$weekdays, $onVacation, $onHoliday, $nextDay, $forceOnce,
|
$weekdays, $onVacation, $onHoliday, $nextDay, $forceOnce,
|
||||||
$oncePerDay, $lockout, $id);
|
$oncePerDay, $lockout, $halten, $id);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
$db->query("DELETE FROM automation_conditions WHERE automation_id = " . $id);
|
$db->query("DELETE FROM automation_conditions WHERE automation_id = " . $id);
|
||||||
@@ -974,11 +1007,11 @@ function saveAutomation($data)
|
|||||||
$stmt = $db->prepare("INSERT INTO automations
|
$stmt = $db->prepare("INSERT INTO automations
|
||||||
(name, floor, enabled, window_from, window_to, weekdays,
|
(name, floor, enabled, window_from, window_to, weekdays,
|
||||||
on_vacation, on_holiday, next_day, force_once,
|
on_vacation, on_holiday, next_day, force_once,
|
||||||
once_per_day, lockout_secs)
|
once_per_day, lockout_secs, hold_secs)
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
|
||||||
$stmt->bind_param("ssissiiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo,
|
$stmt->bind_param("ssissiiiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo,
|
||||||
$weekdays, $onVacation, $onHoliday, $nextDay, $forceOnce,
|
$weekdays, $onVacation, $onHoliday, $nextDay, $forceOnce,
|
||||||
$oncePerDay, $lockout);
|
$oncePerDay, $lockout, $halten);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$id = $db->insert_id;
|
$id = $db->insert_id;
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
|
|||||||
Reference in New Issue
Block a user