Du musst unbedingt zwei Dinge unterscheiden, und zwar Ereignisse und Zustände.
Ein Item hat einen Zustand (state). Ändert sich der Zustand oder wird er auch nur aktualisiert, so ist diese Änderung oder Aktualisierung ein Ereignis, welches zu einem bestimmten Zeit
punkt auftritt.
Rules werden ausschließlich durch Ereignisse gestartet, deshalb heißt es auch, sie werden getriggert.
Alle Trigger einer Rule werden im when-Teil der Rule aufgelistet. Dabei ist es gleichgültig, welcher der Trigger die Rule auslöst, sie wird bei jedem Ereignis einmal ausgeführt.
Es gibt für Deine Aufgabenstellung zwei unterschiedliche Lösungsansätze, die einfache (aber auch ungenaue) und die "smarte" aber aufwändigere.
dein Ansatz entspricht erst mal der einfachen Variante. Deine Rule wird zwischen 8 Uhr und 22 Uhr beginnend zur vollen Stunde alle 25 Minuten ausgelöst. Das heißt: 8:00:00, 8:25:00, 8:50:00, 9:00:00(!) ... 21:00:00, 21:25:00, 21:50:00. Es wäre vermutlich besser, die Rule alle 20 Minuten triggern zu lassen, oder alternativ all 30 Minuten. Eventuell könnte ein Trigger "0 */25 8-21 * * ?" hier das gewünschte Verhalten bringen (regelmäßig alle 25 Minuten)
Jetzt muss die Rule prüfen, ob eines der iPhones da ist (dies ist ein Zustand, kein Ereignis!) so:
Anschließend sollte der Code zum Einschalten und Ausschalten so funktionieren, wie er da steht.
Code: Alles auswählen
rule "Cron Zirkulation"
when
Time cron "0 */25 8-21 * * ?"
then
if(iPhones.state == ON) {
logInfo("Z-Cron","Zirkulation eingeschaltet")
WWPumpe.sendCommand(ON)
createTimer(now.plusSeconds(300)) [ |
WWPumpe.sendCommand(OFF)
]
}
end
Wie gesagt ist das zumindest nicht sonderlich smart. Das Problem dabei: Nehmen wir an, Du kommst nach Hause und Dein iPhone verbindet sich exakt um 8:00:01 mit dem WLAN (bzw. es wird exakt zu diesem Zeitpunkt als vorhanden registirert, da passieren ja auch noch ein paar Dinge vorher, je nachdem, wie die Erkennung umgesetzt ist). Die erste Zirkulation findet dann um 08:25:00 Uhr statt, denn die Rule lief ja um 08:00:00 und zu dem Zeitpunkt war iPhones noch OFF.
Smarter wäre also eine Rule, welche direkt triggert, wenn das erste Telefon erkannt wird und dann innerhalb der Rule prüft, ob auch der Zeitrahmen passt. so:
Code: Alles auswählen
var Timer tZPumpe = null
rule "Cron Zirkulation"
when
Time cron "0 0 8,22 * * ?" or // Um 8 Uhr und um 22 Uhr
Item iPhones changed // Wenn Anwesend oder Abwesend
then
var Boolean bPumpe = false // Default ist aus
if(now.getHour > 7 && now.getHour < 22 && iPhones.state == ON) // Zeitfenster erfüllt und anwesend
bPumpe = true // an
if(bPumpe && tZPumpe === null) { // Pumpe soll laufen, Timer läuft aber nicht
logInfo("zpumpe","Zirkulation Interval gestartet!")
tZPumpe = createTimer(now.plusSeconds(1),[| // erzeuge Timer und führe ihn auch gleich aus
var Integer iTime = 20 * 60 // Ausschaltdauer setzen
if(WWPumpe.state != ON) // Falls Pumpe gerade aus,
iTime = 5 * 60 // Einschaltdauer setzen
logInfo("zpumpe","Timer: Pumpe schaltet nun für {} Sekunden {}.",iTime,if(WWPumpe.state != ON) "ein" else "aus")
WWPumpe.sendCommand(if(WWPumpe.state != ON) ON else OFF) // Toggle Pumpe
tZPumpe.reschedule(now.plusSeconds(iTime)) // und lass den Timer in iTime Sekunden erneut ausführen
])
} else if(!bPumpe) { // Pumpe soll nicht laufen
tZPumpe?.cancel // falls ein Timer läuft, abbrechen
tZPumpe = null // und Variable leeren
if(WWPumpe.state != OFF) // Falls Pumpe gerade läuft
WWPumpe.sendCommand(OFF) // Ausschalten
logInfo("zpumpe","Zirkulation Interval beendet!")
}
end
Die Rule triggert, wenn sich der Zustand des Items ändert (von ON auf OFF oder auch von OFF auf ON), sowie um 8 Uhr und um 22 Uhr.
Sobald die Rule ausgeführt wird, wird berechnet, ob die Zirkulation mit Interval gestartet werden soll oder nicht, also ob das Zeitfenster erfüllt und jemand anwesend ist.
Im nächsten Schritt wird geprüft, ob die Zirkulation laufen soll, aber der Timer nicht aktiv ist. Ist dies der Fall, so wird der Timer angelegt und die Rule ist beendet.
Andernfalls wird geprüft, ob die Zirkulation abgeschaltet werden soll. Ist dies der Fall, so wird ein eventuell laufender Timer beendet und deinitialisiert. Falls die Pumpe gerade läuft, wird diese angehalten. damit ist die Rule beendet.
Die eigentliche Arbeit - das zyklische Schalten der Pumpe - geschieht nun ausschließlich über den Timer Code, weshalb ich den hier nochmal extra einfüge (nur zur Anschauung):
Code: Alles auswählen
var Integer iTime = 20 * 60 // Ausschaltdauer setzen
if(WWPumpe.state != ON) // Falls Pumpe gerade aus,
iTime = 5 * 60 // Einschaltdauer setzen
logInfo("zpumpe","Timer: Pumpe schaltet nun für {} Sekunden {}.",iTime,if(WWPumpe.state != ON) "ein" else "aus")
WWPumpe.sendCommand(if(WWPumpe.state != ON) ON else OFF) // Toggle Pumpe
tZPumpe.reschedule(now.plusSeconds(iTime)) // und lass den Timer in iTime Sekunden erneut ausführen
Erreicht der Scheduler die geplante Zeit, so wird der Code ausgeführt.
Zunächst wird die Variable iTime mit der Ausschaltdauer vorbelegt.
Falls die Pumpe gerade nicht läuft, wird iTime auf die Einschaltdauer gesetzt
Anschließend wird die Pumpe getoggelt, falls sie also ausgeschaltet ist, wird sie eingeschaltet und umgekehrt.
Zum Schluss wird der Timer neu geplant, mit der vorher ausgewählten Zeit.
Die log Meldung verdient auch noch ein paar Worte
Code: Alles auswählen
logInfo("zpumpe","Timer: Pumpe schaltet nun für {} Sekunden {}.",iTime,if(WWPumpe.state != ON) "ein" else "aus")
Die beiden geschweiften Klammerpaare werden substituiert, d.h. mit dem Inhalt der Variablen ersetzt, die angehängt sind. Zum einen ist das die Schaltdauer, zum anderen der Zustand, welcher aus dem aktuellen Zustand abgeleitet wird. Ist die Pumpe gerade nicht an, so wird eingeschaltet, ansonsten ausgeschaltet. Das Konstrukt
if(a) b else c heißt übrigens
ternärer Operator, also wenn a zutrifft nimm b, ansonsten c. Überaus praktisch, wie man sehen kann
Weil es immer wieder für Verwirrung sorgt:
= -> Zuweisung eines Wertes.
== -> Vergleich linker Wert mit rechter Wert.
=== -> Prüfung, ob Zeiger auf der linken Seite und Zeiger auf der rechten Seite gleich sind. Bei Prüfung auf null soll mit
=== getestet werden (für ungleich mit
!==).
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet