Seite 1 von 2

Rule nur einmal am Tag ausführen lassen

Verfasst: 12. Mai 2022 11:50
von Grandlhuber
Hallo Leute,

ich möchte eine Regel nur 1x am Tag ausführen lassen. Habe hier im Forum einen Code aufgeschnappt und ihn an meine Bedürfnisse angepasst aber irgendwie führt er die Regel trotzdem immer wieder aus. Wo ist der Fehler?

Code: Alles auswählen

var Number nCount = 0

        If(nCount > 0)                     
            return;
        HierkommtdasItem1rein.sendCommand(100)       
        HierkommtdasItem2rein.sendCommand(100)       
          nCount = nCount + 1
nCount wird doch bei jedem Aufruf der Rule wieder auf 0 zurück gesetzt (var Number nCount = 0) oder nicht?
Lasse ich das "=0" weg, funktioniert die Rule ebenfalls nicht so wie sie sollte.

Habe ich hier einen Denkfehler?

Nach Ablauf des Tages würde ich dann nochmals eine Regel erstellen, in der dann nCount wieder auf 0 zurück gesetzt wird. Einfach ausgedrückt:

Code: Alles auswählen

nCount = 0
oder

Code: Alles auswählen

nCount = nCount -1 

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 12. Mai 2022 12:11
von int5749
Hi,

zum einen würde die komplette Rule helfen, denn hier sehe ich keinen Trigger

Code: Alles auswählen

rule test
when
	trigger
then
	mache etwas
end
Soll die Regel immer zur gleich Zeit ausgeführt werden?? Dann bietet sich cron als trigger an, dann kannst Du auf count verzichten.

Aber dies ist nur raten in der Glaskugel, da (mir) noch nicht klar ist, was Du erreichen möchtest.

VG

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 12. Mai 2022 13:43
von Grandlhuber
Hallo,

ich will z.b. die Rolladen vorzeitig schließen falls mal nicht so ein schöner Tag ist und es eher dunkel wird als die Zeit, die das Astrobinding ausgiebt (extra rule). Hier der Code:

Code: Alles auswählen

configuration: {}
triggers:
  - id: "4"
    configuration:
      cronExpression: 0 * 16-18 * * ? *
    type: timer.GenericCronTrigger
conditions:
  - inputs: {}
    id: "1"
    configuration:
      itemName: SonnensensorsudTerrasse_Luminance
      state: "150"
      operator: <=
    type: core.ItemStateCondition
  - inputs: {}
    id: "6"
    configuration:
      itemName: SonnensensorsudTerrasse_Luminance
      state: "65"
      operator: ">="
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: |-
        BadDG_Control.sendCommand(100)

        BadEG_Control.sendCommand(100)

        Esszimmer_Rollo_Control.sendCommand(100)

        Gastezimmer_Control.sendCommand(100)

        KucheGarten_Control.sendCommand(100)

        Thread::sleep(1000)

        KucheStrasse_Control.sendCommand(100)

        KinderzimmerLea_Control.sendCommand(100)

        WohnzimmerFest_Control.sendCommand(100)

        // WohnzimmerTerrasse.sendCommand(100)

        createTimer(now.plusMinutes(1), [|              

        SchlafzimmerAnkleide_Control.sendCommand(100)

        SchlafzimmerTur_Control.sendCommand(100)

        ])             
    type: script.ScriptAction
Das Rule funktioniert so schon gut, jedoch wird es halt wenn man pech hat mehrfach abgefahren bis die Lux unter 65 gehen. Da möchte ich halt, dass die dsl-Regel nur einmal durchgeht und dann nicht mehr .

Ich hoffe, ich habs verständlich ausgedrückt? :?

Gruß Oli

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 12. Mai 2022 13:48
von peter-pan
Das ist so eine Sache mit den Countern. Zum einen hat @int5749 Recht, wenn er sagt, dass die ganze Regel gepostet werden sollte, damit man sich ein Bild vom Code/Ablauf machen kann.

Ich schau mal in die Glaskugel und sage einfach, dass die Variable (nCount) nach der Script-Anweisung (then) ausgeführt wird. Und da haben wir das Problem, dass der Counter immer wieder auf 0 gesetzt wird. Deshalb wird die Regel auch immer ausgeführt.

Diese Variable muss deshalb global vor der ersten Regel in deinem Rule-File angelegt werden. Um bei dem Beispiel von Jörg zu bleiben, müsste das dann etwa so aussehen:

Code: Alles auswählen

val nCount = 0   //Globale Variable

rule "test"
when
	trigger
then
        If(nCount > 0)                     
            return;
        mache etwas 
	nCount = nCount + 1
end
...und dann sollte die Regel auch etwas machen (aber nur einmal) ;) . Wie die dann wieder zurück gesetzt wird ist deshalb auch die nächste Frage, denn viele Wege führen nach Rom

Hier noch ein Beispiel aus meinem SetUp:

Code: Alles auswählen

var countNTP = 0

rule "Zeit mit Zeitzone"
  when
//    Item Dummy_4x changed to ON 
    Item Current_DateTime received update
  then
    var vTimeZoneString = "MEZ"
    val vTimeZoneNumber = Current_DateTime.state.format("%1$tZ")
    if (vTimeZoneNumber == "+02:00") vTimeZoneString = "MESZ"
    CurrentTime2.postUpdate(Current_DateTime.state.format("%1$tA, %1$td.%1$tm %1$tH:%1$tM ") + vTimeZoneString)
    TimeType.postUpdate(vTimeZoneString)
    if (countNTP < 239) {
       countNTP = countNTP +1 
    }
    else {
        logInfo("ntp","Zeit hat sich geändert {} schon wieder 4 Stunden vorbei", countNTP)
        countNTP = 0
    }
end
Dabei geht es darum heraus zu finden, ob wir Sommer- oder Normalzeit haben und mir dann ein "gehübschtes" Datumsformat in ein Item hineinschreibt und ebenfalls die aktuelle Zeitzone (MEZ/MSEZ) abspeichert. (..das nur am Rande)
Ich will aber wissen, ob die Rule auch wirklich läuft, deshalb habe ich einen Counter eingebaut, der alle 4 Stunden meldet, dass die Rule noch funktioniert.

Log:

Code: Alles auswählen

2022-05-12 02:14:44.335 [INFO ] [org.openhab.core.model.script.ntp   ] - Zeit hat sich geändert 239 schon wieder 4 Stunden vorbei
2022-05-12 06:14:45.343 [INFO ] [org.openhab.core.model.script.ntp   ] - Zeit hat sich geändert 239 schon wieder 4 Stunden vorbei
2022-05-12 10:14:46.368 [INFO ] [org.openhab.core.model.script.ntp   ] - Zeit hat sich geändert 239 schon wieder 4 Stunden vorbei
Wie du siehst läuft die Rule präzise. Aber wie gesagt, das ist abhängig vom Problem.

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 12. Mai 2022 16:00
von peter-pan
Jetzt hab ich nochmal an deinem Problem herum experimentiert und das ist dabei heraus gekommen:

Code: Alles auswählen

var vCount = 0

rule "Test Grantlhuber"
   when
      Item Dummy_4 changed to ON or       // Schalter für frühere Auslösung
      Time cron "0 * 16-18 * * ? *" or    // normale Auschaltzeit 
      Time cron "0 59 23 ? * * *"         // Zeit wenn Counter gelöscht wird
//            Time cron "0 35 15 ? * * *"         // Zeit wenn Counter gelöscht wird -- Test

then
      if ( Moon_illumination.state < 150 && Moon_illumination.state >= 65 && vCount == 0) {
//        BadDG_Control.sendCommand(100)
//        BadEG_Control.sendCommand(100)
//        Esszimmer_Rollo_Control.sendCommand(100)
//        Gastezimmer_Control.sendCommand(100)
//        KucheGarten_Control.sendCommand(100)
        logInfo("Grantlhuber 1","Die erste Rollo-Gruppe wird herunter gefahren")

//        Thread::sleep(1000)   den würde ich ebenfalls mit einem Timer abarbeiten

        createTimer(now.plusMinutes(1), [|              
//        KucheStrasse_Control.sendCommand(100)
//        KinderzimmerLea_Control.sendCommand(100)
//        WohnzimmerFest_Control.sendCommand(100)
        logInfo("Grantlhuber 2","Die zweite Rollo-Gruppe wird herunter gefahren")
        ])
        createTimer(now.plusMinutes(2), [|              
//        SchlafzimmerAnkleide_Control.sendCommand(100)
//        SchlafzimmerTur_Control.sendCommand(100)
        logInfo("Grantlhuber 3","Die dritte Rollo-Gruppe wird herunter gefahren")
        ]) 
       vCount ++   
        logInfo("Grantlhuber 4","vCount ist: {}", vCount)
      }          
    else  {
      logInfo("Grantlhuber 5","vCount ist: {} Rule ist schon gelaufen", vCount)
     }
   val vHourMinute = Integer::parseInt(now.toLocalTime().getHour.toString + now.toLocalTime().getMinute.toString )           // Stunde + Minute für Vergleich zum Rücksetzen des Counters
   if (vHourMinute >= 2359  && vCount > 0) {
//    if (vHourMinute >= 1535  && vCount > 0) {  --Test
     vCount = 0
     logInfo("Grantlhuber 6","vCount wurde zurück gesetzt: {}", vCount)

   }
end
Deine Items habe ich soweit auskommentiert und für die Illumination hab' ich einfach das Moon-Item aus dem Astrobinding genommen, da ich für die Sonne keins gefunden habe. Für das manuelle Triggern habe ich noch ein zusätzliches Item genommen.
Das mit dem "Sleep" hab ich mal in einen Timer umfunktioniert, da ich irgendwo gelesen habe, dass das nicht so gut sein soll (bei OH3 nicht mehr ganz so wild).
Ich frage mich auch, ob die Regel wirklich jede Minute zwischen 16 - 18 Uhr laufen muss ?! Du kannst dir ja auch noch überlegen, ob du die Variable durch ein Item ersetzen willst. Das ist evtl. sicherer bei Stromausfall, etc.

Der Test war erfolgreich:

Code: Alles auswählen

2022-05-12 15:30:03.218 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'ntp.rules'

-------Rule wurde ausgelöst > manuell oder mit Cron Trigger -- erstes mal
2022-05-12 15:30:29.172 [INFO ] [nhab.core.model.script.Grantlhuber 1] - Die erste Rollo-Gruppe wird herunter gefahren
2022-05-12 15:30:29.180 [INFO ] [nhab.core.model.script.Grantlhuber 4] - vCount ist: 1
2022-05-12 15:31:29.175 [INFO ] [nhab.core.model.script.Grantlhuber 2] - Die zweite Rollo-Gruppe wird herunter gefahren
2022-05-12 15:32:29.177 [INFO ] [nhab.core.model.script.Grantlhuber 3] - Die dritte Rollo-Gruppe wird herunter gefahren

-------Rule wurde ausgelöst > manuell oder mit Cron Trigger -- zweites mal
2022-05-12 15:34:12.273 [INFO ] [enhab.core.model.script.Grantlhuber ] - vCount ist: 1 Rule ist schon gelaufen

-------Rule wurde ausgelöst > mit Cron Trigger -- zum Zurücksetzen
2022-05-12 15:35:00.160 [INFO ] [enhab.core.model.script.Grantlhuber ] - vCount ist: 1 Rule ist schon gelaufen
2022-05-12 15:35:00.168 [INFO ] [enhab.core.model.script.Grantlhuber ] - vCount wurde zurück gesetzt: 0

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 12. Mai 2022 23:56
von Grandlhuber
Oh Gott hast du dir eine Mühe wegen mir gemacht :o !

Hatte bis jetzt leider keine Zeit das mal auszutesten. Ich übertrage das morgen mal in mein System und schaue was passiert.

Zu deiner Anmerkung mit der minütlichen Überprüfung: Da könnte man alle 2-3 Minuten überprüfen, reicht auch denke ich. Oder alle 5 Minuten...

Wegen dem zeitverzögerten runterfahren der Rollos: Das musste ich so machen weil anscheinend nicht alle auf einmal angepingt werden können vom Binding. Habe Somfy Tahoma als System. Wenn ich alle auf einmal runter lasse geht mindestens einer nicht runter. Habe da auch mal was bezüglich einer Limitation beim Somfy Binding gelesen. :(

Melde mich wieder!

Gruß Oli

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 13. Mai 2022 09:23
von udo1toni
Also, ganz grundsätzlich, Rules, die in einem bestimmten Zeitraum minütlich triggern, lassen sich meist auf andere Art besser triggern.

Du möchtest beispielsweise aufgrund der Helligkeit die Rollläden fahren. Du musst also in der Rule nicht nur prüfen, ob die rule schon gelaufen ist, sondern auch, wie hell es ist. Eine weitere Bedingung ist der Zeitraum, der ist bei Dir über den Trigger abgedeckt. Das eigentliche Ereignis ist aber kein zeitliches, sondern das Unterschreiten eines Messwertes.

Entsprechend sollte die Rule eigentlich auf die Messwertänderung triggern.
Was das Stoppen der Rule nach vollendeter Arbeit betrifft, so gibt es dazu mindestens noch eine Frage, nämlich: gibt es einen Rollladen, der sicher offen ist (also nie von Hand gesteuert wird) und der von der Rule geschlossen wird? Denn dann sollte es ausreichen, den Status dieses Rollladens abzufragen. Ansonsten musst Du Dir merken, ob die Rule bereits ausgelöst wurde. Wenn Du Rules (egal welcher Bauart) in der UI anlegst, kannst Du keine globalen Variablen nutzen, allenfalls kannst Du Variablen definieren, deren Zustand nach dem Durchlauf der Rule erhalten bleiben - aber das hilft Dir hier nicht, denn Du musst die Variable ja auch wieder zurücksetzen, sie muss also zwingend "echt" global sein.
Der Ausweg: Du nutzt ein Item, welches Du nur zu diesem Zweck anlegst. Das Item wird nicht mit einem Channel verlinkt, es bleibt also ungebunden. Da Du die Rule nur maximal einmal am Tag ausführen lassen willst, reicht sogar ein Switch Item aus.

Als klassische DSL Rule sähe es so aus:

Code: Alles auswählen

rule "Rolllaeden zu bei Dunkelheit"
when
    Item SonnensensorsudTerrasse_Luminance changed  // ein Number Item, welches die Helligkeit enthält
then
    if(dunkelAktiv.state == ON) // Rule bereits aktiviert?
        return;
    if(now.getHour < 16)    // zu früh?
        return;
    if(now.getHour > 18)    // zu spät?
        return;
    if(!(newState instanceof Number)) // liefert Sensor keinen Messwert?
        return;
    if((newState as Number).intValue > 165) // oberer Grenzwert noch nicht unterschritten?
        return;
    if((newState as Number).intValue < 65) // unterer Grenzwert bereits unterschritten?
        return;
   
   // alle Bedingungen sind erfüllt!
   dunkelAktiv.postUpdate(ON)
   // hier Rollläden zu
end
Tatsächlich kann es durchaus sein, dass diese Rule häufiger ausgelöst wird (nämlich sobald sich die Helligkeit ändert), dennoch ist diese Variante vorzuziehen.
Nacheinander werden alle Abbruchbedingungen geprüft, wenn keine der Bedingungen zutrifft wird der Merker gesetzt und der Rest des Codes ausgeführt.
In den UI Rules gibt es natürlich den but-only-if-Bereich. All diese Abbruchbedingungen können auch dort hinterlegt werden. Technisch dürfte es zu der klassischen DSL Rule hier keinen Unterschied geben.

Das Changed Ereignis belegt keinen Slot im Scheduler (im Gegensatz zum Time cron Trigger), wobei beliebig viele Slots zur Verfügung stehen, das sollte also keine Rolle spielen, aber: Das Changed Ereignis tritt ohnehin auf, das Time cron Ereignis tritt zusätzlich auf.
Der Zustand des Switch Items muss so oder so ausgelesen werden, ebenso die anderen Zustände. Der Messwert kommt aber als implizite Variable in die Rule hinein (newState), hier wird durch die Wahl des Triggers keine zusätzliche Abfrage der Registry gebraucht.
Now.getHour() steht "einfach" zur Verfügung, also ohne dass dazu weiterer Programmcode gebraucht wird (außer die Methode selbst natürlich).

Im Zweifel ist die Begrenzung des Messwertes nach unten (<65) unnötig, da die Rule vermutlich ohnehin bereits ausgelöst haben wird.

Ein weiterer Gedanke: Du steuerst die Rollläden ja über das Astrobinding. Es wäre also naheliegend, sich die Grenzzeit ebenfalls von Astro zu holen.
Nehemn wir an, Du löst das Schließen der Rollläden mit dem RangeEvent civilDusk triggered START aus,
dann musst Du in der Rule lediglich prüfen, ob die aktuelle Zeit z.B. nicht mehr als 45 Minuten vor DateTime civilDuskStart liegt (und nicht mehr als 1 Minute nach civilDuskStart). Damit funktioniert die Rule dann jahreszeitunabhängig und die Läden werden bis zu 45 Minuten vor dem eigentlichen Zeitpunkt geschlossen. Leider ist das Format des Datetime Items ein anderes als JavaTime, so dass die Formulierung der Bedingungen hier etwas aufwändiger ist, aber es wäre vermutlich "schicker" ;)

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 13. Mai 2022 12:29
von peter-pan
Wie du siehst, gibt es mehrere Ansatzmöglichkeiten sich dem Problem zu nähern. Meine ist halt etwas einfacher gestrickt. Der Ansatz von Udo ist da schon professioneller, berücksichtigt auch diverse Fehlermöglichkeiten, bezieht das Astrobinding mit ein und kommt gänzlich ohne Cron-Trigger aus.
Allerdings habe ich bereits das Rücksetzen des Counters berücksichtigt.

Hier noch ein Bespiel für einen Cron-Trigger der alle 10 Minuten zwischen 16 - 18:59 Uhr läuft:

Code: Alles auswählen

      Time cron "0 0/10 16-18 ? * * *" or    // normale Auschaltzeit 0 0/10 16-18 ? * * *
Die Logzeilen kannst du natürlich auskommentieren, vor allem die in der "ELSE-Bedingung" die kommt sonst jede Minute (oder 5-10) hoch. Und das mit dem zusätzlichen Item, statt der globalen Variable, würde ich mir auch noch mal überlegen.

Gruss - Peter

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 13. Mai 2022 23:44
von Grandlhuber
Hi ihr zwei!

Hui, jetzt bin ich platt. Mit soviel Feedback habe ich nicht gerechnet. Hatte heute leider wieder keine Zeit zu testen. Eigentlich wollte ich. Aber: wir fahren morgen nach Frankreich (Kurzurlaub) und das muss gepackt werden. Mit einem Kind wird die Sache dann nicht unbedingt leichter :?

Ich habe eine weitere rule laufen, die exakt das gleiche macht wie diese, nur dass diese dann die Rolladen schließt wenn ( wue udo1toni vermutet) astro sagt: es wird dunkel. Ihr werdet jetzt bestimmt denken: geht professioneller :D !

Ihr könntet dann bestimmt so lösen. Ich auch. Aber nur in VBA. Dav könnte ich auch sowas realisieren. Aber hier: Java und diese ganzen dsl Rules sind von der Logik her ähnlich wie VBA, aber es wird komplett anders geschrieben. Da bin dann raus... Ich verstehe eure Gedankengänge in eurem Code und das war es auch schon :(
Frage, nämlich: gibt es einen Rollladen, der sicher offen ist (also nie von Hand gesteuert wird) und der von der Rule geschlossen wird? Denn dann sollte es ausreichen, den Status dieses Rollladens abzufragen.
Ja, Wohnzimmer Fest zb. wird nach dem auslösen nicht mehr verändert. Den könnte man triggern und dann die rule abbrechen.

Ich muss mir das am Sonntag oder Montag mal genauer ansehen. Per VPN kann ich ja prima auf zu Hause zugreifen und von Urlaub aus probieren und testen :geek:


Nehmt es mir also bitte nicht übel falls ich nicht gleich reagiere :? :|

So, ich gehe jetzt ins Bett. In 4 Stunden gehts los 8-)

Bis denne,Oli

Re: Rule nur einmal am Tag ausführen lassen

Verfasst: 13. Mai 2022 23:50
von peter-pan
...schönen Urlaub und viel Spass und Erholung ;) 8-)
...et bon voyage