Seite 5 von 8

Re: Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 00:30
von udo1toni
Boris099 hat geschrieben: 27. Feb 2020 18:40 Ich bekomme das mit dem Kopieren des Codes nicht ordentlich hin, sieht leider alles etwas schräg aus :lol: )
Das liegt daran, dass die Forensoftware einen Tabulator alle acht Zeichen setzt, während default andere Editoren vier Zeichen nutzen. Am besten gar keine Tabulatoren nutzen und nur mit Leerzeichen formatieren. Tipp: in VisualStudio Code kann man wählen, ob der Editor Tabs oder Leerzeichen einfügt.
Boris099 hat geschrieben: 1. Die Mai Infos werden nur gesendet wenn der neu aufgesetzte Dummy Switch "InfoSenden" auf ON steht.
Ja, das hast Du soweit korrekt umgesetzt
Boris099 hat geschrieben: 2. Ich schalte mit 2 zusätzlichen Timer einen LED Strahler
Dazu müsstest Du aber auch zwei zusätzliche Timer verwenden, nicht den einen, der schon an anderer Stelle genutzt wird.
Boris099 hat geschrieben: Insbesondere diese neuen shutoffTimer, die ich da eingebaut haben, gehen die an diesen Positionen,
und stören die nicht den eigentlichen Alarmtimer?
Da Du keine zusätzlichen Timer verwendest, geht das komplett schief.

Ich habe hier einige Kommentare entfernt, damit die Rule lesbar bleibt:

Code: Alles auswählen

import java.util.List
var List<Timer> timers = newArrayList                                                       // Timer Liste
var Timer shutoffTimer = null                                                               // Timer zum Beenden des Alarms
var Timer LEDTimer1 = null                                                                  // Timer für Bewegungserkennung
var Timer LEDTimer2 = null                                                                  // Timer für optischne Alarm
var lastRun = 0                                                                             // Zeitstempel

rule "Rule Datei eingelesen"
when
    System started
then
    lastRun = now.minusMinutes(2)
end

rule "Motion detected 3 times in a minute"
when
    Member of gBW changed from OFF to ON
then
                                                                                                // sobald die Rule triggert
    if(AlarmAussen.state == OFF) {                                                              // Alarm unscharf-Block
        logInfo("bw_alarm","Alarmanlage aus, Rule Ende!")
        return;
    }
                                                                                                // Wenn der Alarm scharf ist
    if(LEDTimer1 === null) {                                                                    // Nur, falls die LED noch nicht an ist
        LED_GaesteV.sendCommand(ON)                                                             // LED Strahler ein und
        LEDTimer1 = createTimer(now.plusSeconds(2), [ |                                         // Timer zum Abschalten des LED Strahler anlegen
            LED_GaesteV.sendCommand(OFF)
            LEDTimer1 = null
        ] )
    }
    val mailActions = getActions("mail","mail:smtp:c1a3d968")                                   // MailAction Objekt auf jeden Fall anlegen
    if(InfoSenden.state == ON) {                                                                // Falls InfoSenden aktiv
        val mailText = "Bewegungsmelder " + triggeringItem.name + " hat ausgelöst"
        mailActions.sendMail("xxx@gmail.com","Alarm "+ triggeringItem.name, mailText)
    }
    if(shutoffTimer !== null) {                                                                 // Falls shuttoffTimer existiert
        logInfo("bw_alarm","Alarm schon aktiv, Rule Ende!")
        return;
    }
                                                                                                // Wenn der Alarm scharf und gerade nicht aktiv ist
    if(lastRun.isAfter(now.minusMinutes(2))) {                                                  // Falls letzter Alarm vor weniger als 2 Minuten
        logInfo("bw_alarm","letzter Alarm vor weniger als 2 Minuten, Rule Ende!")
        return;
    }
                                                                                                // Wenn der letzte Alarm schon 2 Minuten her ist
    if(timers.size < 3) {                                                                       // Falls weniger als 3 Timer laufen
        val t = createTimer(now.plusMinutes(1), [ |
            timers.remove(0)
        ] )
        timers.add(t)
    }
    if(timers.size == 3) {                                                                      // Falls Liste 3 Timer umfasst
        if(InfoSenden.state == ON) {                                                            // Falls InfoSenden aktiv
            mailActions.sendMail("xxx@gmail.com","Sirene aktiviert ", "Sirene wurde aktiviert")
        }
        Sirene_Gaeste.sendCommand(ON)
        LEDTimer2 = createTimer(now.plusMillis(10), [ |                                         // LEDTimer anlegen
            if(LED_GaesteV.state != ON) {                                                       // Falls LED aus
                LED_GaesteV.sendCommand(ON)                                                     // LED ein
                LEDTimer2.reschedule(now.plusSeconds(3))                                        // und Timer in 3 Sekunden erneut ausführen
            } else {                                                                            // Falls LED an
                LED_GaesteV.sendCommand(OFF)                                                    // LED aus
                LEDTimer2.reschedule(now.plusSeconds(2))                                        // und Timer in 2 Sekunden erneut ausführen
            }
        ] )
        lastRun = now
        while(timers.size > 0) {
            timers.get(0).cancel
            timers.remove(0)
        }
        shutoffTimer = createTimer(now.plusSeconds(30), [ |                                     // Timer zum Abschalten des Alarms anlegen
            Sirene_Gaeste.sendCommand(OFF)
            logInfo("Alarmrule", "Sirene Ende")
            LEDTimer2?.cancel                                                                   // Falls LEDTimer2 existiert, abbrechen
            LEDTimer2 = null
            if(LED_GaesteV.state != OFF)                                                        // Falls die LED gerade an ist
                LED_GaesteV.sendCommand(OFF)                                                    // abschalten
            shutoffTimer = null
        ] )
    }
end
Die Theorie hinter dem Timer LEDTimer2: Der Timer wird angelegt und läuft fast sofort ab. Wenn der Timer abläuft, wird die LED eingeschaltet, falls sie aus ist und ausgeschaltet, falls sie an ist. Außerdem wird der Timer neu geplant, und zwar mit unterschiedlich langer Einschalt- und Ausschaltdauer.
Sobald der Timer zum Abschalten der Sirene abläuft, wird auch der LEDTimer2 beendet und die LED, falls noch eingeschaltet, wird ausgeschaltet.

Re: Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 11:56
von Boris099
Hallo, und zuerst mal vielen Dank,
sieht super aus!

ich hoffe du schüttelst diesen Code nicht einfach so aus dem Ärmel, sonst wäre ich deprimiert :D
Das kostet mich jetzt erst mal ein paar Minuten der Sache zu folgen...
und habe ein paar kleine Verständnisfragen zu diesen Timern

Wieso braucht man eigentlich diese Timer-Befehle am Anfang des rules, werden die Timer hier sicherheitshalber initialisiert?

Code: Alles auswählen

var Timer LEDTimer1 = null
var Timer LEDTimer2 = null
Wo wird denn diese timer Liste eigentlich gefüllt bzw. verwendet?

Code: Alles auswählen

var List<Timer> timers = newArrayList
Und nun zu diesem neuen irgendwie geschachtelten Millis Timer - ziemlich geschickt würde ich sagen :idea:
Kann man wohl ohne entsprechende Erfahrung gar nicht drauf kommen!

Also mit der ersten Zeile wird der Timer überhaupt mal erstellt, aber wieso denn nur 10 Millisekunden?
Und dann wird der Timer rescheduled, und ausschalten tut man erst unten mit dem Shutofftimer des gesamten Alarms,
somit läuft das bis der eigentliche Alarm ausgeht, und das ist wiederum flexibel steuerbar mit der Länge des eigentlichen
Alarms (momentan 30s), korrekt?

Code: Alles auswählen

        LEDTimer2 = createTimer(now.plusMillis(10), [ |                                    
            if(LED_GaesteV.state != ON) {                                                  
                LED_GaesteV.sendCommand(ON)                                               
                LEDTimer2.reschedule(now.plusSeconds(3))                       
            } else {                                                                         
                LED_GaesteV.sendCommand(OFF)                                            
                LEDTimer2.reschedule(now.plusSeconds(2))                                
            }
        ] )
und mit dem Fragezeichen fragt man If timer exist then cancel?

Code: Alles auswählen

            LEDTimer2?.cancel 

Re: Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 15:19
von udo1toni
Also, von oben nach unten:

Ich schüttel den Code einfach so aus dem Ärmel. :D Nein, Spaß, ich muss schon etwas drüber nachdenken, abhängig von der Komplexität. ;)

Die "Timer Befehle" zu Beginn der Datei sind keine Befehle, sondern Definitionen. Die Timer müssen außerhalb der Rules definiert werden, damit sie auch außerhalb der Rule Gültigkeit besitzen. Das Schlüsselwort var bedeutet, dass das folgende Statement eine Variable definiert. Es gibt noch das Gegenstück val, welches für die Definition von Konstanten verwendet wird.
In der Definition kann, muss aber nicht, der Typ der Variablen/Konstanten angeben werden. In diesen Fällen also Timer, das heißt, der nachfolgende Bezeichner steht für ein TimerObjekt. Ebenfalls kann, muss aber nicht unbedingt bei der Definition ein konkreter Wert zugewiesen werden (bei Konstanten ist das natürlich ein Muss)

Die Timerliste wird in der Rule durch die Methode timers.add(t) gefüllt. Das geschieht nach der Definition des Timers, weil es sonst echt unübersichtlich wird (man müsste statt .add(t) .add(createTimer(now...,[...])) schreiben, wobei die drei Punkte jeweils für weiteren Code stehen...

Zum Erstellen des Timers für die Blinkfunktion hole ich etwas weiter aus. Man könnte hier beim createTimer() auch direkt now als Zeitpunkt angeben, bei mir hat das bisher auch immer zuverlässig funktioniert, es gab aber Rückmeldungen über Nicht-Funktion. Mit einer kleinen Verzögerung (eben 10 Millisekunden) klappt es sehr zuverlässig.
Der Blinktimer schaltet das Licht immer abwechselnd an und aus. Ich könnte das Licht auch zuerst einschalten und den Timer dann nach drei Sekunden das erste Mal zum Ausschalten starten, aber das bedeutet dann eine unnötige Codeduplikation, denn der Timer soll das Licht ja auch wieder einschalten.
Ich habe für mich festgestellt, dass ein einzelner Timer eine sehr effiziente Möglichkeit bietet, zeitgesteuerte Vorgänge zu automatisieren. Der Blinktimer ist dabei die einfachste Form. Man kann das problemlos zu einer Ablaufsteuerung erweitern, indem man eine globale Variable als Zähler verwendet. Beim initialisieren des Timers wird der Zähler auf 0 gesetzt. Bei jedem Timeraufruf wird die Variable im Timer hochgezählt. Anschließend verzweigt man z.B. mit einer switch...case Anweisung und lässt bei jedem Aufruf unterschiedliche Sachen passieren, wahlweise kann man immer die gleiche Zeit zwischen zwei Durchläufen vergehen lassen, oder man baut den reschedule Befehl mit in das case-Konstrukt und hat individuelle Zeitabstände. Der Timer läuft jeweils nur wenige Millisekunden, man braucht nur eine Rule...

Was das Beenden des Blinkens betrifft, ja, das hast Du richtig erfasst, genau so funktioniert das.
Und auch das mit dem Fragezeichen ist weitgehend korrekt.

Code: Alles auswählen

            LEDTimer2?.cancel 
und

Code: Alles auswählen

            if(LEDTimer2 !== null) LEDTimer2.cancel 
sind gleichbedeutend.

Re: Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 16:49
von Boris099
OK, ich denke ich komme mit diesen Timern in Zukunft nun auch besser klar, die braucht man ja ständig.

Dieses Coding fällt mir und anderen sicher nicht leicht, ich hatte mal etwas Basic auf einem C64 programmiert (dieser Generation gehöre ich an :-) ) und danach mal etwas Fortran77 im Studium, nun muß ich manchmal mit etwas ABAP rumschlagen, aber das hier ist schon eine andere Größenordnung.

So, jetzt setze ich noch meinen Telegram Bot in den Code ein und dann mache ich das mal aktiv, zuerst mal ohne Sirene :-)

Ich denke ich melde mich in ein paar Tagen mal mit einem neuen Thema bzgl. Präsenz oder Geolocation. Ich möchte damit die Anwesenheit oder Annäherung verschiedener Personen (Familie) auswerten und entsprechend schalten oder Heizung runter/hoch regeln... dazu schaue ich mich aber erst mal in den Foren um, was ist denn möglich und was macht Sinn, aber das klingt verlockend!

Re: [SOLVED]Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 19:33
von Boris099
Ich habe doch noch was Kleines bzgl. der Telegram Funktion, die ich zusätzlich einbauen möchte

Code: Alles auswählen

    if(TelegramSenden.state == ON) {                                                            
        val telegramAction = getActions("telegram","telegram:telegramBot:xxxx")				
        telegramAction.sendTelegram("triggeringItem.name")									
    }														
Kann ich den triggeringItem.name da auch einbauen, in der Binding Beschreibung steht da nichts drin?

Edit: Habe auch gerade noch in der openhab community gefragt

Re: [SOLVED]Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 20:18
von udo1toni
Zum Einen möchte ich empfehlen, getAction eher außerhalb der Bedingung zu definieren, ansonsten muss das val mehrfach im Code definiert werden (so wie auch mit der mailAction).
Das Andere ist der Name des triggernden Items. triggeringItem ist ein implizites Objekt, welches innerhalb der Rule zur Verfügung steht (es sei denn, die Rule wurde nicht durch ein Item getriggert, sondern z.B. durch ein Systemereignis oder Time cron).
.name ist eine Eigenschaft dieses Objekts und steht ebenso zur Verfügung, unabhängig von der Stelle im Code. Allerdings dürfen da keine Anführungszeichen drum herum, denn Du möchtest ja keinen Text ausgeben, sondern den Inhalt der Eigenschaft. Die Eigenschaft ist per Definition ein String, man muss also nicht noch extra ein .toString anhängen.

Re: [SOLVED]Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 20:54
von Boris099
Also so?

Code: Alles auswählen

telegramAction.sendTelegram("Movement triggered on", triggeringItem.name.toString)
Edit: Sorry toString nicht notwendig dann also eher

Code: Alles auswählen

telegramAction.sendTelegram("Movement triggered on", triggeringItem.name)

Das mit der getAction hatte ich mir auch schon fast gedacht

Re: [SOLVED]Alarm Rule - Bitte checken ob korrekt?

Verfasst: 28. Feb 2020 21:32
von udo1toni
Genau so :)

Re: [SOLVED]Alarm Rule - Bitte checken ob korrekt?

Verfasst: 29. Feb 2020 17:05
von Boris099
Ich teste gerade und leider löst der Alarm nicht aus,
er meckert im log isAfter wäre nicht korrekt, aber kann das der Grund sein warum der Alarm
überhaupt nicht ausgelöst wird?
Die Bewegungsmelder habe ich zum Testen mit einem Steckdosenswitch gekoppelt, das funktioniert prima

an dieser Stelle steht das isAfter

Code: Alles auswählen

   if(lastRun.isAfter(now.minusMinutes(2))) {                                              
        logInfo("bw_alarm","letzter Alarm vor weniger als 2 Minuten, Rule Ende!")				
        return;																	
    }								
Log dazu
Anmerkung 2020-02-29 164754.gif

Das Senden mit Telegram funktioniert grundsätzlich auch, allerdings ignoriert er den triggeringItem.name
Zum Testen hänge ich mal das toString hinten dran, also so

Code: Alles auswählen

telegramAction.sendTelegram("Movement triggered on", triggeringItem.name.toString)
EDIT: das mit dem TriggeringItem ignoriert er ich bekomme lediglich Movement triggered on
EDIT2: auch wenn ich + triggeringItem.name versuche, ignoriert er das einfach, bei der Mail kommt es mit

Re: [SOLVED]Alarm Rule - Bitte checken ob korrekt?

Verfasst: 29. Feb 2020 17:22
von 03chris
Bei der Telegramaktion fehlt %s

Code: Alles auswählen

telegramAction.sendTelegram("Movement triggered on %s", triggeringItem.name)
und lastRun muss als DateTime type deklariert werden

Code: Alles auswählen

var DateTime lastRun