Präsenz-Simulation mit openHAB

In unserem Haus sind inzwischen alle Lichtschalter mit Aktoren von Homematic ersetzt und ein Raspberry Pi 3 übernimmt mit openHAB die Steuerung, siehe hierzu diverse vorherige Blogartikel.

Aber letztlich ist es egal, ob es Homematic oder irgendein anderer Hersteller benutzt wird: Hauptsache die Geräte werden durch openHAB unterstützt.

Nun steht demnächst die Urlaubszeit an und auch hier kann man den Raspberry und openHAB dazu verwenden, das Zuhause zu schützen. Ein Element des Schutzes kann eine Anwesenheitssimulation sein. Ist Licht an, wird vielleicht jemand zu Hause sein. Idealerweise „lebt“ das Zuhause auch noch durch das Wechseln verschiedener Lichtschaltungen, so dass der Eindruck entsteht, es ist wirklich jemand zu Hause.

Folgende Anforderungen sollen umgesezt werden:

  • Die Präsenzsteuerung soll via Oberfläche ein- und ausschaltbar sein
  • Die Präsenzsteuerung soll nicht an sein, wenn die Sonne noch nicht untergegangen ist und spätestens um halb zwei Nachts beendet sein
  • Die Präsenzsteuerung soll definierte Lampen für die Vorder- und Rückseite des Hauses kennen und zufällig schalten; dabei sollen Übergangszeiten und Zufallszahlen-Anteile abrupte Übergänge verhindern und die Schaltbilder sollen alls 15 Minuten wechseln

openHAB - Oberfläche: Präsenzsteuerung
openHAB – Oberfläche: Präsenzsteuerung

Zum Ablauf:

  • Um auf Sonnenauf- und -untergang zurückgreifen zu können ist das astro-binding installiert.
  • Das Scheduling erfolgt via Cronjob.
  • Die Lampen, welche für vorne und hinten zur Verfügung stehen sollen sind in der .items-Datei der Gruppe pSimH und pSimV zugewiesen (vorn und hinten)
  • Soll geschalten werden, sucht die Rule zufällig eine Lampe je für vorn und für hinten aus, welche noch nicht an ist. Diese Lampe wird dann an geschalten und mit einem Timer versehen und nach der Wartezeit inkl. etwas Puffer wieder ausgeschalten.
  • Um das Ganze besser verfolgen zu können erfolgt ein Logging in der openhab.log

.items-Datei:

Group pSimH
Group pSimV
Switch praesenzSimulation "Präsenz-Simulation"    <shield>
DateTime    astro_Sonnenaufgang            "Sonnenaufgang [%1$tH:%1$tM]"                            (gruppeAstro)    {astro="planet=sun, type=rise, property=start"}
DateTime    astro_Sonnenuntergang        "Sonnenuntergang [%1$tH:%1$tM]"                            (gruppeAstro)    {astro="planet=sun, type=set, property=start"}

.sitemap-Datei:

(...)
   Text label="Präsenz-Simulation" icon="network" {
      Switch item=praesenzSimulation
   }
(...)

.rules-Datei:

import org.openhab.core.library.types.*
import org.joda.time.*
import java.util.TimeZone

val java.util.Random rand = new java.util.Random

rule "Praesenzsimulation durchführen"
 when
 Time cron "0 0/15 17-23 * * ?" or
 Time cron "0 0/15 0-1 * * ?"
 // Rule soll alle fünfzehn Minuten aktiv werden im Zeitraum von 17:00 - 01:30 Uhr
 then
 
 var Integer schaltintervall = 15 // Sollte mit Cron-Aufruf-Intervall übereinstimmen
 var Integer schaltueberlappung = 4 // Zufällige Überlappung, damit nicht alle Lampen gleichzeitig ausgehen (plus zus. 1 Minute)
 var DateTime datumSonnenaufgang = new DateTime((astro_Sonnenaufgang.state as DateTimeType).calendar.timeInMillis)
 var DateTime datumSonnenuntergang = new DateTime((astro_Sonnenuntergang.state as DateTimeType).calendar.timeInMillis)
 val boolean istDunkel = datumSonnenaufgang.afterNow || datumSonnenuntergang.beforeNow

val Boolean log = true

if (log) logInfo('rules','Präsenzsimulation: Rule gestartet. istDunkel=' + istDunkel.toString() + ' praesenzSimulation=' + praesenzSimulation.state.toString())
 
 if (praesenzSimulation.state == OFF) {
 if (log) logInfo('rules','Präsenzsimulation: Präsenzsimulation ausgesetzt - nicht aktiv!')
 } else if ((praesenzSimulation.state == ON) && (!istDunkel)) {
 if (log) logInfo('rules','Präsenzsimulation: Präsenzsimulation ausgesetzt - aktiv, aber es ist noch nicht dunkel!')
 } else { // es ist dunkel und die Simulation wurde aktiviert
 if (log) logInfo('rules','Präsenzsimulation: Präsenzsimulation wird ausgeführt')
 // Je Präsenzsimulations-Lichtergruppe soll mindestens ein Licht geschalten sein. (pSimH und pSimV)
 var pSimHAnzahl = (pSimH.members.size)
 var pSimVAnzahl = (pSimV.members.size)

// je Gruppe eine zufallsgenerierte Lampen schalten für Zufallszeit zwischen schaltintervall + schaltüberlappung in Minuten
 var Integer schaltzeitH = rand.nextInt(schaltueberlappung) + schaltintervall + 1
 var Integer schaltzeitV = rand.nextInt(schaltueberlappung) + schaltintervall + 1
 var Integer counterH = 0
 var lampeHON = true
 var lampeH = 0
 var boolean exitFlag = false
 
 // LampeH suchen, welche noch nicht AN ist, maximal 30 Versuche, sonst endlosschleife, wenn alle Lampen an wären
 while (lampeHON && !exitFlag) {
 lampeH = rand.nextInt(pSimHAnzahl)
 lampeHON = (pSimH.members.get(lampeH).state == ON) || (pSimH.members.get(lampeH).state > 0)
 if (log) logInfo('rules', 'Präsenzsimulation: Versuch Nr. ' + counterH + ' Ermittelte LampeH (Index) ' + lampeH + '/' + pSimH.members.get(lampeH).name + ' Status lampeHON ' + lampeHON.toString())
 counterH = counterH + 1
 if (counterH > 30) exitFlag = true
 }
 if (!exitFlag) {
 // LampeH schalten und Timer für Ausschalten setzen
 if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeH an: ' + pSimH.members.get(lampeH).name + ' für ' + schaltzeitH + ' Minuten')
 pSimH.members.get(lampeH).sendCommand(ON)
 createTimer(now.plusSeconds(schaltzeitH * 60)) [|
 pSimH.members.get(lampeH).sendCommand(OFF)
 if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeH aus: ' + pSimH.members.get(lampeH).name + ' nach ' + schaltzeitH + ' Minuten')
 ]
 } else {
 logInfo('rules','Präsenzsimulation: Keine freie LampeH gefunden!')
 }
 // LampeV suchen, welche noch nicht AN ist, maximal 30 Versuche, sonst ergibt sich evtl eine Endlosschleife, wenn alle Lampen an wären
 var Integer counterV = 0
 var lampeVON = true
 var lampeV = 0
 exitFlag = false
 while (lampeVON && !exitFlag) {
 lampeV = rand.nextInt(pSimVAnzahl)
 lampeVON = (pSimV.members.get(lampeV).state == ON) || (pSimV.members.get(lampeV).state > 0)
 if (log) logInfo('rules', 'Präsenzsimulation: Versuch Nr. ' + counterV + ' Ermittelte LampeV (Index) ' + lampeV + '/' + pSimV.members.get(lampeV).name + ' Status lampeVON ' + lampeVON.toString())
 counterV = counterV + 1
 if (counterV > 30) exitFlag = true
 }

if (!exitFlag) {
 // LampeV schalten und Timer für Ausschalten setzen
 if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeV an: ' + pSimV.members.get(lampeV).name + ' für ' + schaltzeitV + ' Minuten')
 pSimV.members.get(lampeV).sendCommand(ON)
 createTimer(now.plusSeconds(schaltzeitV * 60)) [|
 pSimV.members.get(lampeV).sendCommand(OFF)
 if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeV aus: ' + pSimV.members.get(lampeV).name + ' nach ' + schaltzeitV + ' Minuten')
 ] 
 } else {
 if (log) logInfo('rules','Präsenzsimulation: Keine freie LampeV gefunden!')
 }
 }
end

Viel Spaß damit

 

5 Antworten auf „Präsenz-Simulation mit openHAB“

  1. Hi,

    klasse idee und ich habe es direkt auch mal übernommen. Leider bekomme ich eine Fehlermeldung im Log wenn die 17 Uhr erreicht werden. Error during the execution of rule Präsenzsimulation durchführen
    java.lang.NullPointerException: null

    Finde den Fehler irgendwie nicht.

    1. Hallo Marco,

      Danke für den Hinweis – im geposteten Script war leider ein kleiner Fehler enthalten, der sich wohl beim Hineinkopieren eingeschlichen hatte…
      Ich habe das Script aktualisiert, das aktuelle Script funktionierte bei mir nun im Test. Versuche es bitte nocheinmal damit!

      Viele Grüße
      Florian

  2. Hallo Florian,

    Funktioniert wunderbar. Hab es gestern bei mir neu eingespielt und laufen lassen.
    Läuft stabil und macht was es soll. Sehr cool. Danke.

    Gruß Marco

  3. Hallo Florian,

    eine tolle Anwendung – vielen Dank dafür.

    Einen kleinen Fehler habe ich noch gefunden – in der while-schleife (lampeVON && !exitFlag) muss es beim ersten if „+ counterV +“ lauten, anstatt „+ counterH +“ , sonst steht im Logfile immer für die vorderen Lampen „Versuch Nr. 31“.

    Im VS Code-Editor gibt es diverse Fehlermeldungen (Cannot refer to the non-final variable lampeH inside a lambda expression), die aber scheinbar ignoriert werden können – sieht aber nicht so schön aus …
    https://github.com/eclipse/smarthome/issues/4711

    Dirk

    1. Hallo Dirk,

      Danke für das Feedback, da hast Du recht, das war ein kleiner Fehler, der sich aber nur in der leicht fehlerhaften Log-Ausgabe niedergeschlagen hat. Ich habe das entsprechend abgeändert im Script.

      Bezüglich der lambda-expression-Ferhler: Ja, die Fehler sind irritierend, die im Editor angezeigt werden, aber man kann sie getrost ignorieren. Eine Lösung um die loszuwerden wäre z.B, diese global in der Rule zu definieren. Aber das finde ich persönlich nicht so schön, denn dann gibt es globale Variablen die aber dann doch nur Rule-spezifisch sind… Von daher habe ich lieber die angeblichen Fehler, die der Editor anzeigt, ist in meinen Augen sauberer….

      Viele Grüße
      Florian

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

This site uses Akismet to reduce spam. Learn how your comment data is processed.