Ich hatte so was mal vor Jahren im Einsatz. Das Ganze ist aber recht kritisch - Du darfst nie vergessen, dass openHAB kein Echtzeitsystem ist. Mehr noch, es ist asynchron designed. Das ist an mancher Stelle sehr praktisch, für diese Anwendung aber eher hinderlich, da man keine qualifizierten Aussagen über Laufzeiten machen kann.
Dennoch ein Beispielcode:
Code: Alles auswählen
var Long lStart = null // Speicher für Laufzeitmessung
var Integer iRollCommand = null // Zwischenspeicher für Befehl
val Integer iUpTime = 15000 // Gesamtlaufzeit von geschlossen nach offen
val Integer iDownTime = 13000 // Gesamtlaufzeit von offen nach geschlossen
rule "Rollo"
when
Item MyRollo received command // Befehl empfangen
then
var Integer iProzent = (MyRollo.state as Number).intValue // aktuelle gespeicherte Position
switch(receivedCommand) { // Welcher Befehl
case STOP : {
if(lStart === null) return; // falls was schief gegangen ist, raus
val Laufzeit = now.toInstant.toEpochMilli - lStart // Differenz Start zu Stopp in ms
if(iRollCommand == 1) { // Falls Richtung hoch war
iProzent = iProzent - (Laufzeit/iUpTime)*100 // ziehe Anteil der Laufzeit entsprechend von Prozent ab
if(iProzent < 0) iProzent = 0 // falls unter 0, setze 0
} else if(iRollCommand == -1) { // Falls Richtung runter war
iProzent = iProzent + (Laufzeit/iDownTime)*100 // addiere Anteil der Laufzeit
if(iProzent > 100) iProzent = 100 // Falls über 100, setze 100
}
logInfo("rollo",
"Rollo gestoppt. Laufzeit {} ms, neue Position {} %",
Laufzeit,iProzent)
MyRollo.postUpdate(iProzent) // melde neue Position
lStart = null // leeren!
}
case UP : {
lStart = now.toInstant.toEpochMilli // Setze Startzeit
iRollCommand = 1 // Setze Richtung
}
case DOWN : {
lStart = now.toInstant.toEpochMilli // Setze Startzeit
iRollCommand = -1 // Setze Richtung
}
default : { // Positionsfahrt
val Integer iSoll = (receivedCommand as Number).intValue // Setze Sollposition
val Integer iMyTime = 0 // Benötigte Fahrzeit
if(iSoll < iProzent) { // Falls Fahrtrichtung hoch
iRollCommand = 1 // Setze Richtung
iMyTime = (iProzent-iSoll)/100*iUpTime // bestimme benötigte Zeit
}
if(iSoll > iProzent) { // Falls Fahrtrichtung runter
iRollCommand = -1 // Setze Richtung
iMyTime = (iSoll-iProzent)/100*iDownTime // bestimme benötigte Zeit
}
if(iMyTime > 0) { // Falls Fahrtzeit > 0
createTimer(now.plusNanos(1000000*iMyTime),[| // Erzeuge Timer (ohne Zugriff!)
MyRollo.sendCommand(STOP) // Sende Stop
])
MyRollo.sendCommand(if(iRollCommand == 1) UP else DOWN) // Sende Befehl UP oder DOWN
}
}
}
end
Sieht schlimm aus...
Erst mal die globalen Variablen und Konstanten:
lStart ist Zwischenspeicher für einen Unix Zeitstempel (Anzahl Millisekunden seit 1.1.1970,00:00:00.000 Uhr) Hier merkt sich die Rule, wann ein Befehl UP oder DOWN empfangen wurde
iRollCommand dient zum Merken der Fahrtrichtung
Die beiden Konstanten
iUpTime und
iDownTime geben die exakte Verfahrdauer von oben nach unten bzw. von unten nach oben an. Die beiden Zeiten weichen voneinander ab, weil der Motor beim Aufwickeln des Rollladens mehr Kraft benötigt.
Lokale Variablen und Konstanten:
iProzent: Die Position des Ladens
Laufzeit: Die Fahrtdauer in Millisekunden
iSoll: neue Sollposition
iMyTime: Errechnete Fahrzeit zum Erreichen der neuen Sollposition
Die Rule reagiert auf empfangene Befehle. Da die Position an verschiedenen Stellen gebraucht wird, wird zunächst die aktuell gespeicherte Position in iProzent gesichert.
Nun gibt es vier verschiedene Möglichkeiten:
1. Der Befehl lautete STOP
2. Der Befehl lautetet UP
3. der Befehl lautete DOWN
4. etwas Anderes
Im Fall 2 oder 3 müssen der aktuelle Zeitpunkt und die gewünschte Fahrtrichtung gesichert werden.
Im Fall 1 muss zunächst geprüft werden, ob es einen gültigen Wert in lStart gibt - falls nicht, muss die Rule abgebrochen werden. Ist dieser Test überstanden, können wir gefahrlos die Laufzeit bestimmen, indem wir lStart vom aktuellen Unix Zeitstempel abziehen.
Falls die Richtung aufwärts war, ergibt sich die neue Position aus der Differenz der alten Position minus der Laufzeit, geteilt durch die Gesamtlaufzeit * 100. Lief der Motor länger als nötig, um auf Position 0 zu kommen, ergibt sich dabei ein negativer Wert, den wir abfangen müssen, also falls iProzent kleiner 0, setze iProzent auf 0.
Das gleiche gilt sinngemäß für die Abwärtsbewegung, nur dass hier der prozentuale Anteil addiert wird und bei Überschreiten der 100 die 100 als Maximalwert gesetzt wird.
Der logInfo() Befehl sieht etwas komisch aus

ich wollte damit die Kommentar-Einrückungen etwas minimieren...
Für Fall 4, "etwas Anderes" bleibt eigentlich nur noch eine Zahl als Befehl übrig, denn andere Befehle kennt ein Rollershutter Item nicht.
Die Zahl ist also die neue Sollposition.
Nun müssen wir die benötigte Fahrdauer bestimmen. Diese ist natürlich abhängig von der Fahrtrichtung, die sich wiederum daraus ergibt, ob die neue Position kleiner oder größer als die aktuelle Position ist.
Dabei gilt, dass die Differenz aus den beiden Positionen den prozentualen Verfahrweg ergibt, also z.B. von 40 auf 50 wären 10. Das muss man dann durch 100 teilen und mit der Gesamtfahrzeit in Fahrtrichtung multiplizieren, um die nötige Fahrzeit zu ermitteln, natürlich wieder für beide Fahrtrichtungen getrennt

Ergibt sich eine Fahrtzeit über 0 (es könnte ja auch eine Positionsfahrt auf die aktuelle Position angefordert werden), so wird nun also der Timer für den Stop-Befehl gestartet und anschließend der Befehl UP bzw. DOWN gesendet (womit sich die Rule nun selbst triggert!)
Ich hoffe, dass die Theorie hinter der Rule klar wird
Ich hab bestimmt noch irgendwo Fehler drin

wie immer...
Nächstes Level ist dann, diese Rule so zu generalisieren, dass sie mit Gruppen funktioniert (wobei das bei Rollläden kritisch werden könnte - eine Rule kann seit 3.0 nur noch einmal zeitgleich ausgeführt werden, Rollläden werden aber gerne in Gruppen verfahren...)