Du denkst zu kompliziert
Im Grunde reichen zwei Rules (um die beiden Funktionen Zeitsteuerung und Beregnung zu trennen).
Damit die Beregnung automatisch zur eingestellten Zeit startet, muss openHAB bei Bedarf einen Timer starten. Sinnvollerweise wird das täglich geprüft. Die Rule "Startzeit einstellen" kümmert sich um Zeit und Wochentage. Damit die Rule bei Änderungen am Wochentag triggert, müssen alle Wochentag-Items in der Gruppe gWeekdays zusammengefasst werden. Die Rule triggert bei einer Änderung an einem der Wochentage, der Startminuten oder Startstunden und täglich um Mitternacht. Da jeder Timer eine gewisse Zeit braucht, bis er soweit ist, ist eine Startzeit um 00:00 Uhr nicht möglich, aber das sollte zu verschmerzen sein.
Die Wochentag-Status werden im Array hinterlegt.
Danach werden die Variablen für Minuten und Stunden initialisiert und falls die Items einen gültigen Wert enthalten, werden die Variablen mit diesem Wert gefüllt. Ansonsten stehen sie auf 0.
Die Werte werden auf Ober- und Untergrenze getestet und gegebenenfalls angepasst. Abschließend wird der Wert ins Item zurückgeschrieben.
Ein Setpoint Widget in der Sitemap kann entsprechend ohne Ober- und Untergrenze definiert werden, die Eingabe springt passend um (sowohl nach oben als auch nach unten).
Die Startzeit wird als Startminute am Tag gemerkt, für später.
Nun wird ein eventuell laufender StartTimer abgebrochen.
Jetzt prüft die Rule, ob "heute" bewässert werden soll. Ist das der Fall, prüft sie, ob die eingestellte Zeit schon erreicht ist. Ist das nicht der Fall, so wird ein StartTimer angelegt, der zur gewünschten Zeit die Beregnung startet.
Die Beregnung selbst läuft wie gehabt. Ich habe lediglich Start und Stop in einer Rule zusammengefasst. Das hat den Vorteil, dass die logisch zusammengehörenden Codeteile auch örtlich zusammengefasst sind.
Ich habe auch jeweils einen Pumpen-Ein- bzw. Ausschaltbefehl eingebaut
Zeitauswahl und Wochentagsauswahl sollten ebenfalls gesperrt werden, analog zur Regnerauswahl. Wenn der Schalter Beregnung in die Oberfläche integriert ist, kann man einen gestarteten Beregnungsvorgang jederzeit stoppen, auch, um eine andere Startzeit zu wählen.
Wichtig ist aber, dass die Startzeit noch nicht erreicht ist, zum Testen um 15:14 muss man also 15:15 einstellen,damit der Timer genug Zeit zur Steuerung hat.
Code: Alles auswählen
var Timer tRegner = null
var int iRegner = 0
var Timer tStartRegner = null
rule "Startzeit einstellen"
when
Member of gWeekdays changed or // Wochentag geändert
Item Startzeit_Minuten changed or // Minuten geändert
Item Startzeit_Stunden changed or // Stunden geändert
Time cron "1 0 0 * * ?" // täglich um 00:00:01 Uhr
then
// Alle Wochentag-Items müssen in der Gruppe gWeekdays zusammengefasst sein, damit der Trigger oben funktioniert
var dayOfWeekSetting = newArrayList(
Monday.state,
Tuesday.state,
Wednesday.state,
Thursday.state,
Friday.state,
Saturday.state,
Sunday.state
)
var int minutes = 0 // Initialwert für Minuten definieren
if(Startzeit_Minuten.state instanceof Number) { // Falls das Item eine gültige Zahl enthält
minutes = Startzeit_Minuten.state as Number // Setze minutes auf diese Zahl
if(minutes > 59) minutes = 0 // Falls Obergrenze überschritten setze auf 0
if(minutes < 0) minutes = 59 // Falls Untergrente unterschritten setze auf 59
}
Startzeit_Minuten.postUpdate(minutes) // Schreibe Wert in das Item
var int hours = 0 // Initialwert für Sunden definieren
if(Startzeit_Stunden.state instanceof Number) { // Falls das Item eine gültige Zahl enthält
hours = Startzeit_Stunden.state as Number // Setze hours auf diese Zahl
if(hours > 23) hours = 0 // Falls Obergrenze überschritten setze auf 0
if(hours < 0) hours = 23 // Falls Untergrente unterschritten setze auf 23
}
Startzeit_Stunden.postUpdate(hours) // Schreibe Wert in das Item
val int startMinutes = hours * 60 + minutes // Zeit in Minuten
logInfo("watering","Kontrolliere Wochentag")
tStartRegner?.cancel // geplante Beregnung canceln
if(dayOfWeekSetting.get(now.getDayOfWeek-1) == ON) { // Falls heute Beregnung gewünscht
if(now.getMinuteOfDay < startMinutes) { // Falls Startzeit noch nicht erreicht
tStartRegner = createTimer(now.withTimeAtStartOfDay.plusMinutes(startMinutes),[| // Setze einen Timer auf gewünschte Startzeit
Beregnung.sendCommand(ON) // Starte zur gewünschten Zeit
])
}
}
end
rule "Beregnung An und Aus"
when
Item Beregnung received command
then
if(receivedCommand == OFF) {
logInfo("watering","Beregnung wird abgebrochen!")
tRegner?.cancel // Timer stoppen
tRegner = null // Variable leeren
gRegner.members.filter[i|i.name.contains("Power")].filter[j| j.state != OFF].forEach[r|r.sendCommand(OFF)] // alle Regner abschalten
Pumpe.sendCommand(OFF) // Pumpe abschalten
Sperre.postUpdate(OFF) // Bediensperre aufheben
} else {
logInfo("watering","Beregnung gestartet.")
if(tRegner !== null) { // falls ein Timer läuft
logInfo("watering","Regner scheint noch zu laufen! Abbruch der Rule!")
return; // Abbruch
}
iRegner = 0 // Zähler auf 0
Sperre.postUpdate(ON) // Bediensperre aktivieren
Pumpe.sendCommand(ON) // Pumpe starten
tRegner = createTimer(now.plusMillis(10), [| // Timer anlegen
iRegner++ // nächsten Regener anwählen
logInfo("watering","{}. Durchlauf",iRegner)
gRegner.members.filter[i|i.name.contains("Power")].filter[j| j.state != OFF].forEach[r|r.sendCommand(OFF)] // alle Regner abschalten
val maxRegner = gRegner.members.filter[i|i.name.contains("Sperre")].filter[j| j.state == OFF].size // wieviele Regner sollen insgesamt beregnen?
logInfo("watering","{}. Durchlauf von {} Durchläufen.",iRegner,maxRegner)
if(iRegner > maxRegner) { // letzten Regner schon erreicht?
logInfo("watering","Alle Regner sind schon gelaufen!")
tRegner = null // Dann Schluss!
Sperre.postUpdate(OFF) // Sperre deaktivieren
Beregnung.postUpdate(OFF) // Item zurücksetzen
Pumpe.sendCommand(OFF) // Pumpe aus
return; // und Timer verlassen
}
Thread::sleep(500) // kleine(!) Pause einlegen
val strRegner = gRegner.members.filter[i|i.name.contains("Sperre")].filter[j| j.state == OFF].sortBy[name].get(iRegner-1).name.split("_").get(2) // Namen des Regner ermitteln
logInfo("watering","Nächster Regner: {}",strRegner)
gRegner.members.filter[i|i.name.contains("Power") && i.name.contains(strRegner)].head.sendCommand(ON) // Regner einschalten
val Dauer =(gRegner.members.filter[i|i.name.contains("Dauer") && i.name.contains(strRegner)].head.state as Number).intValue
logInfo("watering","Regenr wird nach {} Minuten wieder abgeschaltet.",Dauer)
tRegner.reschedule(now.plusMinutes(Dauer)) // Nächste Ausführung planen
])
}
end
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet