Anwesenheit mit Node-Red simulieren

Die mechanischen Zeitschaltuhren mit ihrem Drehrad waren lange Jahre das einzige Mittel, zuhause Lampen zeitgesteuert einzuschalten. Damit wurde dann in Abwesenheit vorgetäuscht, dass das Haus bewohnt ist. Mit Smarthome wird diese Funktion bereits mit vielen fertigen App-Lösungen angeboten. Für mich stellt sich die Frage, wie man so etwas übergeordnet und damit unabhängig von den meisten Smarthome-Zentralen und ihren App-Lösungen realisieren  und damit beliebige Geräte steuern kann.

Als Plattform habe ich Node-Red gewählt.

Zufallszahlen erzeugen

Für meine Umsetzung tue ich so, als ob ich nur eine Lampe schalten möchte. Die folgenden Werte dienen zur Demonstration und können natürlich frei verändert werden. Ich benötige folgende zufällig erzeugten Werte in einem vorgegebenen Intervall [min;max]):

  1. Dauer: Wie lange soll das Licht anbleiben (ich gebe den Bereich von 1 bis 30 Minuten vor (Intervall [1;60])
  2. Startdauer: Um wieviel Minuten ab jetzt verzögert soll ein Licht erst angehen. (auch hier soll der Start von 1 bis 15 Minuten in der Zukunft liegen, (Intervall [1;60])

Um diese Zufallswerte zu erzeugen braucht man einen Zufallsgenerator. Unter Node-Red wird dies mit der Math.random()-Funktion erreicht, die eine zufällige rationale Zahl von 0 bis kleiner 1 erzeugt.

Um hieraus eine Zahl für das gewünschte Intervall zu erzeugen, verwendet man folgende Formel:

(Math.random() * (max - min + 1)) + min

Damit wird gewährleistet, dass die Zufallszahl immer beim min-Wert beginnt und auch den max-Wert berücksichtigt.
Als Ergebnis kommt eine rationale Zahl heraus, die ich aber noch in eine ganze Zahl umwandeln muss.
Hierzu verweise ich auf den Artikel in selfhtml, in dem verdeutlicht wird, warum zum Runden die Math.floor()-Funktion besser geeignet ist, als die Math.round()-Funktion.

Daraus ergibt sich die Formel

Zufallswert = Math.floor((Math.random() * (max - min + 1)) + min)

und somit die Berechnungsformeln für die zwei Werte oben.

Flow Variablen verwenden

Die oben ermittelten Zufallswerte muss man in Node-Red Variabeln zwischenspeichern, um auf sie zurückgreifen zu können. Ich benötige daher sogenannte Flow-Variabeln, die ich aus jeden Knoten in dem Flow abrufen kann. Mit dem Befehl

var vara=flow.get('f_vara') || 0;

wird der Wert der flow-Variabel 'f_count' der lokalen Variabel 'vara' zugewiesen. Sollte 'f_count' nicht existieren, dann wird automatisch der Wert 0 (Null) der lokalen Variabeln 'vara' zugewiesen.

Mit dem Befehl

flow.set('f_vara',vara);

wird der Flow-Variabeln 'f_vara' wieder der Wert der lokalen Variabeln 'vara' zugewiesen. Über diesen Weg lassen sich alle Variebeln für den Flow sauber initialisieren.

Für den Node-Red Flow verwende ich folgende Flow Variabeln:

  • f_startzeit (hierin steht die Startzeit zu der das Licht angehen soll)
  • f_stopzeit (dies ist die Stopzeit, zu der das Licht wieder ausgehen soll)
  • f_dauer (dies ist die Zeitspanne, die das Licht anbleiben soll in Minuten)
  • f_verzoegerung (dies ist die Zeitspanne, bevor das Licht eingeschaltet werden soll in Minuten)

Node-Red Flow

Ich verzichte explizit auf eine Timer Funktion, da dann wieder eine Bibliothek eingebunden werden müsste. In dem Beispiel habe ich nur angedeutet, wo eigene Aktoren eingebunden werden können, wenn man das Beispiel verwenden möchte.

Aus den ganzen Überlegungen ist der folgende Flow entstanden:

Anwesenheitssimulation Node-Red

Der Code hierzu lautet wie folgt:

Copy to Clipboard

Flow Erläuterungen

Die Inhalte der Nodes erfüllen folgende Aufgaben:

Node Beschreibung
Check pro Minute Der Inject Node startet das Ganze und wird jede Minute ausgeführt. Er übermittelt die aktuelle Ausführungszeit und wird einmal bei Neustart initiiert.
Initialisierung Diese Function Node dient dazu, alle Flow-Variabeln zu initialisieren. Dies ist insbesondere dann wichtig, wenn Node-Red neu gestartet wurde und es die Variabeln noch nicht gibt.
Entscheidung Im Switch Node werden zwie Bedingungen unterschieden:

  • Ist die Startzeit erreicht, dann führe die Startfunktionen durch
  • Ist die Stopzeit erreicht oder überschritten, dann ermittle neue Zeiten
Millisekunden entfernen Ich habe festgestellt, dass der Inject Node zwar jede Minute, manchmal aber mit einer Millisekunde Verzögerung gestartet wurde. Damit der Vergleich im switch node mit der Startzeit funktioniert, muss ich diese Ungenauigkeit ausschließen und lösche daher die für mich uninteressanten Millisekunden für die weitere Verarbeitung.
Dauer Lichtphase übermitteln Wenn das Licht eingeschaltet werden soll, dann kann man z.B. bei Homematic Aktoren die Dauer der Lichtphase vorab übermitteln. Dies kann man in diesem Function-Node machen. Für meinen Homematic IP Aktor muss ich die Zeit in Sekunden angeben, so dass ich dies in diesem Node umrechne. Im Anschluss sollte der Node platziert werden, der den Wert an den Aktor übermittelt (für HMIP wäre das ein "set value"-Node).
Einschalten Licht Über diesen Function-Node kann der Wert generiert werden, der an einen Aktor zum Einschalten gesendet werden muss. In meinem Fall wird der Homemtiac IP Aktor mit einer 1 aktiviert, die ich in dieser Funktion einfach belege. Anschließend würde der Wert über einen entsprechenden Node an den Aktor gesendet werden. Damit der Einschaltbefehl sicher nach der Dauer der Lichtphase übermittelt wird verzögere ich diese Befehlsausführung vorher noch um eine Sekunde.
Neue Startzeiten ermitteln In diesem Function-Node ermittle ich alle neuen Zeiten.

Zuerst werden Dauer und Verzögerung, wie oben beschrieben ermittelt. Für die Verzögerung ermittle ich Werte zwischen 1 und 30 Minuten für die Dauer zwischen 1 und 15 Minuten.

Aus diesen Werten kann ich nun mit der Zeit des Inject Nodes (wird als payload übergeben) die Stratzeit sowie die Stopzeit ermitteln.

Die Formatieren und Debug Nodes Diese Nodes dienen nur zur Ausgabe der laufenden Werte, damit man ein Ergebnis sieht und können später gelöscht werden.
Manuell Mit diesem Inject-Node kann ich das manuelle Erzeugen der Zeiten anstoßen (wenn mir das mal zum Testen zu lange dauert).

Abschluss

Ich habe an einigen Stellen natürlich mehr Code als notwendig ergänzt, da das Ganze dann auch für den Laien lesbarer ist.

Der Flow kann beliebig erweitert und umgebaut werden.

Denkbar ist z.B. die Einschränkung auf den Zeitbereich zwischen Sonnenunter- und Sonnenaufgang. Darüber hinaus würde ich die Schaltung zufällig n-Mal wiederholen. Ebenso könnte man das Ganze für mehrere Lampen anpassen und diese abhängig voneinander schalten lassen.