Mein Tipp an der Stelle: Denk mal ganz genau über den Code nach, den Du da verzapft hast.
Du kommst von zwei Number Items, welche Stunde und Minute darstellen. Das ist aus verschiedenen Gründen suboptimal.
Du prüfst, ob es einen Überlauf gibt, für die Stunde >23, für die Minute >59 . Die Idee an sich ist gut, leider funktioniert es aber nicht so, wie Du Dir das vorstellst, beginnend damit, dass sendCommand hier mit allerhöchster Wahrscheinlichkeit ungeeignet ist. Erschwerend kommt hinzu, dass die Rule auf received update triggert, auch dafür gibt es keinen Grund.
received command -> triggert, wenn das Item einen Befehl empfangen hat. Dieser Trigger löst z.B. aus, wenn man in der UI den Wert eines Items sendet.
changed -> triggert, wenn sich der Wert eines Items geändert hat.
received update -> triggert, sobald ein Item ein Status Update erhalten hat, unabhängig davon, ob dies eine Änderung bewirkt hat oder nicht.
Du hast also einen Trigger gewählt, der immer auslöst, obwohl die Rule nur bei einer Wertänderung triggern muss.
Schlimmer: in der Rule verwendest Du sendCommand auf die Items, welche die Rule triggern. sendCommand führt (default Verhalten) zu einem Update des Status des Items (hier sicher auch zu einer Wertänderung), allerdings erst als Folge des bereits ausgeführten Befehls. Du maximierst also die Laufzeit des Befehls, obwohl Du doch eigentlich möglichst schnell aus der 60 oder der 24 eine 0 machen willst. Die Rule wird also sich selbst triggern, allerdings wird die Ausführung des Triggers verzögert, bis die Rule vollständig abgearbeitet ist. Das heißt, die nachfolgende Berechnung erhält definitiv zunächst den 60er Wert oder den 24er Wert, erst im nächsten oder gar übernächsten Durchlauf der Rule passt der Wert dann.
Dann bestimmst Du Startstunde und Startminute, versuchst damit (auf absurd komplizierte Weise) eine DateTime Variable zu füllen, die Du dann nutzt, um daraus wieder Stunde und Minute zu extrahieren. Was soll das?
Und dann kann ich an den Namen der Items ablesen, dass es fast sicher mindestens weitere neun Items und weitere drei Rules gibt, die alle exakt das gleiche erledigen...
Je nachdem, wie die Oberfläche definiert ist, wäre es wesentlich sinnvoller, für jede Zeit (zum Einstellen) nur ein Item zu nutzen. Das Item enthält dann die Minute des Tages. Du definierst vier Knöpfe, der erst Knopf zieht 60 ab, der zweite Knopf addiert 60, der drite Knopf zieht 1 ab, der vierte Knopf addiert 1. Alternativ kannst Du natürlich auch andere Schrittweiten wählen, aber das wäre naheliegend. Du erhältst damit also Stunde +/- und Minute +/-, nur musst Du Dich nicht mehr um den Überlauf der Minute kümmern. das Item bekommt Minimum -1 und Maximum 1440, die Rule macht aus 1440 eine 0 und aus -1 eine 1439.
Die Rule triggert auf received command, so dass die Rule nur noch auslöst, wenn Du in der UI einen der Knöpfe drückst.
Die Rule errechnet alle notwendigen Werte, schickt sie dann aber mit postUpdate in das TagesminutenItem, so dass die Rule selbst dadurch nicht ein weiteres Mal getriggert wird.
Code: Alles auswählen
rule "Set Program A Startime"
when
Item ProgramA_StartTimeM received command // Item hat Befehl empfangen
then
var Integer iVal = -2 // initialisiere mit ungültigem Wert
if(receivedCommand instanceof Number) // Falls Befehl eine Zahl ist
iVal = (receivedCommand as Number).intValue // schreibe die Zahl in die Variable
if(iVal < -1) { // Falls Wert außerhalb des erwarteten Bereichs
logWarn("setTimer","Received illegal command {}",receivedCommand) // Warnmeldung
return; // und Abbruch der Rule
}
// ab hier ist alles gut!
if(iVal == 1440) // Überlauf nach oben
iVal = 0 // also auf Minimum setzen
else if (iVal == -1) // Überlauf nach unten
iVal = 1439 // also auf Maximum setzen
val hour = (iVal/60).intValue // Stunde ermitteln
val minute = iVal - hour * 60 // Minute ermitteln
// update der Startzeit in der für Rules und Anzeige
ProgramA_StartTime.sendCommand(String.format("%02d:%02d", hour, minute) // Zeit senden
ProgramA_StartTimeM.postUpdate(iVal) // und Steuer-Item setzen
end
Auch diese Rule kümmert sich zunächst nur um eine Zeit. Wenn Du allerdings die Items passend organisierst, reicht eine einzige Rule für alle Schaltzeiten. Du brauchst immer noch zwei Items pro Zeitpunkt (das eine für die Anzeige, das andere für die Wahl des Werts), aber eben nur noch eine einzige Rule. Dazu müssen die Items in zwei Gruppen zusammengefasst werden. Also alle ...TimeM-Items kommen z.B. in die Gruppe gProgramTimeM und alle ...Time-Items kommen in die Gruppe gProgramTime. Dann sieht die eine Rule, die sich um alle Items kümmert, so aus:
Code: Alles auswählen
rule "Set Program Times"
when
Member of gProgramTimeM received command // Item hat Befehl empfangen
then
var Integer iVal = -2 // initialisiere mit ungültigem Wert
if(receivedCommand instanceof Number) // Falls Befehl eine Zahl ist
iVal = (receivedCommand as Number).intValue // schreibe die Zahl in die Variable
if(iVal < -1) { // Falls Wert außerhalb des erwarteten Bereichs
logWarn("setTimer","Received illegal command {}",receivedCommand) // Warnmeldung
return; // und Abbruch der Rule
}
// ab hier ist alles gut!
if(iVal == 1440) // Überlauf nach oben
iVal = 0 // also auf Minimum setzen
else if (iVal == -1) // Überlauf nach unten
iVal = 1439 // also auf Maximum setzen
val hour = (iVal/60).intValue // Stunde ermitteln
val minute = iVal - hour * 60 // Minute ermitteln
// update der Startzeit in der für Rules und Anzeige
gProgramTime.members.filter[i|
triggeringItem.name.startsWith(i.name)
].head.sendCommand(String.format("%02d:%02d", hour, minute) // Zeit senden
triggeringItem.postUpdate(iVal) // und Steuer-Item setzen
end
Die Rule musste also nur minimal angepasst werden, um nun mit den beiden Gruppen zu arbeiten.
triggeringItem liefert das Item, welches die Rule über Member of getriggert hat. Es handelt sich dabei dann um ein "echtes" Item, es hat also alle Methoden, die auch ein gewöhnliches Item hat. triggeringItem.name ist also der Name des Items, welches die Rule ausgelöst hat.
filter[] liefert eine gefilterte Liste aller Items der Liste, die aufgerufen wird (die Liste besteht aus den Membern der gewählten Gruppe)
Am Itemnamen fehlt lediglich das M am Schluss, das heißt also, der Name des triggernden Items beginnt mit dem vollständigen Namen des Items, welches selektiert werden soll.
Zum Schluss wird das triggernde Item per postUpdate auf den neuesten Stand gebracht.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet