Files
Smart-Dashboard/restricted/autoActions
adminandClaude Opus 5 5f643b9844 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>
2026-08-30 19:21:07 +02:00
..

AutoActions

Automatiken, die im Web-UI unter „Automatismen" angelegt werden und hier ausgeführt werden: wenn Bedingung, dann Kommando.

Browser                     Datenbank homeMesh              Runner
──────────────────────      ─────────────────────────       ────────────────────
Karte „Automatismen"   ──▶  automations                ──▶  autoaction_runner.py
ajax/AutoAction.php         automation_conditions           MQTT / HTTP / Tahoma
restricted/automations.php  automation_actions              ──▶ Geräte
js/solar/autoActionFuncs.js automation_action_params
                            automation_log
                            calendar_days              ◀──  fetch_calendar.py

Wie eine Automatik aufgebaut ist

Eine Automatik hat Auslöser, Rahmenbedingungen und Aktionen.

Ein Auslöser vergleicht einen Messwert (actor_states) mit einer Schwelle. Mehrere Auslöser werden über group_no verknüpft: gleiche Nummer heißt UND, verschiedene Nummern heißen ODER — ausgewertet wird any(all(gruppe)). Im Editor ist eine Gruppe ein gerahmter Block mit eigenem „+ Bedingung", zwischen den Blöcken steht ein ODER. Die Klammerung ist damit gezeichnet und nicht bloß vereinbart, und darunter steht derselbe Ausdruck noch einmal als Satz.

Eine Aktion ist ein Kommando (actor_commands) mit einem Wert je Parameter (command_parameters) — nicht vier feste Spalten „Wert 1" bis „Wert 4", sondern so viele Zeilen, wie das Gerät Parameter hat.

Die Rahmenbedingungen (Wochentage, Zeitfenster, Ferien, Feiertage) sagen, wann die Automatik überhaupt hinsehen darf.

Warum ein Dauerläufer und kein Cronjob

Zwei Gründe:

  • Schwellwert-Auslöser sollen greifen, wenn die MQTT-Nachricht hereinkommt, nicht erst im nächsten Minutenraster.
  • actor_states.current_value wird sonst von niemandem fortgeschrieben — es wird beim Geräte-Discovery einmal gesetzt und danach nie wieder. Ein zustandsloser Cronjob hätte gar nichts, womit er vergleichen könnte. Der Runner pflegt den Wert nebenbei mit (gedrosselt auf einmal je Minute), wovon auch der Editor profitiert: er zeigt neben jedem Messwert den aktuellen Stand.

Nur die steigende Flanke

automations.cond_met hält fest, ob die Bedingung beim letzten Durchlauf schon erfüllt war. Ohne das würde „Temperatur über 22 Grad" bei jedem Takt erneut feuern. Verlässt die Automatik ihr Zeitfenster, wird die Flanke zurückgesetzt, damit sie im nächsten Fenster wieder steigen kann.

Aus demselben Grund heißen Zeit-Auslöser „ab 16:30" und nicht „gleich 16:30". Ein Gleichheitsvergleich wäre nur in genau einer Minute wahr — fällt der Runner in dieser Minute aus, ist die Automatik für den Tag verloren. „Ab 16:30" bleibt bis Mitternacht wahr, ausgelöst wird trotzdem nur einmal. Die breiten Zeitfenster in auto_watering.py folgen derselben Überlegung.

force_once („am Ende des Zeitraums auf jeden Fall ausführen") greift, wenn das Fenster zugeht und in diesem Fenster noch nichts passiert ist.

Transporte

Welcher Weg zum Gerät führt, entscheidet die URL des Aktors in actors:

URL Messwert (actor_states.url) Kommando
mqtt://… vollständiges Topic, abonniert publiziert auf das Parameter-Topic
http://… Feldname in der JSON-Antwort, gepollt Parameter als Abfrageargumente
io://… Tahoma-Statusname, gepollt exec/apply an die Tahoma-Box
Logic gerechnet: Uhrzeit, Datum, Sonne

Alle vier stehen in transports.py. Eine fünfte Geräteart kommt als weitere Klasse dazu; sie braucht passt(), zustaende_lesen() und senden().

Bei HTTP heißen die Schaltbefehle nicht so wie in der Datenbank — dafür gibt es in HTTPTransport eine kleine Übersetzungstabelle (turn_onon=true). Das ist die Stelle, die beim Anschluss neuer HTTP-Geräte wächst.

Voraussetzungen

  • Python 3 mit pymysql, requests, paho-mqtt
  • homeMesh_automations.sql einmal eingespielt
  • In ../deviceDiscovery/config.ini muss clear_tables = false stehen. Discovery schreibt mit ON DUPLICATE KEY UPDATE auf den URLs, das Leeren ist unnötig — ein TRUNCATE würde dagegen die Geräte-IDs neu vergeben, und die Automatiken zeigen per Fremdschlüssel genau auf diese IDs.

Einrichten

cp config.ini.example config.ini      # ausfüllen: Datenbank, MQTT, Tahoma
python3 fetch_calendar.py             # Feiertage und Ferien holen
python3 autoaction_runner.py --once --dry-run --verbose   # Probelauf

--dry-run schaltet nichts, protokolliert aber jedes Kommando, das geschickt würde. --once macht einen einzigen Durchlauf.

Im Dauerbetrieb wird autoaction_runner.py beim Booten gestartet (auf der Synology über den Aufgabenplaner, Ereignis „Hochfahren", als root). Er verbindet sich selbst neu, wenn MQTT wegbricht, und lädt das Regelwerk nach, sobald im Browser etwas gespeichert wurde — ein Neustart nach jeder Änderung ist nicht nötig.

fetch_calendar.py gehört einmal jährlich in den Cron:

0 4 1 1 * /usr/bin/python3 /volume1/web/smart/restricted/autoActions/fetch_calendar.py

Ein zusätzlicher Lauf im Herbst schadet nicht — die Ferientermine des übernächsten Schuljahres stehen erst später fest.

Nachsehen, was passiert ist

automation_log hält je Auslösung fest, ob sie durchlief (fired), wegen force_once nachgeholt wurde (forced) oder scheiterte (error, mit Grund in detail). Einträge älter als 30 Tage räumt der Runner selbst weg.