Automatiken: Rahmen am Vorabend fuer den folgenden Tag

Neuer Schalter "Am Vorabend" im Wann-Band (Spalte next_day). Wochentage,
Ferien und Feiertage gelten dann fuer morgen: "Kinderrollos zu, wenn morgen
Schule ist" ist Mo-Fr, Ferien nie, Feiertage nie - wie der Wecker. Mit dem
heutigen Tag ging das am letzten Ferientag, am Abend vor einem Feiertag und
am Abend eines Feiertags daneben.

Editor-Satz, Kurzfassung und Uebersicht nennen den Vorabend vorne, damit
auch "nicht an Feiertagen" als Folgetag gelesen wird. Die Spalte legt
vorabend.sql im SolarManager an, ausgewertet wird sie im Runner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 09:33:59 +02:00
co-authored by Claude Opus 5
parent deb81045d5
commit 59d2a4a55e
4 changed files with 41 additions and 11 deletions
+15
View File
@@ -200,6 +200,11 @@ if ($action === "list") {
} else { } else {
$wann = "nie kein Tag ausgewählt"; $wann = "nie kein Tag ausgewählt";
} }
// Vorabend: alle Tagesangaben meinen morgen. Vorne, damit auch
// "nicht an Feiertagen" weiter hinten als Folgetag gelesen wird.
if ($a["next_day"]) {
$wann = "Vorabend von " . $wann;
}
if ($a["window_from"] !== "00:00" || $a["window_to"] !== "23:59") { if ($a["window_from"] !== "00:00" || $a["window_to"] !== "23:59") {
$wann .= ", " . $a["window_from"] . "" . $a["window_to"]; $wann .= ", " . $a["window_from"] . "" . $a["window_to"];
} }
@@ -405,6 +410,16 @@ $nutzdaten = json_encode([
<div class="btn-group btn-group-sm" role="group" id="feiertagGroup"></div> <div class="btn-group btn-group-sm" role="group" id="feiertagGroup"></div>
</div> </div>
</div> </div>
<!-- Vorabend: fuer alles, was abends fuer morgen geschieht - "Kinder
ins Bett, wenn morgen Schule ist" ist dann Mo-Fr, Ferien nie,
Feiertage nie, wie der Wecker. Uhrzeit und Zeitfenster bleiben
beim heutigen Tag. -->
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="nextDay">
<label class="form-check-label" for="nextDay">
Am Vorabend Wochentage, Ferien und Feiertage gelten für den folgenden Tag
</label>
</div>
<!-- <!--
.callout ist ein AdminLTE-Bauteil: farbiger Balken links, getoenter .callout ist ein AdminLTE-Bauteil: farbiger Balken links, getoenter
Grund, und die Toene kommen aus Bootstraps *-bg-subtle-Variablen - Grund, und die Toene kommen aus Bootstraps *-bg-subtle-Variablen -
+1
View File
@@ -36,6 +36,7 @@ CREATE TABLE IF NOT EXISTS `automations` (
`weekdays` tinyint(3) unsigned NOT NULL DEFAULT 127 COMMENT 'Bitmaske: Bit 0 = Montag ... Bit 6 = Sonntag', `weekdays` tinyint(3) unsigned NOT NULL DEFAULT 127 COMMENT 'Bitmaske: Bit 0 = Montag ... Bit 6 = Sonntag',
`on_vacation` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auch in den Ferien ausfuehren', `on_vacation` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auch in den Ferien ausfuehren',
`on_holiday` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auch an Feiertagen ausfuehren', `on_holiday` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auch an Feiertagen ausfuehren',
`next_day` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Wochentage, Ferien und Feiertage gelten fuer morgen (Vorabend). Nachgeruestet mit vorabend.sql im SolarManager',
`force_once` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Am Ende des Zeitraums auf jeden Fall ausfuehren', `force_once` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Am Ende des Zeitraums auf jeden Fall ausfuehren',
`lockout_secs` int(11) NOT NULL DEFAULT 60 COMMENT 'Sperrzeit: so lange nach einer Ausloesung wird nicht erneut geschaltet. Gegen Messwerte, die um die Schwelle pendeln - jedes Ueberschreiten waere sonst eine echte Flanke. Der Editor bietet 0, 60 und 900 an', `lockout_secs` int(11) NOT NULL DEFAULT 60 COMMENT 'Sperrzeit: so lange nach einer Ausloesung wird nicht erneut geschaltet. Gegen Messwerte, die um die Schwelle pendeln - jedes Ueberschreiten waere sonst eine echte Flanke. Der Editor bietet 0, 60 und 900 an',
`cond_met` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'War die Bedingung beim letzten Durchlauf erfuellt? Nur die steigende Flanke loest aus, sonst wuerde Temperatur groesser 22 im Sekundentakt feuern', `cond_met` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'War die Bedingung beim letzten Durchlauf erfuellt? Nur die steigende Flanke loest aus, sonst wuerde Temperatur groesser 22 im Sekundentakt feuern',
+10 -1
View File
@@ -1100,6 +1100,9 @@ function rahmenText() {
if (dazu.length && tage !== 127) satz += ", dazu " + aufzaehlung(dazu); if (dazu.length && tage !== 127) satz += ", dazu " + aufzaehlung(dazu);
} }
if (verbote.length) satz += ", aber nicht " + aufzaehlung(verbote); if (verbote.length) satz += ", aber nicht " + aufzaehlung(verbote);
// Vorabend: derselbe Satz, nur fuer morgen gerechnet. Vorne, damit auch
// "aber nicht an Feiertagen" als Folgetag gelesen wird.
if (autoModel.next_day) satz = satz.replace(/^Läuft /, "Läuft am Vortag, gerechnet für den Folgetag: ");
return satz + "."; return satz + ".";
} }
@@ -1112,6 +1115,7 @@ function rahmenText() {
*/ */
function rahmenKurzText() { function rahmenKurzText() {
const teile = [tageText(autoModel.weekdays) || "an keinem Tag"]; const teile = [tageText(autoModel.weekdays) || "an keinem Tag"];
if (autoModel.next_day) teile[0] = "Vorabend von " + teile[0];
if (autoModel.on_vacation === 0) teile.push("nicht in den Ferien"); if (autoModel.on_vacation === 0) teile.push("nicht in den Ferien");
if (autoModel.on_vacation === 2) teile.push("dazu Ferientage"); if (autoModel.on_vacation === 2) teile.push("dazu Ferientage");
if (autoModel.on_holiday === 0) teile.push("nicht an Feiertagen"); if (autoModel.on_holiday === 0) teile.push("nicht an Feiertagen");
@@ -1133,6 +1137,7 @@ function rahmenIstVorgabe() {
return autoModel.weekdays === 127 return autoModel.weekdays === 127
&& autoModel.on_vacation === 1 && autoModel.on_vacation === 1
&& autoModel.on_holiday === 1 && autoModel.on_holiday === 1
&& !autoModel.next_day
&& 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
@@ -1179,6 +1184,7 @@ function fuelleFormular() {
document.getElementById("tSpanTo").value = autoModel.window_to; document.getElementById("tSpanTo").value = autoModel.window_to;
document.getElementById("runOnce").checked = autoModel.force_once; document.getElementById("runOnce").checked = autoModel.force_once;
document.getElementById("oncePerDay").checked = autoModel.once_per_day; document.getElementById("oncePerDay").checked = autoModel.once_per_day;
document.getElementById("nextDay").checked = autoModel.next_day;
renderRahmen(); renderRahmen();
rahmenFelderVerdrahten(); rahmenFelderVerdrahten();
renderConditions(); renderConditions();
@@ -1197,6 +1203,7 @@ function leseFormular() {
// sofort nachziehen muss. // sofort nachziehen muss.
autoModel.force_once = document.getElementById("runOnce").checked; autoModel.force_once = document.getElementById("runOnce").checked;
autoModel.once_per_day = document.getElementById("oncePerDay").checked; autoModel.once_per_day = document.getElementById("oncePerDay").checked;
autoModel.next_day = document.getElementById("nextDay").checked;
autoModel.lockout_secs = Number(document.getElementById("lockoutSecs").value); autoModel.lockout_secs = Number(document.getElementById("lockoutSecs").value);
} }
@@ -1205,7 +1212,7 @@ 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"].forEach(id => { ["tSpanFrom", "tSpanTo", "lockoutSecs", "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(); });
}); });
@@ -1250,6 +1257,7 @@ function loadAutomatic() {
weekdays: a.weekdays, weekdays: a.weekdays,
on_vacation: a.on_vacation, on_vacation: a.on_vacation,
on_holiday: a.on_holiday, on_holiday: a.on_holiday,
next_day: !!a.next_day,
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,
@@ -1338,6 +1346,7 @@ function submitAutoAction() {
weekdays: autoModel.weekdays, weekdays: autoModel.weekdays,
on_vacation: autoModel.on_vacation, on_vacation: autoModel.on_vacation,
on_holiday: autoModel.on_holiday, on_holiday: autoModel.on_holiday,
next_day: autoModel.next_day,
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,
+15 -10
View File
@@ -803,6 +803,7 @@ function emptyAutomation($floor)
"weekdays" => 127, "weekdays" => 127,
"on_vacation" => 1, "on_vacation" => 1,
"on_holiday" => 1, "on_holiday" => 1,
"next_day" => false,
"force_once" => false, "force_once" => false,
"once_per_day" => false, "once_per_day" => false,
"lockout_secs" => 60, "lockout_secs" => 60,
@@ -835,6 +836,9 @@ function loadAutomation($id)
"weekdays" => intval($row["weekdays"]), "weekdays" => intval($row["weekdays"]),
"on_vacation" => intval($row["on_vacation"]), "on_vacation" => intval($row["on_vacation"]),
"on_holiday" => intval($row["on_holiday"]), "on_holiday" => intval($row["on_holiday"]),
// Vorabend: der Rahmen gilt fuer morgen. Siehe vorabend.sql im
// SolarManager; ?? 0 wie bei once_per_day, falls die Spalte fehlt.
"next_day" => (bool)($row["next_day"] ?? 0),
"force_once" => (bool)$row["force_once"], "force_once" => (bool)$row["force_once"],
// ?? 0, damit der Editor auch vor der Migration noch aufgeht: // ?? 0, damit der Editor auch vor der Migration noch aufgeht:
// rahmen_erweitern.sql legt die Spalte erst an. // rahmen_erweitern.sql legt die Spalte erst an.
@@ -925,6 +929,7 @@ function saveAutomation($data)
$weekdays = intval($data["weekdays"] ?? 127) & 127; $weekdays = intval($data["weekdays"] ?? 127) & 127;
$onVacation = kalendertagWert($data["on_vacation"] ?? 1); $onVacation = kalendertagWert($data["on_vacation"] ?? 1);
$onHoliday = kalendertagWert($data["on_holiday"] ?? 1); $onHoliday = kalendertagWert($data["on_holiday"] ?? 1);
$nextDay = !empty($data["next_day"]) ? 1 : 0;
$forceOnce = !empty($data["force_once"]) ? 1 : 0; $forceOnce = !empty($data["force_once"]) ? 1 : 0;
$oncePerDay = !empty($data["once_per_day"]) ? 1 : 0; $oncePerDay = !empty($data["once_per_day"]) ? 1 : 0;
// Nur die angebotenen Stufen, sonst die Vorgabe. Eine krumme Zahl kaeme // Nur die angebotenen Stufen, sonst die Vorgabe. Eine krumme Zahl kaeme
@@ -957,12 +962,12 @@ function saveAutomation($data)
// Zeitstempel, dass er sein Regelwerk neu laden muss. // Zeitstempel, dass er sein Regelwerk neu laden muss.
$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 = ?, force_once = ?, on_vacation = ?, on_holiday = ?, next_day = ?, force_once = ?,
once_per_day = ?, lockout_secs = ?, changed = NOW() once_per_day = ?, lockout_secs = ?, changed = NOW()
WHERE id = ?"); WHERE id = ?");
$stmt->bind_param("ssissiiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo, $stmt->bind_param("ssissiiiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo,
$weekdays, $onVacation, $onHoliday, $forceOnce, $oncePerDay, $weekdays, $onVacation, $onHoliday, $nextDay, $forceOnce,
$lockout, $id); $oncePerDay, $lockout, $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);
@@ -970,12 +975,12 @@ function saveAutomation($data)
} else { } else {
$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, force_once, once_per_day, on_vacation, on_holiday, next_day, force_once,
lockout_secs) once_per_day, lockout_secs)
VALUES (?,?,?,?,?,?,?,?,?,?,?)"); VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
$stmt->bind_param("ssissiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo, $stmt->bind_param("ssissiiiiiii", $name, $floor, $enabled, $windowFrom, $windowTo,
$weekdays, $onVacation, $onHoliday, $forceOnce, $oncePerDay, $weekdays, $onVacation, $onHoliday, $nextDay, $forceOnce,
$lockout); $oncePerDay, $lockout);
$stmt->execute(); $stmt->execute();
$id = $db->insert_id; $id = $db->insert_id;
$stmt->close(); $stmt->close();