Ankündigung

Einklappen
Keine Ankündigung bisher.

Probleme mit timer in openHAB3

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    Probleme mit timer in openHAB3

    Hallo!
    Ich hatte mir erst vor kurzem die Bewässerungssteuerung in openHAB2 gebastelt und konnte etwas für mich brauchbares zusammenbauen:

    Code:
    rule "Tropfer Z1"
    when
    Item BW_AutoTR1_Switch received command ON
    then
    var DateTime endTimeTR1 /// DateTime cannot be resolved to a type.(org.eclipse.xtext.diagnostics.Diagnostic.Lin king)
    val LZ_TR1 = (TR_Auto_LZ1.state as DecimalType).intValue
    
    if (LZ_TR1 > 0) {
    
    endTimeTR1 = now.plusMinutes(LZ_TR1)]/// wenn DateTime auf DateTimeType geändert kommt hier: Type mismatch: cannot convert from ZonedDateTime to DateTimeType(org.eclipse.xtext.xbase.validation.Is sueCodes.incompatible_types)
    sendCommand(BW_TR1, ON)
    logInfo("Garten", "Tropfer Zone 1 Bewässerung gestartet")
    
    BW_TR1_Timer = createTimer(endTimeTR1) [|
    sendCommand(BW_TR1, OFF)
    BW_TR1_Timer = null
    BW_AutoTR1_Switch.postUpdate(OFF)
    logInfo("Garten", "Tropfer Zone 1 Bewässerung abgeschlossen")
    ]
    
    }
    end
    Leider (?) wurde bei openHAB mit Zeit "Sachen" ja einiges neu, wird ja auch ausführlich im openHAB Forum abgearbeitet: https://community.openhab.org/t/date...hab-3-x/107197

    Ich muss ehrlich gestehen, dass mir das langsam aber sicher zu kompliziert wird, ich weiß schon garnicht, wann (ob?) ich DateTimeType, Java Time oder Epoch verwenden soll...

    Habe die var "endTimeTR1" mal von DateTime auf DateTimeType geändert, aber dann kommt halt der nächste Fehler bei der Erstellung des Timers, habe jeweils die Fehlremeldung als Kommentar in den Zeilen eingefügt.

    Könnte mir jmd. vielleicht einen Schubs in die richtige Richtung geben?

    Schönen Abend inzwischen!

    #2
    Du musst Dir darum eigentlich gar keine Sorgen machen. Du könntest im vorliegenden Fall einfach nicht angeben, welche Art Konstante Du da definierst. Aber es gibt schon von vornherein gar keinen Grund, überhaupt eine Konstante zu verwenden...
    Code:
    var Timer BW_TR1_Timer = null
    
    rule "Tropfer Z1"
    when
        Item BW_AutoTR1_Switch received command ON
    then
        val LZ_TR1 = if(TR_Auto_LZ1.state instanceof Number) (TR_Auto_LZ1.state as Number).intValue else 0
        if(LZ_TR1 < 1)
            return;
        BW_TR1.sendCommand(ON)
        logInfo("Garten", "Tropfer Zone 1 Bewässerung gestartet.")
        BW_TR1_Timer = createTimer(now.plusMinutes(LZ_TR1), [|
            BW_TR1.sendCommand(OFF)
            BW_TR1_Timer = null
            BW_AutoTR1_Switch.postUpdate(OFF)
            logInfo("Garten", "Tropfer Zone 1 Bewässerung abgeschlossen.")
        ])
    end
    Beachte bitte, dass die Definition der Variablen BW_TR1_Timer als globale Variable zum Code dazu gehört. Die Rule funktioniert auch ohne die Variable, aber vielleicht willst Du den Zeiger ja an anderer Stelle nutzen.
    Weiter ist es besser, die Methode Item.sendCommand(Befehl) zu nutzen, als die Action sendCommand(Item,Befehl) (wobei Item und Befehl als Strings behandelt werden)
    Da die Konstante LZ_TR1 aus einem Number Item geladen wird, sollte die Rule prüfen, ob das Item überhaupt eine gültige Zahl enthält. Number ist die übergeordnete Zahlengruppe, sie ist besser geeignet als DecimalType.
    Die Abfrage auf Werte größer 0 habe ich im Beispiel umgekehrt, so dass der Code übersichtlicher wird. Das ist aber sicher Geschmackssache.
    Das Lambda ist im Beispiel auch als Parameter der Funktion createTimer() angegeben, das sollte helfen, den Code lesbarer zu gestalten - wie auch die Einrückungen... Die Forensoftware löscht führende Leerzeichen nur, solange die Darstellung auf wysiwyg steht (Schaltfläche ganz links "Quelle")
    Zuletzt geändert von udo1toni; 07.09.2021, 21:39.

    Kommentar


      #3
      Hi! Wiedermal vorzügliche und schnelle Hilfe von dir, vielen Dank! Bin mit der Umsetzung leider nicht ganz so schnell 😀
      Natürlich funktioniert der Code, habe aber trotzdem noch ein paar Verständnisfragen, wenn das ok ist!

      Ich habe den Code Zweilen-weise übertragen, also der Reihe nach

      Code:
      val LZ_TR1 = if(TR_Auto_LZ1.state instanceof Number) (TR_Auto_LZ1.state as Number).intValue else 0
      heißt das übersetzt "wenn TR_Auto_LZ1 eine Zahl ist, dann gebe der Variablen LZ_TR1 den Wert der Zahl, ansonsten 0?

      Code:
      BW_TR1_Timer = createTimer(now.plusMinutes(LZ_TR1), [|
      Ich habe hier nur den Part "now.plusMinutes(LZ_TR1)" (den hinterstehenden "," vergessen") und hatte entsprechend Fehlermeldungen in der Zeile.
      In meiner ursprünglichen Regel schließe ich den "createTimer" sofort mit einer Klammer, du hingegen, stellst das Komma dahinter und schließt erst ganz am Ende der Regel mit der Klammer. Ist das relevant?

      Zitat von udo1toni Beitrag anzeigen
      Das Lambda ist im Beispiel auch als Parameter der Funktion createTimer() angegeben, das sollte helfen, den Code lesbarer zu gestalten
      Ich verstehe ehrlich gesagt nur Bahnhof, wahrscheinlich weil ich mit dem Betriff "Lambda" nichts anfangen kann.

      Zitat von udo1toni Beitrag anzeigen
      Die Forensoftware löscht führende Leerzeichen nur, solange die Darstellung auf wysiwyg steht (Schaltfläche ganz links "Quelle")
      Auch hier beschreibst du glaub ich ein Problem, das ich schon gar nicht sehe, auch bei mehrfachem Blick 😵

      Den Rest hab ich glaub ich verstanden, und wieder was dazulernt! Nochmal: vielen lieben Dank!

      Kommentar


        #4


        Deine erste Frage hast Du schon selbst korrekt übersetzt. Diese Form, if(a) b else c zu verwenden, heißt ternärer Operator. In Abhängigkeit von a wird b oder c übergeben.

        Lambda ist ein Codeblock zwischen eckigen Klammern, also alles von [ bis ]. Man kann diesen Teil außerhalb des Funktionsaufrufs schreiben oder als Teil der Funktion. Dann ist das Lambda halt der zweite Parameter der Funktion. Es gibt Dialekte, die nur eine von beiden Schreibweisen erlauben. Die Rules DSL erlaubt beides, allerdings wird nur aus der Schreibweise als Teil der Funktion sofort klar, dass das Lambda Teil der Funktion ist.

        Forensoftware... Es gibt hier den Editor, in dem Du die Postings schreibst. Du kannst mit dem Knopf mit der Raute bequem Code als Code markieren (hast Du ja oben gemacht) Allerdings löscht die Forensoftware alle führenden Leerzeichen, womit die Einrückungen verloren gehen (auch Tabs sind Leerzeichen). Gewöhnlich schreibt man Code mit Einrückungen (wie in meinem Beispiel oben) um die Lesbarkeit zu erhöhen, vor allem, um Codeblöcke zu kennzeichnen, die als Block ausgeführt werden. Die Schaltfläsche mit den sptzen Klammern <> schaltet die Anzeige um, so dass alle Tags so sehen sind. In diesem Modus werden keine Leerzeichen entfernt, so dass die Einrückungen erhalten bleiben.

        Kann natürlich auch sein, dass Du keine Einrückungen verwendet hast

        Kommentar


          #5
          Zitat von udo1toni Beitrag anzeigen
          Deine erste Frage hast Du schon selbst korrekt übersetzt. Diese Form, if(a) b else c zu verwenden, heißt ternärer Operator. In Abhängigkeit von a wird b oder c übergeben.
          Perfekt, danke. Das wird mir sicherlich/hoffentlich in Zukunft helfen!

          Zitat von udo1toni Beitrag anzeigen
          Lambda ist ein Codeblock zwischen eckigen Klammern, also alles von [ bis ]. Man kann diesen Teil außerhalb des Funktionsaufrufs schreiben oder als Teil der Funktion. Dann ist das Lambda halt der zweite Parameter der Funktion. Es gibt Dialekte, die nur eine von beiden Schreibweisen erlauben. Die Rules DSL erlaubt beides, allerdings wird nur aus der Schreibweise als Teil der Funktion sofort klar, dass das Lambda Teil der Funktion ist.
          mhm, mhm, mhm... 👽
          Ist das auch Antwort auf diese meine Frage :
          Zitat von narf Beitrag anzeigen
          Ich habe hier nur den Part "now.plusMinutes(LZ_TR1)" (den hinterstehenden "," vergessen") und hatte entsprechend Fehlermeldungen in der Zeile.
          oder?

          Zitat von udo1toni Beitrag anzeigen
          Forensoftware
          Ah, also ein Problem das ich verursacht habe
          Habe tatsächlich Einrückungen, mir war beim Einfügen gar nicht aufgefallen, dass diese verschwunden gegangen sind. Werde das nächste mal daran denken!

          Gruß

          Kommentar


            #6
            Zitat von narf Beitrag anzeigen
            mhm, mhm, mhm... 👽
            Ist das auch Antwort auf diese meine Frage :
            Ähh... ja. Schreibweise ist entweder
            Code:
            createTimer(time) [|lambda]
            oder
            Code:
            createTimer(time,[|lambda])
            wobei die Pipe (das ist der senkrechte Strich) in diesem Fall optional ist, weil das Lambda keine Parameter mitführt. Anders wäre es, wenn es um eine Schleife ginge.
            Code:
            GroupItem.members.forEach[i,j| ]
            hier gibt es eine oder wahlweise zwei Variablen, i enthält das jeweils aktuelel Element, j ist der null-basierte Zähler des Durchlaufs, also im ersten Schritt 0, im zweiten Schritt 1 usw., da zumindest der Platzhalter für das Element nicht optional ist, muss der Strich hier gesetzt werden, selbst wenn man in der Schleife nicht auf diesen Platzhalter zugreifen möchte (ist nicht sinnvoll, aber es wäre ja denkbar).

            Kommentar


              #7
              Jetzt bin ich schon beim nächsten Schritt... In meinem alten (oh2) setup hab ich für meine 5 verschiedenen Ventile ein eigenes Rule erstellt, aber das müsste doch sicherlich mit einer einzigen Rule klappen. Habe die ganzen "automatik start" switches in eine Gruppe gepackt und lass das über Member of group triggern.
              Konnte die items, welche ich benötige ganz gut aus dem triggeredItem ableiten, bloß wie ich in der Rule den richtigen Timer verwende ist mir schleiferhaft...
              Hier ist für den Timer noch fix der Wert BW_TR1_Timer hinterlegt:
              Code:
              import org.openhab.core.model.script.ScriptServiceUtil
              
              var Timer BW_TR1_Timer = null
              
              rule "Bewässerung starten"
              when
                  Member of gBW_Switches received command ON
              then
                  var String trigger = triggeringItem.name.toString.replace("_Auto", "")
                  val valve = ScriptServiceUtil.getItemRegistry.getItem(trigger) as GenericItem
                  val LZ = ScriptServiceUtil.getItemRegistry.getItem(trigger + "_LZ") as GenericItem
                  val runtime = if(LZ.state instanceof Number) (LZ.state as Number).intValue else 0
                  if(runtime < 1)
                      return;
                  valve.sendCommand(ON)
                  logInfo("Garten", valve.label + " Bewässerung gestartet")
                  BW_TR1_Timer = createTimer(now.plusMinutes(runtime), [|
                     valve.sendCommand(OFF)
                     BW_TR1_Timer = null
                     triggeringItem.postUpdate(OFF)
                     logInfo("Garten", valve.label + " Bewässerung abgeschlossen")
                     ])
              end
              Wie könnte ich den Timer ebenso variabel vom "trigger" ableiten?
              Timer müsste trigger + "_Timer" sein. Versteh allerdings nicht, wie ich das richtig definieren kann...

              Gruß

              Kommentar


                #8
                Ja, das geht definitiv, ist aber nicht ganz so einfach. Du musst für die Timer in einer HashMap speichern. Ein guter Thread hierzu wäre dieser https://community.openhab.org/t/desi...or-timer/14954 im englischen Forum.
                tl:dr:
                Code:
                import java.util.Map
                val Map<String, Timer> timers = newHashMap
                
                rule "Bewässerung starten"
                when
                    Member of gBW_Switches received command ON
                then
                    var String trigger = triggeringItem.name.toString.replace("_Auto", "")
                    val valve = ScriptServiceUtil.getItemRegistry.getItem(trigger) as GenericItem
                    val LZ = ScriptServiceUtil.getItemRegistry.getItem(trigger + "_LZ") as GenericItem
                    val runtime = if(LZ.state instanceof Number) (LZ.state as Number).intValue else 0
                    if(runtime < 1)
                        return;
                    valve.sendCommand(ON)
                    logInfo("Garten", valve.label + " Bewässerung gestartet")
                    timers.put(trigger, createTimer(now.plusMinutes(runtime), [|
                       valve.sendCommand(OFF)
                       timers.put(trigger, null)
                       triggeringItem.postUpdate(OFF)
                       logInfo("Garten", valve.label + " Bewässerung abgeschlossen")
                    ]))
                end
                Wobei ich das weder getestet habe, noch sicher bin, dass dieser Code so funktioniert.
                Zuletzt geändert von udo1toni; 15.09.2021, 04:48.

                Kommentar


                  #9
                  Die Definition der einzelnen Timer fällt dann weg?
                  Also in obigem Beispiel die Zeile
                  Code:
                  var Timer BW_TR1_Timer = null
                  edit: also im englischen Thread fällt die genannte Definition weg. Werds später versuchen.

                  Danke inzwischen!
                  Zuletzt geändert von narf; 15.09.2021, 08:40.

                  Kommentar


                    #10
                    OK, hab mich gleich rangemacht!

                    In der Zeile "Timers.put(trigger, null)" bekomme ich folgende Fehlermeldung: Cannot refer to the non-final variable timer inside a lambda expression(org.eclipse.xtext.xbase.validation.Issu eCodes.invalid_mutable_variable_access)
                    und habe mich beim Abbrechen etwas schwer getan. Habe mich dann etwas näher an den Vorschlag auf dem englischen openHAB Forum gehalten und - soweit ich das überblicke - läuft es mit diesem Code:

                    Code:
                    rule "Bewässerung starten"
                    when
                        Member of gBW_Switches received command ON
                    then
                    	var String trigger = triggeringItem.name.toString.replace("_Auto", "")
                    	val valve = ScriptServiceUtil.getItemRegistry.getItem(trigger) as GenericItem
                    	val LZ = ScriptServiceUtil.getItemRegistry.getItem(trigger + "_LZ") as GenericItem
                    	
                    	val runtime = if(LZ.state instanceof Number) (LZ.state as Number).intValue else 0
                        if(runtime < 1)
                            return;
                    	valve.sendCommand(ON)
                    	logInfo("Garten", valve.label + " Bewässerung gestartet")
                    	
                    	timers.put(triggeringItem.name, createTimer(now.plusMinutes(runtime), [|
                    	   valve.sendCommand(OFF)
                    	   timers.put(triggeringItem.name, null)
                    
                    	   triggeringItem.postUpdate(OFF)
                    	   logInfo("Garten", valve.label + " Bewässerung abgeschlossen")
                    	   ]))
                    end
                    
                    rule "Automatik Abbrechen"
                    	when 
                    		Member of gBW_Switches received command OFF
                    	then
                    		var String trigger = triggeringItem.name.toString.replace("_Auto", "")
                    		val valve = ScriptServiceUtil.getItemRegistry.getItem(trigger) as GenericItem
                    		
                    		// Beregnung abbrechen, wenn sie gestartet wurde 
                         	if(timers.get(triggeringItem.name) !== null) {
                           		    timers.get(triggeringItem.name).cancel
                    				timers.put(triggeringItem.name, null)
                            	    logInfo("Garten", valve.label + "  Bewässerung abgebrochen")
                        	}
                        	
                    		if 	(valve.state == ON) {
                    				valve.sendCommand(OFF)
                    		}
                    end
                    Vielen Dank für den Hinweis!

                    Kommentar

                    Lädt...
                    X