AutoAction-Runner: Automatiken ausfuehren
Bisher konnte man Automatiken nur anlegen - ausgefuehrt hat sie niemand. restricted/autoActions/autoaction_runner.py holt das nach. Dauerlaeufer statt Cronjob, aus zwei Gruenden: Schwellwert-Ausloeser sollen greifen, wenn die MQTT-Nachricht hereinkommt, und actor_states.current_value wird sonst von niemandem fortgeschrieben - beim Discovery einmal gesetzt und danach nie wieder. Ein zustandsloser Lauf haette gar nichts, womit er vergleichen koennte. Der Runner pflegt den Wert nebenbei mit, wovon auch der Editor profitiert: er zeigt neben jedem Messwert den aktuellen Stand. Ausgeloest wird nur auf der steigenden Flanke (automations.cond_met), sonst wuerde "Temperatur ueber 22 Grad" bei jedem Takt erneut feuern. Aus demselben Grund heissen Zeit-Ausloeser jetzt "ab 16:30" statt "gleich 16:30": ein Gleichheitsvergleich waere nur in einer einzigen Minute wahr, und ein Ausfall in genau dieser Minute kostet den ganzen Tag. Dieselbe Ueberlegung steht hinter den breiten Zeitfenstern in auto_watering.py. Der Weg zum Geraet haengt an der URL des Aktors: mqtt:// abonniert und publiziert, http:// pollt und haengt Parameter an, io:// spricht mit der Tahoma-Box, Logic rechnet Uhrzeit, Datum und Sonnenzeiten. Alle vier stehen in transports.py; eine fuenfte Geraeteart ist eine weitere Klasse mit passt(), zustaende_lesen() und senden(). fetch_calendar.py fuellt calendar_days aus openholidaysapi.org, damit "in den Ferien" und "an Feiertagen" eine Grundlage haben. Einmal jaehrlich per Cron. Die Uebersicht blendet auf schmalen Schirmen Spalten aus, statt sie wegzuschieben - auf dem Handy waren die Knoepfe zum Pausieren und Loeschen sonst nur per Seitwaertsscrollen erreichbar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,13 @@ function meshDb()
|
||||
* Zeichen macht erst die Anzeige daraus. Frueher schickte der Editor sein
|
||||
* innerHTML ("≠", ">") an den Server, wo eine Whitelist mit "!=" stand:
|
||||
* jede Bedingung "ungleich" wurde still zu "gleich".
|
||||
*
|
||||
* Bei Zeit und Datum gibt es bewusst kein "gleich", sondern "ab" und "vor".
|
||||
* "Uhrzeit = 16:30" waere nur in genau der einen Minute wahr - faellt der
|
||||
* Runner in dieser Minute aus, ist die Automatik fuer den Tag verloren. "ab
|
||||
* 16:30" bleibt bis Mitternacht wahr; ausgeloest wird trotzdem nur einmal,
|
||||
* weil nur die steigende Flanke zaehlt. Dieselbe Ueberlegung steht hinter den
|
||||
* breiten Zeitfenstern in auto_watering.py.
|
||||
*/
|
||||
function operatorsForType($type)
|
||||
{
|
||||
@@ -40,6 +47,10 @@ function operatorsForType($type)
|
||||
case "integer":
|
||||
case "float":
|
||||
return ["=", "!=", ">", "<"];
|
||||
case "time":
|
||||
case "date":
|
||||
case "datetime":
|
||||
return [">=", "<"];
|
||||
case "deltatime": // Sonnenauf-/-untergang: Wert ist ein Versatz
|
||||
return ["+", "-"];
|
||||
case "bool":
|
||||
@@ -49,6 +60,16 @@ function operatorsForType($type)
|
||||
}
|
||||
}
|
||||
|
||||
/** Operatoren mit ihrer Beschriftung, so wie der Editor sie anbietet. */
|
||||
function operatorChoices($type)
|
||||
{
|
||||
$liste = [];
|
||||
foreach (operatorsForType($type) as $op) {
|
||||
$liste[] = ["value" => $op, "label" => operatorLabel($op, $type)];
|
||||
}
|
||||
return $liste;
|
||||
}
|
||||
|
||||
/** Wie das Wertfeld im Editor aussieht. */
|
||||
function inputForType($type, $hasOptions)
|
||||
{
|
||||
@@ -74,9 +95,21 @@ function inputForType($type, $hasOptions)
|
||||
}
|
||||
}
|
||||
|
||||
/** Anzeigeform eines Operators. */
|
||||
function operatorLabel($op)
|
||||
/**
|
||||
* Anzeigeform eines Operators. Bei Zeitangaben liest sich "ab"/"vor"
|
||||
* verstaendlicher als ">="/"<", und bei Sonnenauf-/-untergang steht der
|
||||
* Versatz dahinter: "ab + 00:30" heisst eine halbe Stunde danach.
|
||||
*/
|
||||
function operatorLabel($op, $type = "")
|
||||
{
|
||||
if ($type === "time" || $type === "date" || $type === "datetime") {
|
||||
if ($op === ">=") return "ab";
|
||||
if ($op === "<") return "vor";
|
||||
}
|
||||
if ($type === "deltatime") {
|
||||
if ($op === "+") return "ab +";
|
||||
if ($op === "-") return "ab -";
|
||||
}
|
||||
return $op === "!=" ? "≠" : $op;
|
||||
}
|
||||
|
||||
@@ -124,7 +157,7 @@ function deviceCatalog()
|
||||
"name" => $row["state_name"],
|
||||
"type" => $type,
|
||||
"input" => inputForType($type, count($options) > 0),
|
||||
"operators" => operatorsForType($type),
|
||||
"operators" => operatorChoices($type),
|
||||
"options" => array_values($options),
|
||||
"unit" => $row["unit"],
|
||||
"value" => $row["current_value"],
|
||||
@@ -495,13 +528,16 @@ function listAutomations($floor)
|
||||
function stateNames()
|
||||
{
|
||||
$db = meshDb();
|
||||
$res = $db->query("SELECT s.id, s.state_name, s.unit, a.name AS actor_name
|
||||
FROM actor_states s JOIN actors a ON a.id = s.actor_id");
|
||||
$res = $db->query("SELECT s.id, s.state_name, s.unit, a.name AS actor_name, t.type
|
||||
FROM actor_states s
|
||||
JOIN actors a ON a.id = s.actor_id
|
||||
LEFT JOIN state_types t ON s.state_type = t.id");
|
||||
$names = [];
|
||||
while ($row = $res->fetch_assoc()) {
|
||||
$names[intval($row["id"])] = [
|
||||
"text" => $row["actor_name"] . ": " . $row["state_name"],
|
||||
"unit" => $row["unit"],
|
||||
"type" => $row["type"] ?: "string",
|
||||
];
|
||||
}
|
||||
return $names;
|
||||
@@ -530,8 +566,9 @@ function describeConditions($conditions, $names)
|
||||
{
|
||||
$groups = [];
|
||||
foreach ($conditions as $c) {
|
||||
$state = $names[$c["state_id"]] ?? ["text" => "Messwert " . $c["state_id"], "unit" => null];
|
||||
$text = $state["text"] . " " . operatorLabel($c["operator"]) . " " . $c["value"];
|
||||
$state = $names[$c["state_id"]]
|
||||
?? ["text" => "Messwert " . $c["state_id"], "unit" => null, "type" => ""];
|
||||
$text = $state["text"] . " " . operatorLabel($c["operator"], $state["type"]) . " " . $c["value"];
|
||||
if ($state["unit"]) {
|
||||
$text .= " " . $state["unit"];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user