Die gute Lösung dieses Problems funktioniert anders.
Die DSL bietet objektorientierte Programmierung, da braucht es gar kein loop. Auch die Lösung, jede Minute nach der Sollzeit zu schauen ist suboptimal.
Du hast pro Ventil zwei Items, über das eine wird das Ventil gesteuert, über das andere wird die Bewässerungsdauer in Minuten angegeben, wobei 0 für "überspringen" steht. Es bietet sich an, diese Items in Gruppen zusammenzufassen.
Wichtig ist dabei, dass die Items beider Itemtypen in einem Namensteil identisch sind, so dass man von dem einen auf das andere schließen kann. Nehmen wir der Einfachheit halber zwei Gruppen gBWA_Dauer und gBWA_Ventile.
Es soll jeweils nur ein Kreis laufen, wir müssen also lediglich eine Gruppe durchlaufen, um alles zu erledigen.
Code: Alles auswählen
var Timer tBWA = null // Timer für BWA
var Integer iBWAStep = 0 // Zähler für BWA
rule "enable und disable BWA"
when
Item BWA_Switch changed or // Zustand geändert
Item BWA_Switch_H changed or // Stunde geändert
Item BWA_Switch_M changed // Minute geändert
then
tBWA?.cancel // Timer stoppen
if(newState =! ON) { // Fall inaktiv
gBWA_Ventile.members.filter[i|i.state != OFF].forEach[v|v.sendCommand(OFF)] // Alle regner aus
return; // Und Rule beenden
}
iBWAStep = 0 // Zähler auf 0
var Integer iMinute = 0 // Startzeit auf 0
if(BWA_Switch_M.state instanceof Number) // Falls Minute gültig
iMinute += (BWA_Switch_M.state as Number).intValue // zur Startzeit addieren
if(BWA_Switch_H.state instanceof Number) // Falls Stunde gültig
iMinute += (BWA_Switch_H.state as Number).intValue * 60 // zur Startzeit addieren
if(iMinute == 0) // Falls Startzeit 0
return; // Abbruch
if(iMinute < now.getMinuteOfDay) // Falls startzeit überschritten
iMinute += 60 * 24 // auf nächsten Tag verschieben
tBWA = createTimer(now.withStartOfDay.plusMinutes(iMinute), [| // Timer anlegen
gBWA_Ventile.members.filter[i|i.state != OFF].head.sendCommand(OFF) // Laufende Regenr stoppen
iBWAStep ++ // Zähler erhöhen
if(iBWAStep > gBWA_Ventile.members.size) { // Falls Listenende überschritten
tBWA = null // Timer löschen
BWA_Switch.postUpdate(OFF) // Und Automatik aus
} else { // Ansonsten
val Integer iDauer = (gBWA_Dauer.members.filter[ i|i.name.contains(iBWAStep.toString)].head.state as Number).intValue // Dauer ermitteln
if(iDauer == 0) // Falls Dauer 0
tBWA.reschedule(now.plusMillis(10)) // Timer sofort erneut ausführen
else { // Ansonsten
gBWA_Ventile.members.filter[i|i.name.contains(iBWAStep.toString)].head.sendCommand(ON) // Betreffenden regner starten
tBWA.reschedule(now.plusMinutes(iDauer)) // Und Timer erneut einplanen
}
}
])
end
Ablauf der Rule:
Die Rule löst aus, wenn die Automatik aktiviert oder deaktiviert wurde, oder die Startzeit angepasst wurde.
Zunächst wird ein eventuell laufender Timer abgebrochen.
Falls die Automatik deaktiviert ist, werden alle Regner, die eventuell aktiv sind ausgeschaltet. Damit ist die Rule fertig.
Falls die Automatik aktiviert ist, wird die Startzeit bestimmt. Dazu werden Minute und Stunde ausgelesen und passend aufaddiert. Anschließend erfolgt eine Trivialprüfung, nämlich ob die Startzeit auf Mitternacht fällt. Ist das der Fall, so geht die Rule davon aus, dass die Übernahme der Werte schief gegangen ist und bricht ab.
Ist die Startzeit nach Mitternacht, prüft die Rule, ob die Startzeit schon überschritten wurde. Ist das der Fall, so soll der Zyklus vermutlich am nächsten Tag gestartet werden, also werden 24 Stunden aufaddiert.
Nun wird der Timer initialisiert. Danach ist die Rule fertig.
Ist der Startzeitpunkt erreicht, so wird der Code im Timerblock ausgeführt:
Zunächst werden alle eingeschalteten Regner abgeschaltet.
Nun wird der Schrittzähler erhöht.
Anschließend wird geprüft, ob der Schrittzähler die Listenlänge bereits überschritten hat. Ist das der Fall, so wurde gerade der letzte Regner abgeschaltet. Die Rule löscht den Zeiger auf den Timer und schaltet die Automatik ab. Danach ist der Codeblock abgearbeitet.
Fall das Ende der Liste noch nicht überschritten wurde muss die gewünschte Dauer ausgelesen werden.
Falls die Dauer 0 ist, ruft sich der Timer nach 10 Millisekunden erneut auf, um auf den nächsten Regner zu wechseln.
Ist die Dauer größer als 0, so wird der aktuelle Regner gestartet und der Timer ruft sich zur nächsten Schaltzeit auf.
Der Code funktioniert unabhängig von der Anzahl der Regner, wichtig ist nur, dass alle Regner in der einen Gruppe sind und die Dauer Items in der anderen Gruppe sind.
EDIT: Ich hab noch ein paar Fehler gefunden...
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet