Also, die Rule ist ja nun nicht vollständig, insofern ist die Schwere Deines Vergehens nicht abschätzbar

aber Deine Aussage, dass Du ja "nur zwei Rules" hast, welche auf das Item Lichtsensor_Aussen reagieren, und deshalb könnte die grenze von fünf gleichzeitig laufenden Rules nicht überschritten sein, zeigt, dass Du noch nicht vollständig verstanden hast, wie openHAB funktioniert.
Eine Rule (eine!) kann theoretisch hunderte Male gleichzeitig ausgeführt werden, wenn sie nur oft genug getriggert wird und lang genug läuft (und man die Anzahl gleichzeitig ausführbarer Rules entsprechend hochgesetzt hat - tu das nicht!)
Die Threads beziehen sich auf alle Rules insgesamt. Wenn Du also viele Rules hast, die ähnlich gegen die grundsätzlichen Regeln der Programmierung verstoßen wie der Teil der gezeigten Rule, ist es im Gegenteil sogar sehr wahrscheinlich, dass Deine Rules sich gegenseitig blockieren.
Der erste Punkt ist, dass man es grundsätzlich vermeiden sollte, Rules zu definieren, die identische Trigger aufweisen. Es gibt nur wenige Situationen, in denen das ok ist, und man sollte genau überlegen, ob die Rule Engine sich dann tatsächlich immer korrekt verhalten wird (z.B. welche der Rules wurde als erstes gestartet).
Der zweite Punkt ist die Sache mit dem Thread::sleep(). Es ist eine ganz schlechte Idee, Rules zu schreiben, die wesentlich länger als einige hundert Millisekunden(!) laufen.
Was die konkrete Steuerung der Rollläden betrifft: Gibt es wichtige Gründe, ganz bestimmte Abstände zwischen den einzelnen Rollladenfahrten zu verwenden? Ein automatisches Schließen ist sicherlich ok, auch dass bestimmte Läden auf eine Lüftungsposition gefahren werden, aber das macht es natürlich etwas schwerer, die Rule effizient zu gestalten. Aber mal als Beispiel:
Code: Alles auswählen
// globale Variablen zu Beginn der Datei definieren!
var Timer tShutter = null
var Integer iShutter = 0
rule "Rollläden schließen"
when
Item Lichtsensor_Aussen changed
then
if(tShutter !== null) { // Timer läuft schon
logInfo("shutter","Rule getriggert, aber Timer bereits aktiv. Abbruch!")
return;
}
if(!(Lichtsensor_Aussen.state instanceof Number)) { // ist der Status eine Zahl?
logWarn("shutter","Lichtsensor liefert ungültigen Status ({}). Abbruch!",Lichtsensor_Aussen.state)
return;
}
val Number nPrev = if(previousState instanceof Number) (previousState as Number).floatValue else 20 // alten Wert auslesen
if((Lichtsensor_Aussen.state as Number).floatValue < 10 && nPrev > 10) { // Falls Schwellwert unterschritten und vorher nicht unterschritten
iShutter = 0 // Zähler zurücksetzen
tShutter = createTimer(now.plusMillis(10),[| // Timer anlegen und starten
iShutter = iShutter + 1 // Zähler erhöhen
switch(iShutter) { // Abhängig vom Zähler
case 1: { // 1. Schritt
Rollo_Bad_Level.sendCommand(100)
tShutter.reschedule(now.plusSeconds(15)) // Timer erneut ausführen in 15 Sekunden
}
case 2: { // 2. Schritt
Rollo_SchlafzKleider_Level.sendCommand(100)
tShutter.reschedule(now.plusSeconds(7))
}
case 3: { // 3. Schritt
Rollo_SchlafzGarage_Level.sendCommand(100)
tShutter.reschedule(now.plusSeconds(2))
}
case 4: { // 4. Schritt
Rollo_SchlafzBalkon_Level.sendCommand(32)
tShutter.reschedule(now.plusSeconds(3))
}
case 5: ... // 5. Schritt
default: { // letzter Schritt
tShutter = null
}
}
])
}
end
Allein die große Menge an gleichartigen Befehlen sieht nicht schön aus. Gegenbeispiel:
Code: Alles auswählen
// globale Variablen zu Beginn der Datei definieren!
var Timer tShutter = null
var Integer iShutter = 0
val lShutter = newArrayList('Bad', 'SchlafzKleider', 'SchlafzGarage', 'SchlafzBalkon')
val lShutterClose = newArrayList(100, 100, 100, 32)
val lShutterTime = newArrayList(15, 7, 2, 3)
rule "Rollläden schließen"
when
Item Lichtsensor_Aussen changed
then
if(tShutter !== null) { // Timer läuft schon
logInfo("shutter","Rule getriggert, aber Timer bereits aktiv. Abbruch!")
return;
}
if(!(Lichtsensor_Aussen.state instanceof Number)) { // ist der Status eine Zahl?
logWarn("shutter","Lichtsensor liefert ungültigen Status ({}). Abbruch!",Lichtsensor_Aussen.state)
return;
}
val Number nPrev = if(previousState instanceof Number) (previousState as Number).floatValue else 20 // alten Wert auslesen
if((Lichtsensor_Aussen.state as Number).floatValue < 10 && nPrev > 10) { // Falls Schwellwert unterschritten und vorher nicht unterschritten
iShutter = 0 // Zähler zurücksetzen
tShutter = createTimer(now.plusMillis(10),[| // Timer anlegen und starten
if(lShutter.size >= iShutter) {
tShutter = 0
return;
}
sendCommand("Rollo_" + lShutter(iShutter) + "_Level",lShutterClose(iShutter).intValue) // Shutternamen aus Liste berechnen und mit passendem Wert ansteuern
tShutter.reschedule(now.plusSeconds(lShutterTime(iShutter).intValue)) // Timer erneut planen
iShutter = iShutter + 1 // Zähler erhöhen
])
}
end
Hier werden die Namen, Sollhöhen und Wartezeiten aus drei Listen entnommen. Wird das Ende der Liste erreicht, stoppt der Timer.
Ich setze hier voraus, dass alle Rollläden dem Namensschema folgen. Auch wenn die Variante mit der Arraylist nicht optimal ist, kann man sehr schön sehen, dass der Code wesentlich effizienter ist. Die Rule wird nicht länger, egal, wie viele Rollläden auch in der Liste eingetragen sein mögen.
Das Wichtigste bei der Rule ist aber, sicherzustellen, dass der Code nur einmalig gestartet wird. das macht den weitaus größeren Teil der Rule aus

Der Timer sorgt dafür, dass die Threads minimal belegt sind - wenige Millisekunden bei Wertänderung des Sensors, wenige Millisekunden wenn ein Rollladen angesteuert wird.
openHAB4.3.5 stable in einem Debian-Container (bookworm) (Proxmox 8.4.1, LXC), mit openHABian eingerichtet