Automatismen als Zeitleiste des Tages, Liste bleibt umschaltbar
Die Uebersicht auf der Startseite zeigt die Automatiken jetzt als Zeitleiste: eine Bahn je Geraeteart, die Spur ist der Tag. Der Umschalter "Zeitleiste | Liste" oben in der Karte fuehrt zur bisherigen Tabelle zurueck und wird im Browser gemerkt. Jede Automatik erscheint genau einmal, nach festen Regeln (restricted/zeitleiste.php), auch kuenftige: - feste Uhrzeit oder Sonnenstand: Punkt; Zeitfenster mit Messwert: Balken; Kette: hinter ihrem Ausloeser, als gestrichelter Bogen verbunden - ohne jede Zeit: Band "Jederzeit"; pausiert: Band "Pausiert" - an diesem Tag nicht dran (Wochentag, Ferien, Feiertag, Vorabend, wie im Runner gerechnet): gestrichelt mit Grund, nie ausgeblendet - Bahn aus den geschalteten Geraeten ueber bedienform(), Mehrheit gewinnt; Bewaesserung am Geraetetyp - Zaehler "x von y" im Kopf Dazu: Tag vor/zurueck, Etagen einzeln einblendbar (gemerkt), Jetzt-Linie und Nachtschatten aus solarLog.daylight (spaetere Tage vom selben Kalendertag eines Vorjahres), gelaufene Eintraege mit Uhrzeit aus automation_log, Klick oeffnet den Editor. Schmal nur Symbole in den Bahnen und Start bei der aktuellen Uhrzeit. Auf einer Probeseite mit echten Daten geprueft: nichts ragt ueber, keine ueberlappenden Etiketten, alle 22 Automatiken genau einmal, Filter, Tageswechsel und Umschalter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+16
-8
@@ -203,16 +203,23 @@ require_once(__DIR__ . "/reiter.php");
|
||||
</div>
|
||||
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
|
||||
<div class="card p-0 mb-4" style="width:100%">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
<h3 class="card-title">Automatismen</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
<div class="card-header pb-1 pt-1 d-flex align-items-center gap-2 flex-wrap">
|
||||
<h3 class="card-title me-auto">Automatismen</h3>
|
||||
<!-- Zwei Ansichten derselben Automatiken. Die Zeitleiste zeigt den
|
||||
Tag (js/solar/zeitleiste.js), die Liste ist die bisherige
|
||||
Tabelle je Etage. Welche offen ist, merkt sich der Browser. -->
|
||||
<div class="btn-group btn-group-sm zl-ansicht" role="group" aria-label="Ansicht">
|
||||
<button type="button" class="btn btn-outline-secondary" data-ansicht="zeitleiste">
|
||||
<i class="bi bi-bar-chart-steps me-1"></i>Zeitleiste</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-ansicht="liste">
|
||||
<i class="bi bi-list-ul me-1"></i>Liste</button>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-success zl-neu" title="Neue Automatik">
|
||||
<i class="bi bi-plus-lg"></i></button>
|
||||
</div>
|
||||
<div class="card-body pb-1">
|
||||
<div class="card-body pb-2">
|
||||
<div id="zeitleiste" class="zl" hidden></div>
|
||||
<div id="automatikListe">
|
||||
<?php
|
||||
/*
|
||||
* Eine Etage, ein Reiter. Gezeichnet von reiterLeiste() aus
|
||||
@@ -244,6 +251,7 @@ require_once(__DIR__ . "/reiter.php");
|
||||
<?= reiterFlaecheZu() ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div><!-- /automatikListe -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
/**
|
||||
* Die Automatiken eines Tages als Zeitleiste.
|
||||
*
|
||||
* Die Übersicht als Tabelle beantwortete nicht, was man eigentlich wissen
|
||||
* will: läuft der Tag stimmig, und warum ist etwas noch nicht gefahren? Die
|
||||
* Zeitleiste legt deshalb jede Automatik auf die Stunde, zu der sie greift -
|
||||
* und zwar ausnahmslos jede, auch künftige, nach festen Regeln:
|
||||
*
|
||||
* feste Uhrzeit (um / ab 07:00) Punkt
|
||||
* Sonnenauf-/-untergang ± Versatz Punkt, an diesem Tag ausgerechnet
|
||||
* Kette (andere Automatik + 00:10) Punkt hinter dem Auslöser
|
||||
* Zeitfenster und Messwerte Balken über das Fenster
|
||||
* gar keine Zeit, nur Messwerte "jederzeit"
|
||||
*
|
||||
* Die Bahn - die Zeile, in der sie steht - ergibt sich aus den Geräten, die
|
||||
* sie schaltet: dieselbe Einordnung wie im Raum-Modal (bedienform() in
|
||||
* roomControls.php), die Mehrheit gewinnt. Bewässerung ist dort "sonstiges"
|
||||
* und wird hier am Gerätetyp erkannt.
|
||||
*
|
||||
* Ob eine Automatik an diesem Tag überhaupt dran ist, wird genau so
|
||||
* gerechnet wie im Runner (tag_passt und gemeinter_tag in
|
||||
* autoaction_runner.py) - sonst hieße "heute nicht" hier etwas anderes als
|
||||
* dort. Ausgeblendet wird trotzdem nichts: wer nicht dran ist, steht
|
||||
* gestrichelt da, mit Grund.
|
||||
*
|
||||
* Gerechnet wird nur die Lage auf dem Tag, nicht, ob eine Bedingung gerade
|
||||
* erfüllt ist. Das entscheidet der Runner; hier steht, ob sie gelaufen ist
|
||||
* (automation_log) und wann sie frühestens könnte.
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . "/automations.php");
|
||||
require_once(__DIR__ . "/roomControls.php");
|
||||
require_once(__DIR__ . "/costs.php"); // solarDb() für die Sonnenzeiten
|
||||
|
||||
/** Reihenfolge der Bahnen und wie sie heißen. */
|
||||
function zeitleisteBahnen()
|
||||
{
|
||||
return [
|
||||
"licht" => ["titel" => "Licht", "symbol" => "bi-lightbulb"],
|
||||
"beschattung" => ["titel" => "Beschattung", "symbol" => "bi-window-split"],
|
||||
"heizung" => ["titel" => "Heizung", "symbol" => "bi-thermometer-half"],
|
||||
"bewaesserung" => ["titel" => "Bewässerung", "symbol" => "bi-droplet"],
|
||||
"schalter" => ["titel" => "Schalter", "symbol" => "bi-toggle-on"],
|
||||
"sonstiges" => ["titel" => "Sonstiges", "symbol" => "bi-three-dots"],
|
||||
];
|
||||
}
|
||||
|
||||
/** "07:30" -> 450, "-00:30" wird von aufrufender Seite behandelt. */
|
||||
function zlMinuten($text)
|
||||
{
|
||||
if (!preg_match('/^(\d{1,2}):(\d{2})/', trim(strval($text)), $t)) {
|
||||
return null;
|
||||
}
|
||||
return intval($t[1]) * 60 + intval($t[2]);
|
||||
}
|
||||
|
||||
/** 450 -> "07:30" */
|
||||
function zlUhrzeit($m)
|
||||
{
|
||||
$m = (($m % 1440) + 1440) % 1440;
|
||||
return sprintf("%02d:%02d", intdiv($m, 60), $m % 60);
|
||||
}
|
||||
|
||||
/** Ferien und Feiertag an einem Datum aus calendar_days. */
|
||||
function zlKalender($datum)
|
||||
{
|
||||
static $cache = [];
|
||||
if (isset($cache[$datum])) {
|
||||
return $cache[$datum];
|
||||
}
|
||||
$stand = ["feiertag" => null, "ferien" => null];
|
||||
$stmt = meshDb()->prepare("SELECT holiday, vacation FROM calendar_days WHERE date = ?");
|
||||
$stmt->bind_param("s", $datum);
|
||||
$stmt->execute();
|
||||
$z = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
if ($z) {
|
||||
$stand = ["feiertag" => $z["holiday"], "ferien" => $z["vacation"]];
|
||||
}
|
||||
return $cache[$datum] = $stand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ist die Automatik an diesem Datum dran? Gibt [ja/nein, Grund] zurück.
|
||||
* Dieselbe Reihenfolge wie tag_passt(): erst öffnen, dann sperren.
|
||||
*/
|
||||
function zlTagPasst($a, $datum)
|
||||
{
|
||||
$tag = new DateTimeImmutable($datum);
|
||||
if (!empty($a["next_day"])) {
|
||||
$tag = $tag->modify("+1 day");
|
||||
}
|
||||
$kal = zlKalender($tag->format("Y-m-d"));
|
||||
$bit = 1 << (intval($tag->format("N")) - 1); // Montag = Bit 0
|
||||
$vorabend = !empty($a["next_day"]) ? "morgen " : "";
|
||||
|
||||
$passt = (bool)($a["weekdays"] & $bit);
|
||||
if ($kal["feiertag"] && $a["on_holiday"] == 2) $passt = true;
|
||||
if ($kal["ferien"] && $a["on_vacation"] == 2) $passt = true;
|
||||
if (!$passt) {
|
||||
$namen = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
$tage = [];
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
if ($a["weekdays"] & (1 << $i)) $tage[] = $namen[$i];
|
||||
}
|
||||
$extra = [];
|
||||
if ($a["on_holiday"] == 2) $extra[] = "Feiertage";
|
||||
if ($a["on_vacation"] == 2) $extra[] = "Ferien";
|
||||
$liste = implode(", ", array_merge($tage, $extra));
|
||||
return [false, ($vorabend ? "nur am Vorabend von " : "nur ") . ($liste ?: "an keinem Tag")];
|
||||
}
|
||||
if ($kal["feiertag"] && $a["on_holiday"] == 0) {
|
||||
return [false, $vorabend . "Feiertag (" . $kal["feiertag"] . ")"];
|
||||
}
|
||||
if ($kal["ferien"] && $a["on_vacation"] == 0) {
|
||||
return [false, $vorabend . "Ferien (" . $kal["ferien"] . ")"];
|
||||
}
|
||||
return [true, ""];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sonnenauf- und -untergang eines Datums in Minuten, wie im Runner.
|
||||
*
|
||||
* solarLog.daylight kennt nur heute, morgen und die Vergangenheit. Für einen
|
||||
* späteren Tag gilt derselbe Kalendertag eines Vorjahres - die Sonne
|
||||
* verschiebt sich von Jahr zu Jahr um weniger als eine Minute, und so braucht
|
||||
* es keinen Standort, der in einem anderen Haus wieder falsch wäre. "genau"
|
||||
* sagt, ob es der Eintrag des Tages selbst war.
|
||||
*/
|
||||
function zlSonne($datum)
|
||||
{
|
||||
try {
|
||||
$db = solarDb();
|
||||
$stmt = $db->prepare("SELECT sunrise, sunset, date = ? AS genau FROM daylight
|
||||
WHERE date = ? OR DATE_FORMAT(date, '%m-%d') = DATE_FORMAT(?, '%m-%d')
|
||||
ORDER BY genau DESC, date DESC LIMIT 1");
|
||||
$stmt->bind_param("sss", $datum, $datum, $datum);
|
||||
$stmt->execute();
|
||||
$z = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
if ($z) {
|
||||
return ["auf" => zlMinuten($z["sunrise"]), "unter" => zlMinuten($z["sunset"]),
|
||||
"genau" => (bool)$z["genau"]];
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
// ohne Sonnenzeiten eben ohne - die betroffenen stehen dann unter "jederzeit"
|
||||
}
|
||||
return ["auf" => null, "unter" => null, "genau" => false];
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Bahn einer Automatik: welche Art Gerät sie überwiegend schaltet.
|
||||
* $geraete ist command_id => Gerät aus deviceCatalog().
|
||||
*/
|
||||
function zlBahn($aktionen, $geraete)
|
||||
{
|
||||
$zaehler = [];
|
||||
foreach ($aktionen as $akt) {
|
||||
$d = $geraete[$akt["command_id"]] ?? null;
|
||||
if (!$d) {
|
||||
continue;
|
||||
}
|
||||
$form = bedienform($d);
|
||||
if (stripos($d["type"], "bewässer") !== false || stripos($d["type"], "bewaesser") !== false
|
||||
|| stripos($d["url"], "Gartenwasser") !== false) {
|
||||
$form = "bewaesserung";
|
||||
}
|
||||
if (!isset(zeitleisteBahnen()[$form])) {
|
||||
$form = "sonstiges";
|
||||
}
|
||||
$zaehler[$form] = ($zaehler[$form] ?? 0) + 1;
|
||||
}
|
||||
if (!$zaehler) {
|
||||
return "sonstiges";
|
||||
}
|
||||
// Bei Gleichstand gewinnt die Bahn, die weiter oben steht.
|
||||
$reihe = array_flip(array_keys(zeitleisteBahnen()));
|
||||
uksort($zaehler, function ($x, $y) use ($zaehler, $reihe) {
|
||||
return [$zaehler[$y], $reihe[$x]] <=> [$zaehler[$x], $reihe[$y]];
|
||||
});
|
||||
return array_key_first($zaehler);
|
||||
}
|
||||
|
||||
/** Was die Automatik tut, kurz: "Wozi Fensterfront, Küche Tür +5: Auf". */
|
||||
function zlSatz($aktionen, $geraete, $kommandos)
|
||||
{
|
||||
$namen = [];
|
||||
$was = [];
|
||||
foreach ($aktionen as $akt) {
|
||||
$d = $geraete[$akt["command_id"]] ?? null;
|
||||
$namen[] = $d ? $d["label"] : "?";
|
||||
$voll = $kommandos[$akt["command_id"]] ?? "";
|
||||
$was[] = strpos($voll, ": ") !== false ? substr($voll, strpos($voll, ": ") + 2) : $voll;
|
||||
}
|
||||
$namen = array_values(array_unique($namen));
|
||||
$wer = implode(", ", array_slice($namen, 0, 2)) . (count($namen) > 2 ? " +" . (count($namen) - 2) : "");
|
||||
$was = array_values(array_unique($was));
|
||||
return $wer . ": " . (count($was) === 1 ? $was[0] : implode(" / ", array_slice($was, 0, 2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Zeitleiste eines Datums (YYYY-MM-DD).
|
||||
*/
|
||||
function zeitleiste($datum)
|
||||
{
|
||||
$db = meshDb();
|
||||
$d = DateTimeImmutable::createFromFormat("Y-m-d", $datum) ?: new DateTimeImmutable("today");
|
||||
$datum = $d->format("Y-m-d");
|
||||
$sonne = zlSonne($datum);
|
||||
|
||||
// Geräte je Kommando, einmal für alle.
|
||||
$geraete = [];
|
||||
foreach (deviceCatalog() as $g) {
|
||||
foreach ($g["commands"] as $c) {
|
||||
$geraete[$c["id"]] = $g;
|
||||
}
|
||||
}
|
||||
$kommandos = commandNames();
|
||||
$namesState = stateNames();
|
||||
|
||||
// Messwerte, die eine Zeit tragen: Uhrzeit/Datum (Logic), Sonnenstand,
|
||||
// Automatik-Auslöser. Alles andere ist ein Messwert, auf den gewartet wird.
|
||||
$zeitStates = [];
|
||||
$res = $db->query("SELECT s.id, s.url, a.url AS actor_url, t.type
|
||||
FROM actor_states s JOIN actors a ON a.id = s.actor_id
|
||||
LEFT JOIN state_types t ON t.id = s.state_type
|
||||
WHERE a.url IN ('Logic', 'Automatik')");
|
||||
while ($z = $res->fetch_assoc()) {
|
||||
$zeitStates[intval($z["id"])] = $z;
|
||||
}
|
||||
|
||||
// Läufe an diesem Tag.
|
||||
$laeufe = [];
|
||||
$stmt = $db->prepare("SELECT automation_id, ts, result FROM automation_log
|
||||
WHERE ts >= ? AND ts < ? + INTERVAL 1 DAY AND result IN ('fired', 'forced')
|
||||
ORDER BY ts");
|
||||
$stmt->bind_param("ss", $datum, $datum);
|
||||
$stmt->execute();
|
||||
$r = $stmt->get_result();
|
||||
while ($z = $r->fetch_assoc()) {
|
||||
$laeufe[intval($z["automation_id"])][] = substr($z["ts"], 11, 5);
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
$ids = array_map("intval", array_column($db->query("SELECT id FROM automations")->fetch_all(MYSQLI_ASSOC), "id"));
|
||||
$liste = [];
|
||||
foreach ($ids as $id) {
|
||||
$a = loadAutomation($id);
|
||||
if (!$a) {
|
||||
continue;
|
||||
}
|
||||
$von = zlMinuten($a["window_from"]) ?? 0;
|
||||
$bis = zlMinuten($a["window_to"]) ?? 1439;
|
||||
$ganzerTag = $von === 0 && $bis >= 1439;
|
||||
|
||||
// Je ODER-Gruppe: Zeitpunkt, frühester Beginn, spätestes Ende,
|
||||
// Kette, wartet auf Messwert. Innerhalb der Gruppe UND.
|
||||
$gruppen = [];
|
||||
foreach ($a["conditions"] as $c) {
|
||||
$g = &$gruppen[$c["group"]];
|
||||
$g = $g ?? ["punkt" => null, "start" => null, "ende" => null, "kette" => null,
|
||||
"versatz" => 0, "sensor" => false, "sonne" => false];
|
||||
$st = $zeitStates[$c["state_id"]] ?? null;
|
||||
$op = $c["operator"];
|
||||
if ($st && $st["actor_url"] === "Logic" && $st["type"] === "time") {
|
||||
$m = zlMinuten($c["value"]);
|
||||
if ($op === "=") $g["punkt"] = $m;
|
||||
elseif ($op === ">=") $g["start"] = max($g["start"] ?? 0, $m);
|
||||
elseif ($op === "<") $g["ende"] = min($g["ende"] ?? 1440, $m);
|
||||
} elseif ($st && $st["actor_url"] === "Logic" && $st["type"] === "deltatime") {
|
||||
$basis = $st["url"] === "sunrise" ? $sonne["auf"] : $sonne["unter"];
|
||||
$versatz = zlMinuten($c["value"]) ?? 0;
|
||||
if ($basis === null) {
|
||||
$g["sensor"] = true;
|
||||
continue;
|
||||
}
|
||||
$m = $basis + (substr($op, -1) === "-" ? -$versatz : $versatz);
|
||||
$g["sonne"] = true;
|
||||
if ($op === "+" || $op === "-") $g["punkt"] = $m;
|
||||
elseif (strpos($op, ">=") === 0) $g["start"] = max($g["start"] ?? 0, $m);
|
||||
else $g["ende"] = min($g["ende"] ?? 1440, $m);
|
||||
} elseif ($st && $st["actor_url"] === "Automatik" && preg_match('/^auto:(\d+)$/', $st["url"], $t)) {
|
||||
$g["kette"] = intval($t[1]);
|
||||
$g["versatz"] = zlMinuten($c["value"]) ?? 0;
|
||||
} elseif ($st && $st["actor_url"] === "Logic") {
|
||||
// Datum: ein Tagesfilter, keine Lage auf dem Tag.
|
||||
} else {
|
||||
$g["sensor"] = true;
|
||||
}
|
||||
unset($g);
|
||||
}
|
||||
|
||||
[$dran, $grund] = zlTagPasst($a, $datum);
|
||||
$eintrag = [
|
||||
"id" => $id,
|
||||
"name" => $a["name"],
|
||||
"floor" => $a["floor"],
|
||||
"enabled" => $a["enabled"],
|
||||
"dran" => $dran,
|
||||
"grund" => $grund,
|
||||
"bahn" => zlBahn($a["actions"], $geraete),
|
||||
"satz" => zlSatz($a["actions"], $geraete, $kommandos),
|
||||
"wenn" => describeConditions($a["conditions"], $namesState),
|
||||
"laeufe" => $laeufe[$id] ?? [],
|
||||
"art" => "jederzeit",
|
||||
"punkt" => null,
|
||||
"von" => null,
|
||||
"bis" => null,
|
||||
"kette" => null,
|
||||
"versatz" => 0,
|
||||
"sonne" => false,
|
||||
"wartet" => false,
|
||||
];
|
||||
|
||||
$punkte = array_filter(array_column($gruppen, "punkt"), fn($m) => $m !== null);
|
||||
$ketten = array_filter($gruppen, fn($g) => $g["kette"] !== null);
|
||||
$eintrag["wartet"] = (bool)array_filter($gruppen, fn($g) => $g["sensor"]);
|
||||
$eintrag["sonne"] = (bool)array_filter($gruppen, fn($g) => $g["sonne"]);
|
||||
|
||||
if ($punkte) {
|
||||
$eintrag["art"] = "punkt";
|
||||
$eintrag["punkt"] = min($punkte);
|
||||
} elseif ($ketten) {
|
||||
$k = reset($ketten);
|
||||
$eintrag["art"] = "kette";
|
||||
$eintrag["kette"] = $k["kette"];
|
||||
$eintrag["versatz"] = $k["versatz"];
|
||||
// Die eigenen Grenzen der Kette - aufgelöst wird unten, wenn der
|
||||
// Auslöser bekannt ist.
|
||||
$eintrag["_von"] = max($von, $k["start"] ?? $von);
|
||||
$eintrag["_bis"] = min($bis, $k["ende"] ?? $bis);
|
||||
} else {
|
||||
$anfaenge = [];
|
||||
$enden = [];
|
||||
$zeitlich = false;
|
||||
foreach ($gruppen as $g) {
|
||||
if ($g["start"] !== null || $g["ende"] !== null) $zeitlich = true;
|
||||
$anfaenge[] = max($von, $g["start"] ?? $von);
|
||||
$enden[] = min($bis, $g["ende"] ?? $bis);
|
||||
}
|
||||
if ($zeitlich || !$ganzerTag) {
|
||||
$eintrag["art"] = "fenster";
|
||||
$eintrag["von"] = $anfaenge ? min($anfaenge) : $von;
|
||||
$eintrag["bis"] = $enden ? max($enden) : $bis;
|
||||
// Der früheste zeitlich bestimmte Beginn, falls es einen gibt -
|
||||
// bei "Sonnenuntergang + 30 oder dunkel" die Sonnenmarke.
|
||||
$marken = array_filter(array_column($gruppen, "start"), fn($m) => $m !== null);
|
||||
if ($marken) {
|
||||
$eintrag["punkt"] = min($marken);
|
||||
}
|
||||
}
|
||||
}
|
||||
$liste[$id] = $eintrag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ketten auflösen. Die Lage hängt am Auslöser:
|
||||
*
|
||||
* Auslöser lief schon Punkt bei dessen Zeit + Versatz
|
||||
* Auslöser hat einen Punkt Punkt dort + Versatz
|
||||
* Auslöser ist ein Fenster Fenster ab dessen Beginn + Versatz
|
||||
*
|
||||
* Jeweils nicht vor dem eigenen Fensterbeginn und nicht nach dem eigenen
|
||||
* Ende. Mehrstufig (Wecker -> Rollos -> Schlafzimmer), deshalb reihum.
|
||||
*/
|
||||
$aufgeloest = [];
|
||||
for ($runde = 0; $runde < 6; $runde++) {
|
||||
foreach ($liste as $id => $e) {
|
||||
if ($e["art"] !== "kette" || isset($aufgeloest[$id])) {
|
||||
continue;
|
||||
}
|
||||
$vater = $liste[$e["kette"]] ?? null;
|
||||
if (!$vater || ($vater["art"] === "kette" && !isset($aufgeloest[$e["kette"]]))) {
|
||||
continue;
|
||||
}
|
||||
$aufgeloest[$id] = true;
|
||||
$eigenVon = $e["_von"];
|
||||
$eigenBis = $e["_bis"];
|
||||
if ($vater["laeufe"]) {
|
||||
$liste[$id]["punkt"] = max($eigenVon, zlMinuten(end($vater["laeufe"])) + $e["versatz"]);
|
||||
} elseif ($vater["punkt"] !== null && $vater["art"] !== "fenster") {
|
||||
$liste[$id]["punkt"] = max($eigenVon, $vater["punkt"] + $e["versatz"]);
|
||||
} elseif ($vater["art"] === "fenster") {
|
||||
$liste[$id]["art"] = "fenster";
|
||||
$liste[$id]["von"] = max($eigenVon, $vater["von"] + $e["versatz"]);
|
||||
$liste[$id]["bis"] = max($liste[$id]["von"], $eigenBis);
|
||||
} else {
|
||||
$liste[$id]["art"] = "jederzeit"; // Auslöser ohne Lage: dann eben ohne
|
||||
}
|
||||
// Eine Kette erbt, ob der Tag passt: läuft der Wecker nicht, kommt auch der Nachfolger nicht.
|
||||
if (!$vater["dran"] && $liste[$id]["dran"]) {
|
||||
$liste[$id]["dran"] = false;
|
||||
$liste[$id]["grund"] = "Auslöser „" . $vater["name"] . "“ ist nicht dran";
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($liste as $id => $e) {
|
||||
if ($e["art"] === "kette" && $e["punkt"] === null) {
|
||||
$liste[$id]["art"] = "jederzeit"; // Kreis oder fehlender Auslöser
|
||||
}
|
||||
unset($liste[$id]["_von"], $liste[$id]["_bis"]);
|
||||
}
|
||||
|
||||
$etagen = [];
|
||||
foreach (grundriss()[0] as $code => $e) {
|
||||
$etagen[] = ["code" => $code, "label" => $e["label"]];
|
||||
}
|
||||
$heute = (new DateTimeImmutable("today"))->format("Y-m-d");
|
||||
return [
|
||||
"datum" => $datum,
|
||||
"heute" => $datum === $heute,
|
||||
"jetzt" => $datum === $heute ? intval(date("G")) * 60 + intval(date("i")) : null,
|
||||
"sonne" => $sonne,
|
||||
"bahnen" => zeitleisteBahnen(),
|
||||
"etagen" => $etagen,
|
||||
"automatiken" => array_values($liste),
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user