Die Logik von WebUI-Programmen

Mein zaghafter Versuch, die Logik von WebUI-Programmen zu erklären

Die Logik von WebUI-Programmen führt immer wieder zu Verwirrung – nicht zuletzt, weil die Weboberfläche der CCU eine andere Logik suggeriert, als tatsächlich angewendet wird.

Ich möchte an dieser Stelle versuchen zu erläutern, was wann wie ausgeführt wird. Man erkennt schon an der Länge dieser Seite, dass das ein etwas umfangreicheres Thema ist.

Benennung von Systemvariablen

Prinzipiell kann man Systemvariablen – so wie allen Objekten in der CCU – beliebige Namen geben, also z. B. auch Umlaute und Sonderzeichen verwenden. Ich empfehle jedoch, sich auf reguläre Buchstaben (a-z, A-Z) zu beschränken: Bei Umlauten und Sonderzeichen besteht die Gefahr, dass Systemvariablen in Scripten nicht überall gefunden werden.

„Aktualisierung“, „Änderung“ und „prüfen“

Schauen wir uns dieses einfache Beispiel-Programm an.

Der Raumthermostat, der hier in der Bedingung angegeben ist, sendet regelmäßig alle 2-3 Minuten die Temperatur an die CCU. Immer, wenn diese die Temperatur empfängt, prüft sie, ob mindestens eine der Bedingungen für die Programmauslösung erfüllt ist.

bei Änderung auslösen

Im Beispiel ist „bei Änderung“ ausgewählt. Die CCU meint damit aber nicht etwa die Änderung der Temperatur, sondern nur die Änderung der Bedingung: Bei 20,0°C ist die Bedingung nicht erfüllt, bei 20,1°C ist sie erfüllt. Das Programm wird nur ausgeführt, wenn die Temperatur auf 20,1°C steigt.

Steigt sie weiter, ist die Bedingung weiter erfüllt – das Programm wird nicht ausgeführt.

Sinkt sie wieder auf 20°C oder weniger, ist das zwar eine Änderung – aber die Bedingung ist nicht mehr erfüllt, also wird das Programm auch nicht ausgeführt.

Erst wenn die Temperatur danach wieder steigt, kommt es zur Programmausführung.

bei Aktualisierung auslösen

Die Programmausführung erfolgt hier immer, wenn die Bedingung erfüllt ist – nicht nur dann, wenn sie vorher nicht erfüllt war.

Sendet der Raumthermostat 20,1°C, wird das Programm ausgeführt.

Aktualisiert er auf einen beliebigen Wert von 20,1°C oder mehr, wird das Programm ebenfalls ausgeführt – also beim Raumthermostat alle 2-3 Minuten.

Sinkt die Temperatur auf 20,0°C oder weniger, wird das Programm nicht mehr ausgeführt, weil die Bedingung nicht erfüllt ist.

nur prüfen

Diese Option dient dazu, eine Bedingung zu prüfen, ohne dass der entsprechende Kanal das Programm selbst auslöst. Für dieses einfache Programm macht das erst mal keinen Sinn  – es würde niemals aufgrund einer Bedingung ausgeführt.

Mehrere Bedingungen

Fügt man mehrere Bedingungen ein, so werden diese natürlich alle beachtet:

Die Logik ist hier wie erwartet:

Programmauslösung und Programmausführung

Was man unbedingt im Hinterkopf behalten sollte: Programmauslösung und Programmausführung sind zwei unterschiedliche Dinge. Ich habe das Programm mal etwas geändert:

Auf den ersten Blick würde man erwarten, dass bei Überschreiten von 20.0°C der erste „Dann“-Teil ausgeführt wird. Wenn irgendwann die 25.0°C überschritten wurden, dann wird bei jeder Aktualisierung der zweite „Dann“-Teil ausgeführt.

Tatsächlich wird aber bei jeder Programmauslösung – egal, von welcher Bedingung sie angestoßen wurde – das komplette Programm ausgeführt, d.h. alle Bedingungen werden geprüft, bis das Programm den ersten zutreffenden „Dann“-Teil ausführen kann.

Eine Bedingung reicht

Um ein WebUI-Programm auszulösen, reicht es, wenn nur eine der Bedingungen erfüllt ist.

Das Programm wird nur durch den Raumthermostat ausgelöst. Die zweite Bedingung – ist das Licht ausgeschaltet? – steht auf „nur prüfen“ und kann somit das Programm nicht triggern.

Die Bedingungen für den Raumthermostat sind freilich widersprüchlich: Es kann nicht gleichzeitig wärmer und kälter als 20.0°C sein.

Logische Folgerung: Das Programm wird nie ausgeführt, richtig? Falsch. Tatsächlich wird das Programm alle 2-3 Minuten ausgeführt: Entweder von der ersten Bedingung, dass es wärmer als 20.0°C ist, oder von der zweiten, dass es kälter ist. Nur bei exakt 20.0°C passiert nichts, denn dann ist keine der beiden auslösenden Bedingungen erfüllt.

Sobald irgendeine Bedingung zutrifft, wird das gesamte Programm ausgeführt – und wenn das Licht ausgeschaltet ist, dann ist die „nur prüfen“-Bedingung erfüllt und der „Dann“-Teil wird ausgeführt.

Manuelle und automatische Programmausführung

Wenn ein Programm unter „Status und Bedienung“ / „Programme“ aufgerufen wird, dann wird immer der erste „Dann“-Teil ausgeführt. Das ist wenigstens endlich mal einfach.

Wenn ein Programm automatisch ausgeführt wird, also weil eine der Bedingungen für die Programmauslösung erfüllt ist, dann werden sämtliche Bedingungen geprüft wie im vorherigen Abschnitt beschrieben.

Neben der Programmausführung aufgrund einer erfüllten Bedingung gibt es noch drei weitere Gründe, warum ein Programm angestoßen wird:

Auch bei diesen automatischen Starts wird das komplette Programm ausgeführt und die Bedingungen werden geprüft – genauso, als wäre das Programm über eine Bedingung ausgelöst worden.

Nehmen wir ein Beispiel:

Das Programm ist eigentlich simpel:

Schon beim Abspeichern stellt man fest, dass da was nicht stimmt, denn es kommt eine E-Mail mit dem Hinweis, dass Sabotage behoben wurde – obwohl doch zuvor gar keine erkannt war.

Das Gleiche passiert beim Neustart: Das Programm wird automatisch ausgeführt. Die Bedingung „Sabotage“ trifft nicht zu, also wird der „Sonst“-Teil ausgeführt.

Bei einer Infomail ist das kein Problem, aber wenn man z. B. eine Rolladensteuerung programmiert oder Fenster und Türen mit WinMatic und KeyMatic öffnen und schließen lässt, dann sollte man unbedingt darauf achten, dass alle Programme sich bei Neustarts in einem definierten Zustand befinden.

Im schlimmsten Fall – wenn man z. B. einen automatischen Neustart der CCU programmiert – kann die CCU in einer Endlos-Schleife enden, aus der man sie nicht herausbekommt. Im Zweifelsfall sollte man daher neue Programme immer zunächst mit ungefährlichen Aktionen erstellen und die eigentlichen Aktivitäten erst hinterher ergänzen.

Das Zeitmodul

Um das Zeitmodul mache ich einen großen Bogen.

Das Programm im obigen Beispiel wird zu Beginn und Ende des Zeitraums ausgelöst und dann jeweils komplett abgearbeitet. Bei der Ausführung dient der Zeitraum als Bedingung: Innerhalb des angegebenen Bereichs (also nach dem Startzeitpunkt) ist sie erfüllt, außerhalb (also nach dem Endzeitpunkt) nicht.

Wird das Programm durch andere Bedingungen ausgelöst, so wird der angegebene Zeitraum genauso ausgewertet – oder auch nicht. In der Praxis kommt es gelegentlich vor, dass Bedingungen des Zeitmoduls in unerwarteter Weise interpretiert werden, wenn das Programm nicht durch das Modul selbst ausgelöst wurde …

Insgesamt bin ich dazu übergegangen, nicht allzu intensiv mit dem Zeitmodul zu arbeiten. Im Gegensatz zur sonstigen WebUI-Logik bin ich mir da nie so ganz sicher, ob es denn dann wirklich auch genau das tut, was es soll … Spätestens bei der Frage, wie ein Programm bei einem Neustart der CCU reagiert, wird es schnell haarig.

Einfache Aufgaben – also z. B. ein Programm einmal pro Stunde starten oder auch in anderen Programmen prüfen, ob sie z. B. tagsüber ausgelöst wurden – kann man damit durchaus realisieren.

Was man keinesfalls tun sollte, ist mehrere Zeitmodule im selben Programm anzuwenden. Hier würde ich den Umweg über mehrere WebUI-Programme gehen, die dann über Systemvariablen das eigentliche Programm steuern.

Aktualisierung durch Scripte

In meinem Mini-Framework für das E-Mail-Add-on wird der E-Mail-Versand angestoßen, sobald durch ein Programm die Systemvariable E-Mail.Versand gesetzt wird.

Wer E-Mail.Versand über ein WebUI-Programm setzt, erlebt keine Überraschungen: Die Systemvariable wird aktualisiert, das Programm wird ausgelöst und die E-Mail verschickt.

Wer allerdings ein Script streng nach Scriptdokumentation von eQ-3 schreibt, steht auf dem Schlauch: Nichts passiert. Die Systemvariable bekommt ihren Wert, aber das Programm wird nicht ausgeführt.

Zeit, sich die Befehle zum Setzen und Auslesen von Datenpunkten näher anzuschauen.

.Variable()

Laut Dokumentation sollen Systemvariable über diese Funktion in Scripten geändert werden.

Der Haken: Die Systemvariable bekommt zwar den richtigen Wert zugewiesen, gilt aber nicht als „aktualisiert“. Programme, die durch diese Systemvariable ausgelöst werden sollten, werden nicht ausgelöst.

dom.GetObject ("E-Mail.Subject").Variable ("Betreff");
dom.GetObject ("E-Mail.Body").Variable ("Mailkörper");
dom.GetObject ("E-Mail.Versand").Variable (1);

Nichts passiert.

.Value()

In der Anfangszeit der CCU konnte man mit dieser Funktion nicht nur den aktuellen Wert eines Datenpunktes auslesen, sondern auch Systemvariablen einen neuen zuweisen. Dabei trat das gleiche Problem auf wie bei .Variable().

Inzwischen ist das Problem gelöst: .Value() funktioniert nicht mehr, um Werte zu setzen.

.State()

Das ist der Weg, über den Werte gesetzt werden: Die Systemvariable wird aktualisiert und davon abhängende Programme ausgeführt.

dom.GetObject ("E-Mail.Subject").State ("Betreff");
dom.GetObject ("E-Mail.Body").State ("Mailkörper");
dom.GetObject ("E-Mail.Versand").State (1);

.State() kann aber noch mehr: Wenn Datenpunkte von Geräten, die dauerhaft auf Empfang sind, mit dieser Funktion abgefragt werden, dann fragt die CCU aktiv das entsprechende Gerät nach einem aktuellen Status. Das funktioniert zum Beispiel bei Schaltaktoren, Dimmern oder auch der WinMatic.

Ich nutze diese Eigenschaft bei meinem zweiten Script zur Behandlung von Kommunikationsstörungen, um Geräte anzusprechen, ohne Schaltfunktionen auszulösen.

Abgesehen davon, dass diese Kommunikation ein Script natürlich ausbremst, handelt es sich hierbei freilich um eine ganz normale Aktualisierung. Das heißt: Wenn .State() verwendet wird, um den Status eines Kanals abzufragen, werden die von diesem Kanal abhängigen Programme ggf. getriggert. In der Regel wird man also .Value() verwenden wollen, um den Status eines Gerätes abzufragen.

Navigation