Poolpumpe nach x Minuten ausschalten

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
mike69
Beiträge: 47
Registriert: 17. Nov 2020 22:38
Answers: 0

Poolpumpe nach x Minuten ausschalten

Beitrag von mike69 »

Hallo liebe Community.

Bin dabei eine Rule für die Poolfilterung zu erstellen. Der Fillter läuft bei PV Überschuss und besitzt einen Wartungsmodus. Die Taktung beträgt stumpf 5 Minuten, um die Geräte mit häufigen Schaltungen nicht zu strapazieren.

Hier der geistige Erguss:

Code: Alles auswählen


rule "Wartung Poolfilter"
when
  Item pumpswitch received update
then
  if (pumpswitch.state.toString == "ON") {
     logInfo("Pool", "Wartung")
     Shelly03_switch.sendCommand(OFF)
    }
  if (pumpswitch.state.toString == "OFF") {
     logInfo("Pool", "Betriebsbereit")
    }
end

rule "Pool Pump start"
when
  Time cron "0 0/5 9-18 * * ?"
then
  if (pumpswitch.state.toString == "ON")
     return;
  if (House_PowerOut.state >= 1200 && Shelly03_switch.state == OFF) {
    logInfo("Pool", "Pool an")
    Shelly03_switch.sendCommand(ON)
   }
  if (House_PowerOut.state <= 100 && Shelly03_switch.state == ON) {
    logInfo("Pool", "Pool aus")
    Shelly03_switch.sendCommand(OFF)
   }
end
Nicht schön, aber funktioniert. :)

Nun läuft die Pumpe bei sonnigen Wetter lange, zu lange. Würde das gerne mit einer Laufzeiterfassung limitieren. Hier im Forum gibt es paar Ideen eines Counters oder Timer, nur bekommes hier nicht wirklich zum laufen.
Hier die test.rules mit mir 20 Sekunden:

Code: Alles auswählen


rule "Reset Laufzeit"
when    
  Time cron " 20 0 0 1/1 * ? * "
then
  Counter = 0
end

rule "Laufzeit"
when    
  Time cron " 0/20 * * * * ?"
then
  if (Counter.state instanceof Number && Shelly03_switch.state == ON) {
    Counter.postUpdate((Counter.state as Number)+20)
    logInfo("Filter Laufzeit", "counter {} Sekunden", Counter.state)
   }
end
Hier die test.items:

Code: Alles auswählen

Number  Counter      "Counter"             <status>
Und die Fehlermeldungen im Log:

Code: Alles auswählen

[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'test-2' failed: 'postUpdate' is not a member of 'java.lang.Integer'; line 19, column 5, length 48 in test
Tja, ist "postUpdate" in der aktuellen OH Version nicht mehr verfügbar? Weiss jemand Rat oder hat einen Tip?

Gruß, Mike
openHAB 4.2.0 auf Debian 12 als VM unter Proxmox

Benutzeravatar
udo1toni
Beiträge: 14387
Registriert: 11. Apr 2018 18:05
Answers: 224
Wohnort: Darmstadt

Re: Poolpumpe nach x Minuten ausschalten

Beitrag von udo1toni »

Counter ist vermutlich kein Item (die vollständige Meldung besagt ja, dass postUpdate kein Member von java.lang.Integer ist)
Ganz unabhängig davon ist die Umsetzung aber ziemlich ineffizient.

Punkt eins in der "dummen" Version: Du triggerst auf received update, die Rule müsste aber nur auf changed reagieren (streng genommen sogar nur auf changed to ON).
Punkt zwei ist das stumpfe Reagieren auf Zeit.
Besser: reagiere auf Änderungen von Messwerten. Prüfe dann vor dem Schalten der Pumpe, ob sie in den letzten fünf Minuten geschaltet wurde. Ist das der Fall, verschiebst Du das Schalten mittels Timer, etwa so:

Code: Alles auswählen

var Timer tPumpe = null

rule "Wartung Poolfilter"
when
    Item pumpswitch changed
then
    var strMessage = "Betriebsbereit"
    if(newState == ON) {
       strMessage = "Wartung"
       if(Shelly03_switch.state != OFF) Shelly03_switch.sendCommand(OFF)
    }
    logInfo("Pool", strMessage)
end

rule "Pool Pumpe"
when
    Item House_PowerOut changed
then
    val iPower = if(newState instanceof Number) (newState as Number).intValue else 0
    if(tPumpe !== null) {
        logDebug("pool","Taktsperre, Abbruch")
        return;
    }
    if(pumpswitch.state != OFF){
        logDebug("pool","Wartung aktiv, Abbruch")
        return;
    }
    var soll  = Shelly03_switch.state
    if(iPower >= 1200) soll = ON
    if(iPower <=  100) soll = OFF
    if(Shelly03_switch.state != soll) {
        logInfo("pool","Pool Pumpe {}",if(soll == ON) "an" else "aus")
        Shelly03_switch.sendCommand(soll.toString)
        tPumpe = createTimer(now.plusMinutes(5),[|tPumpe = null])
    }
end
Die erste Rule kümmert sich wie gehabt um den Wartungsmodus. Mit dem Unterschied, dass der Shelly nur dann ausgeschaltet wird, wenn dies auch tatsächlich notwendig sein sollte.
Die zweite Rule prüft bei jeder Änderung des aktuellen Wertes von House_PowerOut, ob ein Timer tPumpe existiert. Ist das der Fall, so bricht die Rule ab. Im weiteren Verlauf prüft die Rule, ob der Wartungsschalter aktiv ist und bricht gegebenenfalls ab.
Danach setzt die Rule eine lokale Variable soll auf den aktuellen Status des Shelly. Anschließend prüft es die Grenzwerte ab und ändert gegebenenfalls die lokale Variable.
Zum Abschluss prüft die Rule, ob die lokale Variable vom Istzustand abweicht und schaltet gegebenenfalls den Shelly um. Nur in diesem Fall wird der Timer gesetzt, dessen einzige Funktion es ist, den Zeiger auf sich selbst zu löschen.
Da durch die PV der Wert im Item House_PowerOut ohnehin ständig wechseln dürfte, wir die Rule immer zeitnah erneut ausgeführt.

Wenn der Betriebszähler korrekt eingerichtet ist, reicht es, die OFF-Bedingung passend zu ergänzen:

Code: Alles auswählen

    if(iPower <=  100 || Counter > Grenzwert) soll = OFF
Für den Counter gibt es letztlich zwei Möglichkeiten, nämlich entweder mit Variable oder mit Item. Beide Optionen sind gut, aber die Befehle sind unterschiedlich.
Für die Variante mit Item spricht, dass Du ohne Umweg das Item in der UI anzeigen lassen kannst. Die Option mit der Variablen könnte dafür unterm Strich mit weniger Code auskommen.
Auch hier wäre vermutlich ein anderer Trigger besser:

Code: Alles auswählen

var iCounter = 0                                                                   // löschen, falls Item

rule "Reset Laufzeit"
when
    Time cron "0 0 0 * * ?" // täglich, 0:00:00 Uhr
then
    iCounter = 0                                                                   // löschen, falls Item
    Counter.postUpdate(0)                                                          // löschen, falls globale Variable
end

rule "Laufzeit"
when
    Item Shelly03_switch changed
then
    if(newState == OFF) {
        var iCounter = 0                                                           // löschen, falls globale Variable
        if(Counter.state instanceof Number)                                        // löschen, falls globale Variable
            iCounter = (Counter.state as Number).intValue                          // löschen, falls globale Variable
        iCounter += (now.toEpochSecond - Shelly03_switch.lastChange.toEpochSecond).intValue
        logInfo("pool", "Filter Laufzeit heute {} Sekunden", iCounter)
   }
end
Hier mit der globalen Variablen und mit einem Item, Du solltest Dich für eine der beiden Varianten entscheiden.
Alle globalen Definitionen (also sowohl iCounter als auch tPumpe) müssen wenn, dann vor der ersten Rule der *.rules Datei stehen, also gewöhnlich in den ersten paar Zeilen der Datei.
Die Methode .lastChange steht erst im aktuellen OH4.2 zur Verfügung, sie liefert den Zeitstempel des letzten Changed Ereignisses vor dem aktuellen Change.
Nachteil: die Rule bekommt während die Pumpe läuft nicht mit, wenn die gewünschte Betriebsdauer überschritten wird.
openHAB4.2.0 stable in einem Debian-Container (bookworm) (Proxmox 8.2.4, LXC), mit openHABian eingerichtet

mike69
Beiträge: 47
Registriert: 17. Nov 2020 22:38
Answers: 0

Re: Poolpumpe nach x Minuten ausschalten

Beitrag von mike69 »

Moinsen.

Danke für die Antwort, Udo.

Habe deine Rule scharf geschaltet, funzt mit einer Fehlermeldung:

Code: Alles auswählen

2024-07-25 08:32:19.937 [INFO ] [org.openhab.core.model.script.pool  ] - Pool Pumpe an
2024-07-25 08:34:36.939 [INFO ] [org.openhab.core.model.script.pool  ] - Pool Pumpe aus
2024-07-25 08:37:19.939 [WARN ] [ore.internal.scheduler.SchedulerImpl] - Scheduled job '<unknown>' failed and stopped
java.lang.IllegalStateException: Cannot resolve proxy: test2.rules#/1
	at org.eclipse.xtext.common.types.util.JavaReflectAccess.getRawType(JavaReflectAccess.java:109) ~[?:?]
	at org.eclipse.xtext.common.types.util.JavaReflectAccess.getField(JavaReflectAccess.java:58) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._assignValueTo(XbaseInterpreter.java:1354) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter._assignValueTo(ScriptInterpreter.java:211) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1327) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1319) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:239) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:227) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:475) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:251) ~[?:?]
	at org.openhab.core.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:227) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:213) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:47) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:30) ~[?:?]
	at jdk.proxy16481.$Proxy16613.apply(Unknown Source) ~[?:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$12(SchedulerImpl.java:189) ~[?:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$1(SchedulerImpl.java:88) ~[?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) [?:?]
	at java.lang.Thread.run(Thread.java:840) [?:?]
2024-07-25 08:39:39.854 [INFO ] [org.openhab.core.model.script.pool  ] - Pool Pumpe an
Um 8:32 sprang die Pumpe an, 8:34 durch erhöhten Verbrauch aus und 8:39 artig durch den Timer geregelt wieder an. Soweit passt das alles. :D
Werde jetzt die Laufzeit Rule mit der Variante "Items" installieren, wenn ich dadurch die Laufzeit einfacher im Sitemap anzeigen lassen kann.
Auch hier wäre vermutlich ein anderer Trigger besser:
Welcher Trigger wäre besser nach deiner Meinung?

Noch was, während der tipperei hier und dem aktuell lausigen Überschuss taktet die Pumpe. Eben sprang die Pumpe an und der Toaster schickte ihn nach 15 Sekunden in die Pause. :)
Entweder spliele ich noch mit den Start Stop Werten oder drücke ihn eine Mindestlaufzeit rein. Fange mit den Werten an... :lol:

Gruß, Mike

edit:

Wenn die Pumpe ausschaltet kommt die Meldung:

Code: Alles auswählen

2024-07-25 10:09:16.763 [INFO ] [org.openhab.core.model.script.pool  ] - Pool Pumpe aus
2024-07-25 10:09:17.532 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'test3-2' failed: 'lastChange' is not a member of 'org.openhab.core.library.items.SwitchItem'; line 16, column 42, length 26 in test3
Hier die Rule in der Item Variante:

Code: Alles auswählen

rule "Reset Laufzeit"
when
    Time cron "0 0 0 * * ?" // täglich, 0:00:00 Uhr
then
    Counter.postUpdate(0)                                                          // löschen, falls globale Variable
end

rule "Laufzeit"
when
    Item Shelly03_switch changed
then
    if(newState == OFF) {
        var iCounter = 0                                                           // löschen, falls globale Variable
        if(Counter.state instanceof Number)                                        // löschen, falls globale Variable
            iCounter = (Counter.state as Number).intValue                          // löschen, falls globale Variable
        iCounter += (now.toEpochSecond - Shelly03_switch.lastChange.toEpochSecond).intValue
        logInfo("pool", "Filter Laufzeit heute {} Sekunden", iCounter)
   }
end
Habe erstmal die rules getrennt in 2 Textdateien, nur zur Info.


edit2:

Update auf 4.2.0 wurde vorgeschlagen und installiert. Ein Teil der Fehlermeldungen treten nicht mehr auf (Sheduled Job), dafür kam dieser hier wenn die Pumpe ausgeschaltet wird:

Code: Alles auswählen

Script execution of rule with UID 'test3-2' failed: cannot invoke method public default long java.time.chrono.ChronoZonedDateTime.toEpochSecond() on null
Komme mir vor wie ein Schwein, was ins Uhrwerk schaut... :(
openHAB 4.2.0 auf Debian 12 als VM unter Proxmox

Benutzeravatar
udo1toni
Beiträge: 14387
Registriert: 11. Apr 2018 18:05
Answers: 224
Wohnort: Darmstadt

Re: Poolpumpe nach x Minuten ausschalten

Beitrag von udo1toni »

Oben bei mir fehlte ein r am Anfang von rule :) ansonsten sehe ich da aber eigentlich nicht, was da schief gehen sollte.

Wenn lastChange nicht als Methode zur Verfügung steht, bedeutet das, dass das Item nicht persistiert wird. Hatte ich vermutlich oben vergessen, mit dazu zu schreiben. mapdb als Persistence Service sollte dafür ausreichen und als strategy everyChange, Du kannst aber auch jeden anderen Persistence Service nutzen, solange mindestens everyChange als strategy gesetzt ist. Handelt es sich bei der gewünschten Persistence nicht um die default Persistence, so musst Du die Quelle mit angeben:

Code: Alles auswählen

iCounter += (now.toEpochSecond - Shelly03_switch.lastChange("mapdb").toEpochSecond).intValue
hier wird als Persistence Quelle mapdb verwendet.
openHAB4.2.0 stable in einem Debian-Container (bookworm) (Proxmox 8.2.4, LXC), mit openHABian eingerichtet

Benutzeravatar
udo1toni
Beiträge: 14387
Registriert: 11. Apr 2018 18:05
Answers: 224
Wohnort: Darmstadt

Re: Poolpumpe nach x Minuten ausschalten

Beitrag von udo1toni »

So, ich hab das Ganze hier mal nachgestellt.

Punkt 1: Das Takten wird zuverlässig unterbunden. Da muss bei Dir irgendwas schief gehen. Evtl. lädst Du zwischendurch das Rule File neu, das führt dann dazu, dass der Zeiger auf den Timer verloren geht.
Punkt 2: Der Zeitstempel... Leider verhält sich openHAB hier nicht wie erwartet. Vielmehr nimmt es den Zeitstempel des letzten Changes, das ist aber der, der die Rule ausgelöst hat, also "jetzt".
Deshalb müssen wir hier auf previousState ausweichen, und die Timestamp steht hier leider nur zur Verfügung, wenn die Persistence Engine mehr als den aktuellen Wert hält. Schlimmer noch, funktioniert rrd4j hier eventuell nicht (hab ich nicht ausprobiert, aber unter 4.1.x war das buggy - previousState hat hier gar nicht funktioniert).
Das ist natürlich sehr schade, bedeutet es doch, dass wir anders zählen müssen. Der Einfachheit halber mittels globaler Variable:

Code: Alles auswählen

var Timer tPumpe     = null
var long  lPumpStart = 0

rule "Wartung Poolfilter"
when
    Item pumpswitch changed
then
    var strMessage = "Betriebsbereit"
    if(newState == ON) {
       strMessage = "Wartung"
       if(Shelly03_switch.state != OFF) Shelly03_switch.sendCommand(OFF)
    }
    logInfo("pool", strMessage)
end

rule "Pool Pumpe"
when
    Item House_PowerOut changed
then
    val iPower = if(newState instanceof Number) (newState as Number).intValue else 0
    if(tPumpe !== null) {
        logDebug("pool","Taktsperre, Abbruch")
        return;
    }
    if(pumpswitch.state != OFF){
        logDebug("pool","Wartung aktiv, Abbruch")
        return;
    }
    var soll  = Shelly03_switch.state
    if(iPower >= 1200) soll = ON
    if(iPower <=  100) soll = OFF
    if(Shelly03_switch.state != soll) {
        logInfo("pool","Pool Pumpe {}",if(soll == ON) "an" else "aus")
        Shelly03_switch.sendCommand(soll.toString)
        tPumpe = createTimer(now.plusMinutes(5),[|tPumpe = null])
    }
end

rule "Reset Laufzeit"
when
    Time cron "0 0 0 * * ?" // täglich, 0:00:00 Uhr
then
    Counter.postUpdate(0)
end

rule "Laufzeit"
when
    Item Shelly03_switch changed
then
    if(newState == OFF) {
        var iCounter = 0
        if(Counter.state instanceof Number)
            iCounter = (Counter.state as Number).intValue
        iCounter += (now.toEpochSecond - lPumpStart).intValue
        logInfo("pool", "Filter Laufzeit heute {} Sekunden", iCounter)
        Counter.postUpdate(iCounter)
   } else {
        lPumpStart = now.toEpochSecond
   }
end 
Wenn Du das Logging aufdrehst (in der Karaf Konsole log:set DEBUG org.openhab.core.model.script.pool) kannst Du im Log nachverfolgen, dass die Rule "Pool Pumpe" fünf Minuten lang abgebrochen wird, falls der Shelly umgeschaltet wurde.
openHAB4.2.0 stable in einem Debian-Container (bookworm) (Proxmox 8.2.4, LXC), mit openHABian eingerichtet

mike69
Beiträge: 47
Registriert: 17. Nov 2020 22:38
Answers: 0

Re: Poolpumpe nach x Minuten ausschalten

Beitrag von mike69 »

Moin.

Sorry für die späte Antwort, war viel los am WE.
Punkt 1: Das Takten wird zuverlässig unterbunden. Da muss bei Dir irgendwas schief gehen. Evtl. lädst Du zwischendurch das Rule File neu, das führt dann dazu, dass der Zeiger auf den Timer verloren geht.
Ja das stimmt. :)
Hatte zu viel rumgerührt in der rules Datei.

Mit deiner aktuellen Kreation läuft es seit 2 Tagen sauber durch.
Wenn lastChange nicht als Methode zur Verfügung steht, bedeutet das, dass das Item nicht persistiert wird.
Nutze influxdb only. Da werden keine States erfasst, nur Temperaturen, Energieverbrauch usw...
Macht wohl Sinn, influxdb dahin zu konfigurieren. :)

Danke für deine Hilfe.

Gruß, Mike
openHAB 4.2.0 auf Debian 12 als VM unter Proxmox

Benutzeravatar
udo1toni
Beiträge: 14387
Registriert: 11. Apr 2018 18:05
Answers: 224
Wohnort: Darmstadt

Re: Poolpumpe nach x Minuten ausschalten

Beitrag von udo1toni »

Wenn Du InfluxDB nutzt, ginge es wiederum mit der .perviousState(true).timestamp, das liefert dann den Zeitstempel des letzten vom aktuellen Status abweichenden Datensatzes in der Persistence. Wichtig ist dann noch, dass die Persistence (für dieses Item) ausschließlich everyChange persistiert. Anschließend ist es dann kein Problem, den exakten zeitlichen Abstand zwischen dem letzten ON und dem aktuellen OFF zu bestimmen.
openHAB4.2.0 stable in einem Debian-Container (bookworm) (Proxmox 8.2.4, LXC), mit openHABian eingerichtet

Antworten