Also, es geht mal damit los, dass Du Variablen falsch definierst.
Eine Timer Variable wird benötigt, um auf einen Timer zugreifen zu können. Man sollte grundsätzlich immer mit Timer Variablen arbeiten. ABER: Timer Variablen
müssen global definiert sein, denn der Timer existiert auch noch, wenn die Rule schon längst beendet wurde. Eine lokal definierte Variable wird mit Beenden der Rule aus dem Speicher entfernt.
Beide Rules haben den gleichen Namen, das ist nicht erlaubt. Namen müssen systemweit eindeutig sein!
Was soll das Thread::sleep()? Bitte mach das nicht. Nie.
Es gibt nur eine Ausnahme dieser Regel, nämlich, wenn Du per .postUpdate() oder per .persist() einen Wert in ein Item schreibst oder das Item gezielt persistieren lässt um anschließend über historicState oder irgendwelche Persistence Aggregationen (sumSince usw) auf die Persistence zuzugreifen. Dann muss man tatsächlich eine kleine (!) Denkpause einlegen und zu Thread::sleep(100) greifen - Benötigt eine Rule wesentlich längere Pausen, hast Du etwas falsch gemacht.
Die erste Rule...
Du prüfst zunächst, auf
Shelly_WZ_Power_Out.state != ON (der Status ist nicht ON). Ist dies nicht der Fall (d.h. der Status ist ON), prüfst Du auf
Shelly_WZ_Power_Out.state == OFF (der Staus ist OFF). Da dies aufgrund der vorigen Bedingung ausgeschlossen ist, wird der Code niemals ausgeführt.
Besser:
Code: Alles auswählen
// Globale Variablen immer vor der ersten Rule definieren!
var Timer tFridgeOn = null // Timer für Warnmelung per Broadcast Notification
rule "Power Gefrierschrank"
when
Item Shelly_WZ_Power_Out changed
then
tFridgeOn?.cancel
switch(newState) {
case OFF : {
logWarn("fridge","Stromversorgung Gefierschrank wurde ausgeschaltet!")
tFridgeOn = createTimer(now.plusMinutes(1), [|
sendBroadcastNotification("Gefrierschrank wurde ausgeschaltet!")
])
}
case ON : {
logInfo("fridge","Stromversorgung Gefierschrank wurde eingeschaltet.")
}
default : {
logError("fridge","Ungültiger Zustand des überwachten Items! ({})",newState)
}
}
end
Ändert sich der Status des Items Shelly_WZ_Power_Out wird die Rule gestartet.
Zunächst wird ein eventuell laufender Timer beendet und entfernt. Das ? bedeutet: nur falls die Variable einen gültigen Zeiger enthält, entspricht also diesem Code:
Ja, bei einer Prüfung auf
null (und nur exakt auf null!) schreibt man !== bzw. ===.
Nachdem der eventuell laufende Timer beseitigt ist, prüft die Rule auf drei verschiedene Möglichekiten:
Zustand OFF: eine Warnmeldung wird geloggt, außerdem startet ein Timer, der nach einer Minute eine Meldung versendet.
Zustand ON: Eine Information wird geloggt.
alle anderen Situationen: ein unerwareter Zustand. Dies wird als Fehler geloggt (mit aktuellem Zustand)
newState ist eine impliziete Variable, die den aktuellen Wert des Items enthält, welches die Rule mit
changed getriggert hat. Ist einfach kürzer...
Für den letzten Fall möchtest Du eventuell ebenfalls eine Notification raus hauen, hier wäre es dann sinnvoll, gleich den Zustand mitzumelden, also z.B.
Code: Alles auswählen
sendBroadcastNotification("Gefrierschrank Stromversorgung gemeldeter Zustand ist "+newState.toString)
Nun zum zweiten Teil:
Wenn man mit Status arbeitet, die Messwerte enthalten, ist es immer gut, sich abzusichern, dass der gelieferte Status auch tatsächlich ein gültiger Wert ist (ein Number Item kann u.A. auch UNDEF oder NULL liefern)
Wie lange die normalen Kompressorzyklen sind, weißt Du natürlich am besten

aber denke auch daran, dass man manchmal Ungefrorenes in dne Schrank packt, dann läuft der Kompressor im Zweifel etwas länger,
Etwa so:
Code: Alles auswählen
// Globale Variablen immer vor der ersten Rule definieren!
var Timer tFridgePow = null // Timer für Einschaltdauer
rule "Gefrierschrank Tür offen Erkennung"
when
Item Shelly_WZ_Power changed
then
var nPac = 0.0
if(newState instanceof Number)
nPac = (newState as Number).floatValue
else {
logWarn("fridge","Messung der Leistung liefert ungültigen Wert! ({})",newState)
return;
}
if(nPac < 5 && tFridgePow !== null) {
logInfo("fridge","Kompressor Gefrierschrank hat abgeschaltet.")
tFridgePow.cancel
tFridgePow = null
} else if(nPac > 40 && tFridgePow === null) {
logInfo("fridge","Kompressor Gefrierschrank hat eingeschaltet.")
tFridgePow = createTimer(now.plusMinutes(1), [| // maximale EZ eine Minute?
logWarn("fridge","Kompressor Gefrierschrank zu lange eingeschaltet. Tür prüfen!")
sendBroadcastNotification("Laufzeitüberwachung Kompressor Gefrierschrank hat ausgelöst! Tür prüfen!")
// tFridgePow.reschedule(now.plusMinutes(15)) // erneute Meldung in 15 Minuten
// tFridgePow = null // neue Meldung sobald erneut Leistung über 40 erkannt
])
}
end
Zunächst wird eine lokale Variable mit 0.0 vorbelegt (openHAB generiert daraus automatisch einen Typ Float)
Anschließend wird der aktuelle Status auf Gültigkeit geprüft und gegebenenfalls in die Variable übernommen. Andernfalls bricht die Rule mit einer LogMeldung ab.
Läuft der Code weiter, so gibt es zwei interessante Fälle:
Erstens, die aufgenommene Leistung ist unter 5 und ein Timer ist vorhanden, dann muss dieser Timer gecancelt und auf null gesetzt werden.
Zweitens, die aufgenommen Leistung ist über 40 und es ist kein Timer vorhanden, dann muss ein Timer angelegt werden.
Und nun scheiden sich die Geister... Es gibt drei Möglichkeiten, was der Timer macht, entsprechend zwei Zeilen Code, oben auskommentiert:
- einmalige Meldung. Danach ist Ruhe. Du lässt beide Zeilen auskommentiert.
- erneute Meldung frühestens nach (z.B.) 15 Minuten - unter der Voraussetzung, dass der Kompressor nicht zwischenzeitlich abgeschaltet hat. das wäre die erste im Code auskommentierte Zeile
- Erneute Meldung sobald wieder eine Leistung über 40 gemessen wird (abweichend von der vorher gemessenen Leistung, Meldung wieder nach einer Minute) Das wäre die zweite auskommentierte Zeile
Das ist ein Entweder - Oder (oder keine der beiden Zeilen, keinesfalls beide Zeilen gleichzeitig aktivieren!)
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet