Zeitliche Fensterüberwachung für viele Fenster

Für welche Projekte verwendet Ihr OpenHAB? Was habt Ihr automatisiert? Stellt eure Projekte hier vor.

Moderatoren: Cyrelian, seppy

Antworten
stutmich
Beiträge: 14
Registriert: 4. Mär 2023 17:43
Answers: 0

Zeitliche Fensterüberwachung für viele Fenster

Beitrag von stutmich »

Hallo zusammen,

ich habe folgenden Usecase, kann ihn aber nicht effektiv umsetzen.

Ich möchte eine Erinnerung z.B. per Telegram haben, wenn ein Fenster/Tür länger als ein bestimmte Zeit offen ist. Z.B. nach 10 Minuten.
Für ein Fenster funktioniert es wie folgt:

Ich habe einen "FensterKontakt" und eine virtuellen Schalter "FensterKontakt_Zeit". Öffne ich das Fenster, starte ich einen Timer von 10 Minuten, der dann die Variable FensterKontakt_Zeit einschaltet. Wenn nach den 10 Minuten der FensterKontakt_Zeit einschaltet, prüfe ich ob FensterKontakt immernoch OPEN ist, wenn ja gibt es ein Meldung, wenn nicht, gibt es keine Meldung und der FensterKontakt_Zeit wird wieder auf OFF geschaltet.
Wird das Fenster nach z.b. 15 Minuten geschlossen, wird eine Meldung ausgelöst "Fenster wieder geschlossen".

Soweit funktioniert das sehr gut. Ich habe aber deutlich mehr als 15 dieser Sensoren und möchte nur ein Script haben.

Idee:
Ich gruppiere alle Fenster/Tür Kontakte in einer Gruppe FensterCheckGroup und alle Zeit Variablen (die den gleichen Namen haben wie der FensterKontakt + "_ZEIT") in der Gruppe FensterCheckGroup_ZEIT.
Über ein oder zwei Rules möchte ich indirekt über den Member die entsprechenden Kontakte abfragen.
Das klappt auch ganz gut, aber für die Zeitverzögerung nehme ich eine Lambda Funktion, die keine Indirekten Objekte erlaubt.

Hier hänge ich im Moment.

Script bisher ohne Gruppe für ein Fenster:

Code: Alles auswählen

var Timer tKueche = null

rule "Timer_Fenster_Kueche"
when
   Item ZW_WZ_Kueche_Kontakt_Door_Sensor changed
then
    tKueche?.cancel // Falls Timer definiert, Timer entfernen
    if(ZW_WZ_Kueche_Kontakt_Door_Sensor.state == OPEN) {
        tKueche = createTimer(now.plusSeconds(10),[|  ZW_WZ_Kueche_Kontakt_Door_Sensor_ZEIT.sendCommand(ON)])
    } else {
      tKueche?.cancel
      if (ZW_WZ_Kueche_Kontakt_Door_Sensor_ZEIT.state == ON) {
         ZW_WZ_Kueche_Kontakt_Door_Sensor_ZEIT.sendCommand(OFF)
         val telegramAction = getActions("telegram","telegram:telegramBot:1cXXXXXXX80")
         telegramAction.sendTelegram("Fenster Kueche - wieder zu")
      }
   }
end
  
rule "Activate Timer_Fenster_Kueche"
when
   Item ZW_WZ_Kueche_Kontakt_Door_Sensor_ZEIT received command ON
      // this only happens if timer runs to end
then
   val telegramAction = getActions("telegram","telegram:telegramBot:1cXXXXXXX80")
   telegramAction.sendTelegram("Fenster Kueche - zu lange offen")
end  
der neue Ansatz:

Code: Alles auswählen

var Timer tKueche = null

rule "FensterCheck X"
 when
    Member of FensteCheckGroup changed
 then

    val mode = triggeringItem.state
    val iName = triggeringItem.name
    logInfo("FensterCheck","Name is: {}, Mode is: {}",iName,mode)  

    if (mode==OPEN) {
        var itemName = FensteCheckGroup_Zeit.members.findFirst[ t | t.name == (iName + "_ZEIT") ] as GenericItem //get target item
        logInfo("FensterCheck für ZEIT","Name is: {}",itemName)  
        tKueche = createTimer(now.plusSeconds(10),[|  itemName.sendCommand(ON)])  ==> Hier ist der erste Fehler vom compiler
    } else {   // mode CLOSE
      //  var itemName = FensteCheckGroup_Zeit.members.findFirst[ t | t.name == (iName + "_ZEIT") ] as GenericItem //get target item
    }
end
Wie ich das Thema mit den vielen Timern ("tKueche" ,... löse, weiß ich noch nicht, bin vorher schon gestolpert :? )

Vielen Dank für eure Ideen.

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

Re: Zeitliche Fensterüberwachung für viele Fenster

Beitrag von udo1toni »

Es gibt verschiedene Ansätze dazu. Eine Möglichkeit wäre ein Timer pro Fenster. Du kannst sie einzeln anlegen (wobei Du irgendeine Verbindung zwischen den Items und den globalen Variablen vornehmen musst, und (etwas unangenehmer) Du musst irgendwie die Timer in der passenden Variablen speichern.
Besser: Du legst ein Array an, welches die Timer aufnimmt. Das Blöde dabei: es ist nicht eben komfortabel zu händeln.

Ein komplett anderer Ansatz: Du persistierst die Fensterkontakte (unbedingt mit everyChange, nicht über mapDB, rrd4j ist evtl. nicht gut geeignet). Sobald einer der Fensterkontakte der Gruppe offen anzeigt, wird ein Timer gestartet, der (z.B.) einmal pro Minute für alle "offenen" Fenster prüft, wann sich der Zustand das letzte Mal geändert hat.

Nachteil: Je nach gewählter Länge des Timers kann die Meldung entsprechend verspätet kommen.
Also z.B. : Es ist bereits ein Fenster offen -> der Timer zündet alle 60 Sekunden. Eine Sekunde, nachdem der Timer ausgeführt wurde, wird ein zweites Fenster geöffnet. Das erste Fenster wird wieder geschlossen (Der Timer wird aber weiterhin ausgeführt, weil ja weiterhin ein Fenster geöffnet ist). Der Timer wird also ausgeführt, wenn das zweite Fenster 9:59 Minuten offen ist -> kein Alarm. Das nächste Mal wird der Timer eine Minute später ausgeführt, da ist das Fenster aber schon 10:59 Minuten offen.

Vorteil: simpel zu realisieren:

Code: Alles auswählen

var Timer tFenster = null

rule "ein Timer für alle"
when
    Member of gFenster changed
then
    if(gFenster.members.filter[f|f.state != CLOSED].size == 0) {
        tFenster?.cancel
        tFenster = null
    } else if(tFenster === null)
        tFenster = createTimer(now.plusMinutes(1),[|
            var strNamen = ""
            gFenster.members.filter[f|f.state != CLOSED].forEach[i|
                if(!i.changedSince(now.minusMinutes(10),true)) {
                    logInfo("alarm","Fenster {} mehr als 10 Minuten geöffnet",i.name)
                    strNamen += i.name + ", "
                }
            ]
            logInfo("alarm","als Liste: {}",strNamen)
            tFenster.reschedule(now.plusMinutes(1))
        ])
end
Statt des logInfo Befehls kannst Du natürlich auch beliebige andere Befehle einbauen, wahlweise einzeln (oberer logInfo Befehl) oder auch als Liste (unterer logInfo Befehl).
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

stutmich
Beiträge: 14
Registriert: 4. Mär 2023 17:43
Answers: 0

Re: Zeitliche Fensterüberwachung für viele Fenster

Beitrag von stutmich »

Danke für deine Ideen.
So ganz durchsteige ich die Idee mit dem einzelnen Timer nicht. Wenn die Zeit abgelaufen ist und ich z.B. die Haustür danach wieder zumache, bekomme ich nicht raus ob sie innerhalb der 10min oder danach geschlossen wurde.

Mir ist beim Durchlesen von deiner Idee eine Andere gekommen.
Den Timer jede Minute laufen lassen und alle relevanten Fenster prüfen ist super. Das spart sogar den/die Timer selbst.
Ich könnte sozusagen für jeden Kontakt in den MetaDaten des items einen Counter hochzählen, bzw. zurücksetzen. (es ist nicht wichtig, dass es genau 10 min sind)

Mit MetaDaten habe ich noch nichts gemacht - kann man sie dafür verwenden?
Ich müsste auf ein Datum nur schreibend zugreifen können.

Grüße
Micha

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

Re: Zeitliche Fensterüberwachung für viele Fenster

Beitrag von udo1toni »

Wie gesagt, die Rule funktioniert anders.

Solange mindestens ein Fenster (oder Tür) geöffnet ist (gFenster.members.filter[f|f.state != CLOSED].size > 0)) wird der Timer angelegt, falls er noch nicht existiert.
Der Timer wird einmal pro Minute ausgeführt.
Innerhalb des Timer Codes wird für alle geöffneten Fenster (gFenster.members.filter[f|f.state != CLOSED].forEach[i|) geprüft, ob die letzte Statusänderung mehr als zehn Minuten her ist (if(!i.changedSince(now.minusMinutes(10),true)))
In diesem Fall (das Fenster war das letzte Mal vor mehr als Zehn Minuten geschlossen) wird eine Meldung ausgegeben.

Die Rule kümmert sich noch nicht darum, dass ein Fenster bereits gemeldet wurde, das wäre aber durchaus leicht zu implementieren.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

stutmich
Beiträge: 14
Registriert: 4. Mär 2023 17:43
Answers: 0

Re: Zeitliche Fensterüberwachung für viele Fenster

Beitrag von stutmich »

So habe ich es besser verstanden. Das klingt auf jeden Fall sehr einfach und ausreichend.
Ich werde das so ausprobieren und melde mich.

stutmich
Beiträge: 14
Registriert: 4. Mär 2023 17:43
Answers: 0

Re: Zeitliche Fensterüberwachung für viele Fenster

Beitrag von stutmich »

Lange hatte ich keine Zeit, habe mich jetzt aber wieder um das Thema gekümmert.
Der Ansatz udo1toni war gut, hat aber den Nachteil, das ich nicht weiß ob ein bestimmtes Fenster wieder zu geht.
Ich habe daher eine HashMap benutzt, das geht jetzt gut:

Hier mein Code falls jemand eine ähnliche Lösung sucht:

Code: Alles auswählen

import java.util.HashMap
import org.openhab.core.model.script.ScriptServiceUtil

var HashMap <String, Integer> FensterStatus = new HashMap <String, Integer>

rule "FensterCheck All"
when
    Member of FensteCheckGroup changed or
    Time cron "0 0/1 * * * ? *" 
then
   
    val telegramAction = getActions("telegram","telegram:telegramBot:1cxxxxxxx80")
    
        if (triggeringItem !== null) {
            logInfo("FensterStatus","TriggerItem === null")
            var xxxx = FensterStatus.get(triggeringItem.name)
            if (triggeringItem.state == OPEN) {
                if (xxxx==null) {
                    FensterStatus.put(triggeringItem.name,1)
                } else {
                    FensterStatus.put(triggeringItem.name,xxxx+1)  // wie put, Eintrag wird ersetzt
                }
             //nur ur info am Anfang verwendet
           /*
                for (String i : FensterStatus.keySet()) {
                        logInfo("MapInfo","key: " + i + " value: " + FensterStatus.get(i).toString());
                    }
          */
            } else {
                //fenster/tuer wieder zu
                if (xxxx != null) {
                    if (xxxx>=10) {
                        logInfo("FensterStatus",triggeringItem.name + "wieder zu")
                        telegramAction.sendTelegram(triggeringItem.label + " wieder zu.")
                    }
                    FensterStatus.remove(triggeringItem.name)
                }

            }
        } else {
            //timer kommt (keinFenster)
         
            for (String i : FensterStatus.keySet()) {
              //          logInfo("MapInfo","key: " + i + " value: " + FensterStatus.get(i).toString());
                        val updateItem = ScriptServiceUtil.getItemRegistry.getItem(i)
                        var xxxx = FensterStatus.get(updateItem.name)
                        FensterStatus.put(updateItem.name,xxxx+1)
                        if (xxxx>=10 && xxxx<11) {
                            logInfo("FensterStatus",updateItem.name + " zu lange auf")
                            telegramAction.sendTelegram(updateItem.label + " zu lange auf.")
                        }
            }
       
    }
end

Antworten