Regel Lüften Warnung - Timer basierend auf Item

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
technick90
Beiträge: 43
Registriert: 24. Jul 2019 16:56
Answers: 1

Regel Lüften Warnung - Timer basierend auf Item

Beitrag von technick90 »

Hallo,

ich versuche meine Regel zur Warnung bei geöffneten Fenster anzupassen. Die Zeit bis zur Warnung soll nicht mehr fest definiert sein, sondern pro Fenster mittels Number Item gesteuert werden können.

Grundlage ist folgende Regel:

Code: Alles auswählen

import org.openhab.core.model.script.ScriptServiceUtil
import java.util.Map
val Map<String, Timer> contactTimers = newHashMap
val Map<String, Boolean> contactList = newHashMap

rule "Fenster Status"
when
    Member of Fenstersensoren changed
then
    val String itemLabel = triggeringItem.label
    val String itemName = triggeringItem.name
    val String itemNameSperren = itemName.replace("Kontakt","Sperren")
    val GenericItem SperrenItem = ScriptServiceUtil.getItemRegistry?.getItem(itemNameSperren) as GenericItem
    var vName = triggeringItem.name.split("_").get(1)
    var vTimerName = "timer_" + vName
    contactTimers.get(vTimerName)?.cancel;
    if((triggeringItem.state == "Offen") && (SperrenItem.state != ON)) {
        contactList.put(vTimerName,true)
        contactTimers.put(vTimerName, createTimer(now.plusMinutes(13)) [|
            var text = itemLabel + " ist "
            if(contactList.get(vTimerName)) {
                contactList.put(vTimerName,false)
                text = text + "seit "
            } else {
                text = text + "länger als " }
            text = text + "13 Minuten geöffnet"
            AlexaWohnzimmer_Ankuendigung.sendCommand(text)
            AlexaBad_TTS.sendCommand(text)
            AlexaArbeitszimmer_TTS.sendCommand(text)
            AlexaKueche_TTS.sendCommand(text)
            if (Anwesenheit == OFF) {sendBroadcastNotification(text, "contact", "Info")}
			else {
				if (UniFiWirelessClientRobert_Online.state == ON) {sendNotification("abc@abc.de", text, "contact", "Info")}
			}
            contactTimers.get(vTimerName)?.reschedule(now.plusMinutes(5))
        ])
    } 
	else if ((triggeringItem.state == "Offen") && (SperrenItem.state != OFF)) {
        contactList.put(vTimerName,true)
        contactTimers.put(vTimerName, createTimer(now.plusMinutes(1440)) [|
            var text = itemLabel + " ist "
            if(contactList.get(vTimerName)) {
                contactList.put(vTimerName,false)
                text = text + "seit "
            } else {
                text = text + "länger als " }
            text = text + "1 Tag geöffnet"
            AlexaWohnzimmer_Ankuendigung.sendCommand(text)
            AlexaBad_TTS.sendCommand(text)
            AlexaArbeitszimmer_TTS.sendCommand(text)
            AlexaKueche_TTS.sendCommand(text)
            if (Anwesenheit == OFF) {sendBroadcastNotification(text, "contact", "Info")}
			else {
				if (UniFiWirelessClientRobert_Online.state == ON) {sendNotification("abc@abc.de", text, "contact", "Info")}
			}
            contactTimers.get(vTimerName)?.reschedule(now.plusMinutes(10))
        ])
    } 	
	else
        contactTimers.get(vTimerName)?.cancel
end
Nach besten Wissen habe ich die Regel wie folgt angepasst, aber es funktioniert nicht. Meine Programmierkenntnisse sind leider echt rudimentär, daher bin ich trotz zahlreichen Googlen nicht in der Regel die Regel zum Laufen zu bekommen.

Code: Alles auswählen

import org.openhab.core.model.script.ScriptServiceUtil
import java.util.Map
val Map<String, Timer> contactTimers = newHashMap
val Map<String, Boolean> contactList = newHashMap

rule "Fenster Status"
when
    Member of Fenstersensoren changed
then
    val String itemLabel = triggeringItem.label
    val String itemName = triggeringItem.name
    val String itemNameSperren = itemName.replace("Kontakt","SperrenTest")
    val GenericItem SperrenItem = ScriptServiceUtil.getItemRegistry?.getItem(itemNameSperren) as GenericItem
    var vName = triggeringItem.name.split("_").get(1)
    var vTimerName = "timer_" + vName
	var vTimerTime = "13"
    contactTimers.get(vTimerName)?.cancel;
	if (SperrenItem.state instanceof Number) {vTimerTime = SperrenItem.state as Number}
    if(triggeringItem.state == "Offen") {
        logInfo("INFO", "Fenster geöffnet: " + itemName + " seit " + vTimerTime)
		contactList.put(vTimerName,true)
        contactTimers.put(vTimerName, createTimer(now.plusMinutesvTimerTime)) [|
            var text = itemLabel + " ist "
            if(contactList.get(vTimerName)) {
                contactList.put(vTimerName,false)
                text = text + "seit "
            } else {
                text = text + "länger als " }
            text = text + vTimerTime + " Minuten geöffnet"
            AlexaWohnzimmer_Ankuendigung.sendCommand(text)
            AlexaBad_TTS.sendCommand(text)
            AlexaArbeitszimmer_TTS.sendCommand(text)
            AlexaKueche_TTS.sendCommand(text)
            if (Anwesenheit == OFF) {sendBroadcastNotification(text, "contact", "Info")}
			else {
				if (UniFiWirelessClientRobert_Online.state == ON) {sendNotification("abc@abc.de", text, "contact", "Info")}
			}
            contactTimers.get(vTimerName)?.reschedule(now.plusMinutes(5))
        ])
    } 	
	else
        contactTimers.get(vTimerName)?.cancel
end
Es erscheint folgende Fehlermeldung im Log:

Code: Alles auswählen

2025-07-05 16:59:37.035 [ERROR] [.handler.AbstractScriptModuleHandler] - Script execution of rule with UID 'Lueften-test-1' failed: An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.StringExtensions.operator_plus(java.lang.String,java.lang.String) on instance: null in Lueften-test
Kann mir einer helfen? Vielen Dank.

Gruß

Robert

Harka
Beiträge: 492
Registriert: 30. Apr 2021 13:13
Answers: 19

Re: Regel Lüften Warnung - Timer basierend auf Item

Beitrag von Harka »

Moin,
auch ohne Ahnung von Rule-DSL fällt mir in "createTimer(now.plusMinutes(vTimerTime)" eine fehlende Klammer auf. Warum behandelst Du vTimerTime erst als String und dann als Number? Das mögen viele Sprachen nicht.

technick90
Beiträge: 43
Registriert: 24. Jul 2019 16:56
Answers: 1

Re: Regel Lüften Warnung - Timer basierend auf Item

Beitrag von technick90 »

Die fehlende ( ist ein Copy and Paste Fehler. Hatte zum Test Seconds in der Regel und habe es hier im Forum in Minutes geändert. Dabei ist die Klammer gelöscht werden.

Variable habe ich nun wie folgt deklariert:

Code: Alles auswählen

var Number vTimerTime = "13"
Nun erscheint folgende Fehlermeldung.

Code: Alles auswählen

Script execution of rule with UID 'Lueften-test-1' failed: An error occurred during the script execution: Could not invoke method: java.time.ZonedDateTime.plusSeconds(long) on instance: 2025-07-05T22:33:25.335688154+02:00[Europe/Berlin] in Lueften-test
Meine Versuche das zu lösen sind gescheitert. Habe die Variable als int definiert, das Item mit % 0.f formatiert. Aber es bleibt bei dem Fehler.
Ich weiß nicht wie ich das Item korrekt verwende.

Harka
Beiträge: 492
Registriert: 30. Apr 2021 13:13
Answers: 19

Re: Regel Lüften Warnung - Timer basierend auf Item

Beitrag von Harka »

Moin,
versuch mal

Code: Alles auswählen

var Integer vTimerTime = 13
Quelle: viewtopic.php?p=63411#p63411

technick90
Beiträge: 43
Registriert: 24. Jul 2019 16:56
Answers: 1

Re: Regel Lüften Warnung - Timer basierend auf Item

Beitrag von technick90 »

Moin,

das war es wohl nicht, die feste Defintion (Notfall, falls Item nicht gesetzt) hat auch funktioniert.
Habe es dennoch lösen können:

Code: Alles auswählen

if (SperrenItem.state instanceof Number) {vTimerTime = (SperrenItem.state as Number).longValue}
Aber ich glaube ich muss den Namen der vTimerTime Variable auch dynamisch abhängig vom ausgelösten Fenstersensor machen, die Regel gilt ja für die gesamte Gruppe und die Variable wird sonst überschrieben. Das muss ich nochmal testen.
Vielen Dank erstmal.

technick90
Beiträge: 43
Registriert: 24. Jul 2019 16:56
Answers: 1

Re: Regel Lüften Warnung - Timer basierend auf Item

Beitrag von technick90 »

Es scheint zu funktionieren. :-)

Code: Alles auswählen

import org.openhab.core.model.script.ScriptServiceUtil
import java.util.Map
val Map<String, Timer> contactTimers = newHashMap
val Map<String, Boolean> contactList = newHashMap

rule "Fenster Status"
when
    Member of Fenstersensoren changed
then
    val String itemLabel = triggeringItem.label
    val String itemName = triggeringItem.name
    val String itemNameSperren = itemName.replace("Kontakt","SperrenTime")
    val GenericItem SperrenItem = ScriptServiceUtil.getItemRegistry?.getItem(itemNameSperren) as GenericItem
    var vName = triggeringItem.name.split("_").get(1)
    var vTimerName = "timer_" + vName
	var Integer vTimerTime = 13
    contactTimers.get(vTimerName)?.cancel;
    if(triggeringItem.state == "Offen") {
		if (SperrenItem.state instanceof Number) {vTimerTime = (SperrenItem.state as Number).longValue}
        logInfo("INFO", "Fenster geöffnet: " + itemName + " seit " + vTimerTime)
		contactList.put(vTimerName,true)
        contactTimers.put(vTimerName, createTimer(now.plusMinutes(vTimerTime)) [|
            var text = itemLabel + " ist "
            if(contactList.get(vTimerName)) {
                contactList.put(vTimerName,false)
                text = text + "seit "
            } else {
                text = text + "länger als " }
            text = text + vTimerTime + " Minuten geöffnet"
			AlexaWohnzimmer_Ankuendigung.sendCommand(text)
            AlexaBad_TTS.sendCommand(text)
            AlexaArbeitszimmer_TTS.sendCommand(text)
            AlexaKueche_TTS.sendCommand(text)
            if (Anwesenheit == OFF) {sendBroadcastNotification(text, "contact", "Info")}
			else {
				if (UniFiWirelessClientRobert_Online.state == ON) {sendNotification("abc@abc.com", text, "contact", "Info")}
			}
            contactTimers.get(vTimerName)?.reschedule(now.plusMinutes(5))
        ])
    } 	
	else
        contactTimers.get(vTimerName)?.cancel
end

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

Re: Regel Lüften Warnung - Timer basierend auf Item

Beitrag von udo1toni »

Tipp:
Du kannst (sofern die Contact Items in einer geeigneten Form persistiert werden) den Zeitpunkt der letzten Änderung abrufen, sprich, wann das Fenster geöffnet wurde. Die Contact Items befinden sich bereits in einer Gruppe, genauso könnten die Items für die maximale Öffnungsdauer in einer Gruppe zusammengefasst werden - damit ersparst Du Dir den Zugriff auf die ItemRegistry.

Dann könntest Du eine Rule schreiben, welche, sobald mindestens eines der Fenster geöffnet ist, alle (z.B.) 5 Minuten aufgerufen wird. Die Rule kann dann für alle geöffneten Fenster (Fenstersensoren.members.filter[f|f.state.toString == "Offen"].forEach[i|...] ) prüfen, ob die maximal erlaubte Öffnungszeit überschritten wurde (i.lastChange(true) ist der Zeitstempel der letzten Änderung, Sperrgruppe.members.filter[s|s.name.contains(i.name.split("_").get(1))].head gibt das erste Item in der Gruppe Sperrgruppe zurück, welches im Intemnamen die gleiche Zeichenfolge enthält, wie das aktuell abgearbeitete Item zwischen erstem und zweitem Unterstrich).
So kannst Du bei geöffnetem Fenster alle 5 Minuten eine Liste aller Fenster ausgeben, für die der Alarm aktuell zutrifft, nicht nur mit der Info, ob sie seit "Sperrmax" Minuten oder länger geöffnet sind, sondern gegebenenfalls auch, wie lang exakt oder seit wann genau, separat für jedes Fenster, aber eben nur eine Meldung alle 5 Minuten für alle Fenster, statt im Extremfall innerhalb weniger Sekunden mehrere Meldungen, eventuell auch überlappend.

Beispiel (ungetestet):

Code: Alles auswählen

var Timer tFensteralarm = null

rule "Fenster Status"
when
    Member of Fenstersensoren changed
then
    if(tFensteralarm !== null && Fenstersensoren.members.filter[s|s.state.toString == "Offen"].size == 0) {
        tFensteralarm.cancel
        tFensteralarm = null
    } else if(tFensteralarm === null && Fenstersensoren.members.filter[s|s.state.toString == "Offen"].size > 0) {
        tFensteralarm = createTimer(now.plusMinutes(10), [|
            tFensteralarm.reschedule(now.plusMinutes(5))
            val StringBuilder sb = new StringBuilder
            sb.append("Folgende Fenster sind zu lange geöffnet: ")
            Fenstersensoren.members.filter[s|s.state.toString == "Offen"].forEach[i|
                val tTimestamp = i.lastChange()
                val strID = i.name.split("_").get(1)
                val iTimeout = (Sperrgruppe.members.filter[s|s.name.contains(strID)].head.state as Number).intValue 
                if(tTimestamp.plusMinutes(iTimeout).isBefore(now)) {
                     sb.append(i.label + " ")
                }
            ]
            val text = sb.toString
            if(text.length > 42) {
                AlexaWohnzimmer_Ankuendigung.sendCommand(text)
                AlexaBad_TTS.sendCommand(text)
                AlexaArbeitszimmer_TTS.sendCommand(text)
                AlexaKueche_TTS.sendCommand(text)
                if(Anwesenheit == OFF)
                    sendBroadcastNotification(text, "contact", "Info")
                else if(UniFiWirelessClientRobert_Online.state == ON)
                    sendNotification("abc@abc.com", text, "contact", "Info")
            }
        ])
    }
end
Die Rule selbst macht nichts anderes, als den Timer anzulegen, falls mindestens ein Fenster geöffnet ist und es noch keinen Timer gibt bzw. beim Schließen des letzten Fensters den Timer wieder zu entfernen.

Der Timer wird das erste Mal nach 10 Minuten ausgeführt. Sollte ein Fensteralarm nach weniger als 10 Minuten gewünscht sein, muss diese Zeit entsprechend verkürzt werden.

Wird der Timercode ausgeführt, so wird zunächst der Timer erneut geplant und anschließend ein StringBuilder initialisiert (funktioniert ähnlich wie "text = text + "...").
Danach werden für jeden offenen Kontakt die Zeitstempel ausgelesen und die Maximaldauer addiert. Ist das Ergebnis vor der aktuellen Zeit, so wird der Stringbuilder Variablen das Label des Items hinzugefügt.

Abschließend werden die Meldungen ausgegeben, falls es etwas zu melden gibt (hier habe ich geprüft, ob die auszugebende Zeichenkette länger ist als der Initialtext).

Das reschedule zu Beginn des Timercodes ist kontraintuitiv :) hat aber den Vorteil, dass die Laufzeit des enthaltenen Codes den Abstand zwischen den Ausführungszeitpunkten nicht beeinflusst. reschedule() setzt lediglich den Zeitstempel im Scheduler und hat keinen Einfluss auf den aktuell durchlaufenen Code.
plusMinutes() erwartet als Parameter long oder int, hier dürfte int reichen :)
tTimestamp enthält einen Zeitstempel mit Zeitzoneninformation, ich war jetzt gerade zu faul, passende Umwandlungen rauszusuchen, um die verstrichene Zeit zu bestimmen, lässt sich aber relativ leicht bewerkstelligen ;) um dann den Text mit weiteren Infos anzureichern.

Die Abfrage if(Anwesenheit == OFF) ist eventuell falsch, zumindest, falls es sich bei Anwesenheit um ein Item handelt (dann müsste es Anwesenheit.state sein).
Weiterhin scheint es unsinnig, Texte über Alexa auszugeben, wenn niemand Zuhause ist ;)
openHAB4.3.5 stable in einem Debian-Container (bookworm) (Proxmox 8.4.1, LXC), mit openHABian eingerichtet

Antworten