Mal wieder eine "rule" Frage

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

atk69
Beiträge: 95
Registriert: 15. Jan 2019 19:07
Answers: 1
Wohnort: Weil am Rhein

Mal wieder eine "rule" Frage

Beitrag von atk69 »

Hallo zusammen

Ich habe eine gut funktionierende rule für meine Jarolift (433MHz) Rolläden.
Da der/die Motoren ab und zu nicht auf Anhieb reagieren, habe ich die Befehle wiederholt.
Weiterhin soll dass Ganze nur in einem bestimmten Zeitfenster und bei entsprechendem Luxwert arbeiten.
Soweit alles gut. Es läuft wunderbar in OH2.
Jetzt stellt sich aber das Problem, dass ich bei den heissen Temperaturen gerne mal länger offen lassen möchte.
Der Knackpunkt ist, dass der Luxwert (State <= 3) durchaus schwankt und der absenkimpuls 2-3 ausgelöst wird.
Also müsste nach dem ersten "auslösen" ein Timer (um die 10min) starten, der mir die Messung / Steuerung aussetzt.

Ich habe hier schon so etwas gesehen (wie -fast- immer von udo1toni), bekomme es aber nicht in meine rule :roll:
Kann sich das mal ein erfahrener anschauen :P

Code: Alles auswählen


rule "Luminanz"
when
Item dataLum changed
then
var State = dataLum.state as DecimalType
if ((State <= 3) && ((now.getMinuteOfDay > (60*18)) && (now.getMinuteOfDay < (60*23+1 ))))	
  {
  Markise.sendCommand(DOWN)
  Thread::sleep(60000)
  Roll_WZ_W_li.sendCommand(DOWN)
  Thread::sleep(1000)
  Roll_WZ_W_re.sendCommand(DOWN)
  Thread::sleep(1000)
  Markise.sendCommand(DOWN)
  Thread::sleep(1000)
  Roll_WZ_W_li.sendCommand(DOWN)
  Thread::sleep(1000)
  Roll_WZ_W_re.sendCommand(DOWN)
  }
 end
------------------------------------------------------
// var Timer tLum = null 
//		 {                          
//	tLum = createTimer(now.plusMillis(60000),[
 //	               Markise.sendCommand(DOWN)
//					Thread::sleep(30000)
//					Roll_WZ_W_li.sendCommand(DOWN)
//					Thread::sleep(1000)
//					Roll_WZ_W_li.sendCommand(DOWN)
//					Thread::sleep(1000)
//					Roll_WZ_W_re.sendCommand(DOWN)
//					Thread::sleep(1000)
//					Roll_WZ_W_re.sendCommand(DOWN)
 //	               //tLum = null
 //	           ])
//	}
//	end
der obere Teil ist aktuell und läuft.
der untere wäre mit Timer, läuft aber nicht....

Danke
atk69
and IT works ;)

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

Re: Mal wieder eine "rule" Frage

Beitrag von udo1toni »

Es ist verlockend, das Problem mit Thread::sleep zu lösen, es birgt aber eine reale Gefahr, gerade, wenn der Trigger und das zugehörige if-Statement so leicht mehrfach hintereinander getriggert werden kann; Es kann jegliches Ausführen von Rules verhindern.

Hintergrund dazu: openHAB nutzt für die Ausführung von Rules einen sogenannten Thread Pool, das heißt, es gibt einen Pool von Threads (sic!) die zum Ausführen von Rules vorgehalten werden. Dieser Pool umfasst 5 Threads, es können also maximal 5 Rules gleichzeitig ausgeführt werden. Werden mehr als 5 Rules zeitgleich getriggert, muss die letzte in der Reihe (es gibt im Rechner keine Gleichzeitigkeit...) eben so lange warten, bis ein Thread frei wird. Da die Rules asynchron laufen und das nirgendwo aktiv verhindert wird, kann dieselbe Rule auch 5 mal gleichzeitig starten (z.B. Helligkeit schwankt schnell zwischen 1 und 2 Lux).

Besser ist es also, wenn möglich auf Thread::sleep zu verzichten, allenfalls, um eine kurze Denkpause einzulegen (bis maximal 500mSec) ist Thread::sleep ok.

Die Pausen zwischen den einzelnen Läden sind vermutlich der Funktechnik geschuldet, aber ich verstehe, dass Du da nicht unbedingt investieren willst, wo die manuelle Steuerung ja gut funktioniert.

Für mich sähe der Code eher so aus:

Code: Alles auswählen

// Globale Variablen immer zu Beginn der Datei definieren!
var Timer tLum = null 
var Number nLum = 0


rule "Luminanz"
when
    Item dataLum changed
then
    if(!(dataLum.state instanceof Number)) {
        logWarn("luminanz","dataLum enthält keinen gültigen Wert!")
        return;
    }
    if ((dataLum.state as Number) < 4 && now.getHourOfDay > 17 && now.getHourOfDay < 23 && tLum === null) {
        nLum = 0
        tLum = createTimer(now.plusSeconds(1), [|
            var Number nTime = 1
            nLum += 1
            switch nLum {
                case 1: {
                    Markise.sendCommand(DOWN)
                    nTime = 60
                }
                case 2: {
                    Roll_WZ_W_li.sendCommand(DOWN)
                }
                case 3: {
                    Roll_WZ_W_re.sendCommand(DOWN)
                }
                case 4: {
                    Markise.sendCommand(DOWN)
                }
                case 5: {
                    Roll_WZ_W_li.sendCommand(DOWN)
                }
                case 6: {
                    Roll_WZ_W_re.sendCommand(DOWN)
                    nTime = 600
                }
                default: {
                    nTime = 0
                }
            }
            if(nTime > 0)
                tLum.reschedule(now.plusSeconds(nTime))
            else {
                tLum = null
            }
        ])
    }
end
Ich habe hier bewusst auf schmutzige Tricks verzichtet :) möchte aber Verbesserungspotential nicht vorenthalten:
Wenn man auf verschieden lange Pausen verzichtet, kann man mittels Gruppierung der Items die Rule extrem einkürzen.

Die Rule startet, wenn alle Bedingungen erfüllt sind, dazu gehört, dass der Timer gerade nicht läuft.
Wenn alles passt, wird der Zähler initialisiert und der Timer angelegt. Der Timer startet nach einer Sekunde (man könnte diese Zeit auch noch kürzer wählen, das ist jetzt einfach Bequemlichkeit). Zuerst wird der Zähler erhöht, anschließend wird abhängig vom Zählerstand jeweils ein anderer Befehl ausgeführt. Zu Beginn wird die Hilfsvariable nTime auf 1 gesetzt. Anschließend wird nur bei Bedarf eine andere Zeit eingetragen (bei der Markise z.B. 60). Zum Schluss wird der Timer zur erneuten Ausführung geplant.
Im vorletzten Schritt wird nTime auf 600 gesetzt, was dann 10 Minuten Pause bedeutet.
Wenn der Zähler über 6 steigt, wird nTime auf 0 gesetzt. Damit führt die letzte Bedingung nicht mehr zum Reschedule, sondern der Zeiger (und damit der Code) wird gelöscht. Anschließend kann der Timer wieder durch die Rule angelegt werden.

Die Rule selbst kann nun beliebig oft triggern, der Code wird exakt einmal ausgeführt, danach ist 10 Minuten Ruhe. Der Timer selbst läuft nur wenige Millisekunden pro Zyklus, danach steht der Thread sofort wieder zur Verfügung, auch, wenn der Timer neu geplant wurde.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

atk69
Beiträge: 95
Registriert: 15. Jan 2019 19:07
Answers: 1
Wohnort: Weil am Rhein

Re: Mal wieder eine "rule" Frage

Beitrag von atk69 »

:D HAMMER :D
Ich bin ja selbst in der IT tätig, allerdings nicht Devop sondern System.
aber auf so eine Lösung wäre ich nie gekommen.
Was mich zu der Frage führt: Woher kennst du so genau die Strukturen von Openhab und wie kann ich mich selbst weiterbilden bei der Programmierung.
Bisher habe ich über das "Innenleben" von Openhab relativ wenig Info`s gesehen. Zumindest keine, die mir das scripten erklären /erleichtern.

Auf jeden Fall: einen riesigen Dank für die Hilfe und vor allem für die ausführliche Erklärung. :P
Da werde ich -allein durch deine Infos`s - ein paar rules "nachbearbeiten" ;)

Gruss
atk69

Nachtrag: habe die rule mal 1:1 übernommen (variablen sind ja korrekt)
das log meckert wegen der Zeile :

Code: Alles auswählen

 if ((dataLum.state as Number) < 4 && now.getHourOfDay > 17 && now.getHourOfDay < 23 tLum === null) {
wie folgt:

Code: Alles auswählen

[WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'WZ_WI_ROLL_LUM.rules' has errors, therefore ignoring it: [14,89]: missing ')' at 'tLum'
[14,102]: mismatched input ')' expecting 'end'
was ja nicht wirklich schlimm wäre, allerdings tut sich an den Rollos nix mehr. :roll: ???

Und schon merkt man, das ich nicht wirklich Ahnung habe :(
and IT works ;)

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

Re: Mal wieder eine "rule" Frage

Beitrag von udo1toni »

Genau, da fehlt datsächlich hinten noch ein && vor dem tLum... Ich korrigiere das oben...

Was das Innenleben von openHAB betrifft...

Ich nutze openHAB seit Version 1.0, das war so ca. 2012 (oder 2013?), soll heißen, ich bin ein alter Hase ;)
Die Rules DSL hat ein paar "Eigenheiten". Wenn man das erste Mal damit in Berührung kommt, wird man fast zwangsläufig Code erstellen, der viel Verbesserungspotential hat. Mit der Zeit lernt man aber, dass es z.B. nicht schlimm ist, dass man keine Funktionen erstellen kann (doch, geht, aber der Aufwand lohnt meist nicht), weil es in der DSL viel elegantere Methoden gibt, die gestellte Aufgabe zu lösen.

Wenn man z.B. eine Gruppe definiert:

Code: Alles auswählen

Group Rollershutter gRoll1
Rollershutter Markise "Markise" (gRoll1)
Rollershutter Roll_WZ_W_li "Rollladen Wohnzimmer links" (gRoll1)
Rollershutter Roll_WZ_W_re "Rollladen Wohnzimmer rechts" (gRoll1)
Kann man alle Läden auf einne Schlag steuern:

Code: Alles auswählen

gRoll1.sendCommand(DOWN)
Im vorliegenden Fall gibt es aber das Problem, dass die Steuerung die Befehle nicht schnell genug verarbeiten kann. Macht aber nichts, denn man kann so eine Gruppe gezielt durchlaufen:

Code: Alles auswählen

gRoll1.members.forEach[i|
    i.sendCommand(DOWN)
    Thread::sleep(500)]
für jedes Gruppenmitglied wird der Befehl ausgeführt, anschließend wartet der Prozess eine halbe Sekunde bis zum nächsten Fahrbefehl. (wie erwähnt ist eine halbe Sekunde vielleicht gerade noch so vertretbar).
Da nun Thread::sleep(500) mehrfach aufgerufen wird, ist diese Lösung aber auch nicht gerade toll.

Code: Alles auswählen

nCounter = 0
tMyTimer = createTimer(now.plusSeconds(1), [
    nCounter += 1
    gRoll1.members.sortBy[name].index(nCounter).sendCommand(DOWN)
    if(nCounter < gRoll1.members.size)
        tMyTimer.reschedule(now.plusSeconds(1))
])
durchläuft die Gruppe ebenfalls einmal und sendet pro Sekunde einen Fahrbefehl. Der Befehl soll allerdings zweimal raus gehen...

Code: Alles auswählen

nCounter = 0
tMyTimer = createTimer(now.plusSeconds(1), [
    nCounter += 1
    gRoll1.members.sortBy[name].index((nCounter % gRoll1.members.size) + 1).sendCommand(DOWN)
    if(nCounter < gRoll1.members.size * 2)
        tMyTimer.reschedule(now.plusSeconds(1))
])
schwupp, wird jedem Laden der Befehl zweimal gesendet. Da nun aber mit dem 2. Laden begonnen wird, müsste das Hochzählen im Anschluss erfolgen...

Solche Optimierungen findet man heraus, wenn man anderer Leute Code analysiert (was zu Anfang hart ist, aber mit der Zeit immer leichter fällt)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

atk69
Beiträge: 95
Registriert: 15. Jan 2019 19:07
Answers: 1
Wohnort: Weil am Rhein

Re: Mal wieder eine "rule" Frage

Beitrag von atk69 »

Vielen Dank udo1toni
jetzt weiss ich, dass ich es mit einem erfahrenen "OH`ler"zu tun habe (das wusste ich aber auch schon vorher ;) )
Der kleine Grundkurs ist auf jeden Fall interessant und hat mich auch schon wieder etwas weiter gebracht.
Syntax, Variablen und welche Befehle überhaupt möglich sind, finde ich ja zum Teil im Wiki.

Fremcode zun lesen und auch zu nutzen war bisher mein Weg. Nur mit dem verstehen happert es noch :roll:
Aber das wird...
Ansonsten, wenn nichts mehr geht, habe ich ja hier eine gute Adresse.
Da du ja grundsätzlich deine Unterstützung gut kommentierst, gibt es immer wieder "Lernmaterial" hier zu finden.

Vielen Dank und hoffentlich funktionert die rule heute Abend :D

Gruss
atk69
and IT works ;)

atk69
Beiträge: 95
Registriert: 15. Jan 2019 19:07
Answers: 1
Wohnort: Weil am Rhein

Re: Mal wieder eine "rule" Frage

Beitrag von atk69 »

Noch ein error:

Code: Alles auswählen

2019-08-08 21:11:55.530 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Luminanz': The name 'treateTimer' cannot be resolved to an item or type; line 16, column 16, length 1014
2019-08-08 21:13:55.449 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Luminanz': The name 'treateTimer' cannot be resolved to an item or type; line 16, column 16, length 1014
2019-08-08 21:15:55.454 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Luminanz': The name 'treateTimer' cannot be resolved to an item or type; line 16, column 16, length 1014
habe was bei openhab.org gefunden https://community.openhab.org/t/rule-is ... e/49939/17
sogar hier findet man udo1toni ;)
Werde aber nicht schlau daraus.
Muss "treateTimer" wirlkich als item angelegt werden ?
and IT works ;)

Benutzeravatar
peter-pan
Beiträge: 2564
Registriert: 28. Nov 2018 12:03
Answers: 25
Wohnort: Schwäbisch Gmünd

Re: Mal wieder eine "rule" Frage

Beitrag von peter-pan »

Ich denke, dass soll "createTimer" heissen ;)
Pi5/8GB(PiOS Lite 64-bit(bookworm)/SSD 120GB - OH4.1.1 openhabian

atk69
Beiträge: 95
Registriert: 15. Jan 2019 19:07
Answers: 1
Wohnort: Weil am Rhein

Re: Mal wieder eine "rule" Frage

Beitrag von atk69 »

peter-pan hat geschrieben: 8. Aug 2019 21:55 Ich denke, dass soll "createTimer" heissen ;)
Logisch :mrgreen: :mrgreen: :mrgreen:
Ich seh den Wald vor lauter Bäumen nicht :D
Danke Peter-pan

Gruss
atk69
and IT works ;)

atk69
Beiträge: 95
Registriert: 15. Jan 2019 19:07
Answers: 1
Wohnort: Weil am Rhein

Re: Mal wieder eine "rule" Frage

Beitrag von atk69 »

hallo nochmal,
habe zwar keine Fehlermeldungen mehr, aber die rule wird nicht ausgeführt. :cry:
Keine Eintäge im openhab.log und im event.log.
Jetzt steh ich total auf dem Schlauch.
and IT works ;)

Benutzeravatar
peter-pan
Beiträge: 2564
Registriert: 28. Nov 2018 12:03
Answers: 25
Wohnort: Schwäbisch Gmünd

Re: Mal wieder eine "rule" Frage

Beitrag von peter-pan »

Poste die Rule bitte noch einmal, so wie du sie jetzt im Einsatz hast. Bzw. mache doch mal direkt nach "then" ein "logInfo" in die Rule, als Debug-Point , dann siehst du, ob sie triggert. Wenn das der Fall ist, dann musst du den Body noch einmal etwas genauer untersuchen.
Pi5/8GB(PiOS Lite 64-bit(bookworm)/SSD 120GB - OH4.1.1 openhabian

Antworten