Seite 1 von 1

Timer in Rule wenn Rule neu triggert?

Verfasst: 7. Aug 2023 17:10
von Homer-S
Hallo zusammen,

ich habe eine komplexe Rule um meine Rollos zur Beschattung zu steuern.
Wenn diese "elseif" erfüllt ist, starte ich einen Timer um zu verhindern, dass bei kleinen Schwankungen die Rollos dauernd rauf/runter fahren.

Ich habe aber kein Wissen darüber, wie sich OH verhält, wenn die Rule während der Timer läuft neu getriggert wurde. Könnt ihr mir hierbei bitte helfen?

Code: Alles auswählen

    else if (TempAussDiff <= tempdiffwirdgefahren && Sun.state == ON) {
        if (log) logInfo("****RolloSteuerung****", "-----ELSE IF erfuellt -Temperatur ist wieder gefallen-")
        createTimer(now.plusMinutes(5), [ | // schlafe 5 minuten
            if ((TemperatureAussenVorne.state as Number) - (TemperatureAussenHinten.state as Number) + (tau * ((TemperatureAussenHinten.state as Number) - (TemperatureAussenDifferenz.state as Number))) < (tempdiffwirdgefahren-1)) {
                Sun.postUpdate(OFF)
                AziOGSued.sendCommand(OFF)
                AziEGSued.sendCommand(OFF)
                AziEGSuedMitte.sendCommand(OFF)
                AziWest.sendCommand(OFF)
            }
        ])
Wenn es nützt, kann ich auch noch den Rest posten, aber der ist echt elendig lang (aber funktioniert)....
danke

Re: Timer in Rule wenn Rule neu triggert?

Verfasst: 7. Aug 2023 19:39
von udo1toni
Der Timer ist ein Eintrag im Scheduler. Die Rule startet, macht den Eintrag im Scheduler und ist wieder beendet.
Wenn Du also einen Timer verwenden willst, um übereifriges Schalten zu verhindern, gibt es einen extrem simplen Ansatz:

Code: Alles auswählen

var Timer tSperre = null

rule ...
then
    if(tSperre !== null) // tSperre ist nicht identisch mit null -> Timer gesetzt
        return;          // also Abbruch der Rule
    tSperre = createTimer(now.plusMinutes(5),[|
        tSperre = null
    ])
    // ab hier das, was die Rule erledigen soll
Die Rule wird getriggert und prüft, ob der Timer vorhanden ist (tSperre !== null). Ist das der Fall, wird die Ausfühung der Rule abgebrochen.
Läuft der Timer ab, so löscht er die Timer Variable, so dass nun die Abfrage beim nächsten Durchlauf nicht mehr erfüllt ist.
Entsprechend ist das erste, was man im Code erledigt, den Timer zu aktivieren, um die Rule gleich mal für die weitere Ausführung zu sperren.

Deine Logzeile ist ein Verbrechen. :)

Die Log-Befehle erwarten zwei Strings als Parameter, wobei der erste String der Loggername ist - allerdings nur ein Teil des Loggernamens, da alle Logger für Rules den gleichen Vorfahren haben. Der Loggername ist möglichst kurz und enthält keine Zeichen außer das englische Alphabet, evtl. noch die arabischen Ziffern und vielleicht einen Punkt. Im schlimmsten Fall meinetwegen noch einen Unterstrich oder ein Minuszeichen, das ist aber schon unüblich.
in openHAB wird gewöhnlich CamelCase verwendet, also z.B. rolloSteuerung - wobei das schon viel zu lang ist, natürlich handelt es sich um eine Steuerung, was denn sonst? Sinnvoll wäre also "rollo" als Loggername, zusammen mit dem Vorfahren erscheint dann [org.openhab.core.model.script.rollo] im Log. Der zweite String ist die eigentliche Meldung die geloggt wird.

Nutzt man die Loggernamen korrekt :) , dann kann man einfach im laufenden Betrieb über die Karaf Konsole das Logging gezielt ein- und ausschalten, aber nicht nur ON/OFF, sondern in Stufen (DEFAULT, DEBUG, INFO, WARN, ERROR, OFF)
DEFAULT erbt den Loglevel vom Vorfahren (der steht gewöhnlich auf INFO)
DEBUG gibt alle Logmeldungen aller Logbefehle aus.
INFO loggt keine logDebug() Anweisungen
WARN loggt nur logWarn() und logError()
ERROR loggt ausschließlich logError() Meldungen
OFF schaltet das Logging für diesen einen Logger komplett aus.

Es ist großer Bullshit, das über ein if(log) zu erledigen, man sollte stattdessen die komfortablen Systemwerkzeuge nutzen.

Und falls man das partout über die UI in openHAB schalten will (das geht momentan leider noch nicht für die Rules, wohl aber für die meisten Addons), dann wäre ein gangbarer Weg, über das Exec Binding - oder aus einer Rule heraus über executeCommandLine() - einen einzelnen Befehl an die Karaf Konsole zu senden, der dann den Loglevel wunschgemäß setzt.
Die Umschaltung ist in jedem Fall unmittelbar und persistent.

Re: Timer in Rule wenn Rule neu triggert?

Verfasst: 8. Aug 2023 16:20
von Homer-S
Danke für die Hilfe mit dem Timer, ich werde es versuchen :)
Deine Logzeile ist ein Verbrechen. :)
Um ganz Ehrlich zu sein, hab ich das mit dem loggen und Karaf nicht ganz verstanden und es klingt für mich eher komplizierter als meine Variante :)
Mein loggen ist halt sofort im log erkennbar und über var boolean log = false schnell ausgeschaltet

Aber ich werde mir das mal anschauen

Re: Timer in Rule wenn Rule neu triggert?

Verfasst: 8. Aug 2023 16:25
von Homer-S
Zu dem Timer hab ich noch eine (Verständnis-)Frage.

Wenn die Temperatur wieder niedriger als die Schwelle ist, schalte ich den Timer ein, also tSperre = null;
Aber ich hätte gerne, dass wenn der Timer aus ist, er einen kurzen check macht ob die Bedingung mit der Temperatur immer noch erfüllt ist und DANN soll er was tun.

Das bekomm ich aber irgendwie im Kopf mit deiner Lösung nicht zusammen.
Auf was wechselt der tSperre nach dem Timerablauf?

Re: Timer in Rule wenn Rule neu triggert?

Verfasst: 9. Aug 2023 09:27
von udo1toni
"Meine Variante" verhindert die Ausführung der Rule für jeweils fünf Minuten. Im Anschluss ist die Rule wieder scharf und wird also beim nächsten Update unmittelbar wieder reagieren.
Man könnte auch anders an das Problem heran gehen, nämlich "wenn der Wert mindestens 5 Minuten überschritten (oder unterschritten) wurde".
Dann sähe die Rule anders aus. So im groben:

Code: Alles auswählen

var Timer  tReact = null
var Number nReact = 0

rule ...
when
    Item MeinMesswert changed
then
    val Number nDownLimit = 20
    val Number nUpLimit   = 21
    if(!newState instanceof Number)
        return;
    val Number nIst = (newState as Number).floatValue
    if(nIst >= nDownLimit && nIst <= nUpLimit) {
        // nReact = 0
        // tReact?.cancel
        return;
    }
    if(nIst < nDownLimit) {
        if(nReact > -1) {
            nReact = -1
            tReact?.cancel
            tReact = createTimer(now.plusMinutes(5),[|
                // das was bei Unterschreiten passieren soll
            ])
        }
    } else {
        if(nReact < 1) {
            nReact = 1
            tReact?.cancel
            tReact = createTimer(now.plusMinutes(5),[|
                // das was bei Überschreiten passieren soll
            ])
        }
    }
end
Die Rule prüft, ob ein gültiger Messwert vorliegt. Ist das nicht der Fall, bricht sie die Ausführung ab.
Anschließend prüft sie anhand der gegebenen Grenzwerte nDownLimit und nUpLimit, ob Handlungsbedarf besteht (es ist grundsätzlich eine gute Idee immer eine Hysterese vorzusehen).
Nun gibt es noch zwei Möglichkeiten: Entweder der Messwert ist unterhalb nDownLimit oder eben über nUpLimit. In beiden Fällen ist im Zweifel ein Timer zu starten, allerdings nur, falls er noch nicht läuft. Am sinnvollsten macht man das an einer extra Variablen fest (hier nReact). Diese behält also so lange ihren Zustand, wie der Hysteresebereich nur in einer Richtung verlassen wurde. Fünf Minuten nachdem die Variable ihren Wert angenommen hat, werden die Befehle ausgeführt. Eine Prüfung ist nicht erforderlich, denn wenn der Hysteresebereich in die andere Richtung verlassen wird, wird der Timer ja direkt entfernt und ein neuer angelegt.
Wahlweise kann das auch geschehen, wenn der Hysteresbereich erreicht wird, dann müsste man halt die Kommentarzeichen entfernen.
Aber ich sehe nicht, warum man eine Reaktion extra verzögern sollte. Gewöhnlich möchte man unmittelbare Reaktionen der Steuerung und lediglich verhindern, dass bei kurzzeitigen Wechseln unmittelbar wieder umgesteuert wird.

Was das Logging betrifft: Das ist wirklich einfach. Steht auch alles in der Dokumentation.
Man sollte nicht gegen das System programmieren, sondern mit dem System.

Re: Timer in Rule wenn Rule neu triggert?

Verfasst: 18. Sep 2023 13:21
von Homer-S
Hallo Udo,
endlich kam ich dazu und die zweite Variante hat es jetzt gebracht.
Danke!