Seite 1 von 2

Rule für Lüften Warnung basierend auf Änderung der Gruppenmitglieder

Verfasst: 11. Jan 2023 19:23
von technick90
Hallo,

meine Rule für die Warnung zum Lüften (Fenstersensoren) bestand bisher aus einer Regel pro Sensor.

Das habe ich nun deutlich abgekürzt.

Code: Alles auswählen

rule "Fenster Status"
when                                          
  Member of Fenstersensoren changed                      
then 
val String itemLabel = triggeringItem.label.toString
val String itemName = triggeringItem.name.toString
val String itemNameSperren = itemName.replace("Kontakt","Sperren")
var Number nCount = 0 
var Timer nTimer = null
  if (triggeringItem.state == "Offen") {
      nCount = 0
	  nTimer = createTimer(now.plusMinutes(13), [|              
		nCount = nCount + 1
		if (nCount == 1) 
			{
			AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist seit 13 Minuten geöffnet")
			logInfo(itemNameSperren)
			}
        if (nCount > 1)
			{
			AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist länger als 13 Minuten geöffnet")
			}
		nTimer.reschedule(now.plusMinutes(5))
      ])                                                         
  }
  else {
	nTimer.cancel
	nTimer = null
   }
end
Jedoch habe ich ein Item um die Regel außer Kraft zu setzen.

Das war in der alten Rule wie folgt berücksichtigt:

Code: Alles auswählen

rule "Wohnzimmer-Fenster Status"
when                                          
  Item MQTTSensorWohnzimmerTerrasse_Kontakt changed                        
then          
  if ( (MQTTSensorWohnzimmerTerrasse_Kontakt.state == "Offen") && (MQTTSensorWohnzimmerTerrasse_Sperren.state != ON) ) {
      nCountWohnzimmerFenster = 0
	  nTimerWohnzimmerFenster = createTimer(now.plusMinutes(13), [|              
		nCountWohnzimmerFenster = nCountWohnzimmerFenster + 1
		if (nCountWohnzimmerFenster == 1) 
			{
			AlexaWohnzimmer_TTS.sendCommand("Terassentür im Wohnzimmer ist seit 13 Minuten geöffnet")
			}
        if (nCountWohnzimmerFenster > 1)
			{
			AlexaWohnzimmer_TTS.sendCommand("Terassentür im Wohnzimmer ist länger als 13 Minuten geöffnet")
			}
		nTimerWohnzimmerFenster.reschedule(now.plusMinutes(5))
      ])                                                         
  }
  else {
	nTimerWohnzimmerFenster.cancel
	nTimerWohnzimmerFenster = null
   }
end
Den Namen des Items zum Sperren ermittle ich bereits in meiner Rule. Aber das ist dann ein Stringobjekt, mit dem kann ich nicht den Wert des entsprechenden Items abrufen.
Wie kann ich also den in der Variable gespeicherten Namen des Item verwenden um für diesen Item den Wert (state) zu ermitteln?

Gruß

Robert

Re: Status eines Items basierend auf Variable

Verfasst: 11. Jan 2023 19:45
von technick90
Ich habe fleißig weiter gegoogelt und die Lösung selbst gefunden.

Code: Alles auswählen

import org.openhab.core.model.script.ScriptServiceUtil

rule "Fenster Status"
when                                          
  Member of Fenstersensoren changed                      
then 
val String itemLabel = triggeringItem.label.toString
val String itemName = triggeringItem.name.toString
val String itemNameSperren = itemName.replace("Kontakt","Sperren")
val GenericItem SperrenItem = ScriptServiceUtil.getItemRegistry?.getItem(itemNameSperren) as GenericItem

var Number nCount = 0 
var Timer nTimer = null
  if ((triggeringItem.state == "Offen") && (SperrenItem.state != ON)) {
      nCount = 0
	  nTimer = createTimer(now.plusMinutes(13), [|              
		nCount = nCount + 1
		if (nCount == 1) 
			{
			AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist seit 13 Minuten geöffnet")
			}
        if (nCount > 1)
			{
			AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist länger als 13 Minuten geöffnet")
			}
		nTimer.reschedule(now.plusMinutes(5))
      ])                                                         
  }
  else {
	nTimer.cancel
	nTimer = null
   }
end
Es kann natürlich trotzdem mal jemand über das Script schauen. Denn z. B. beim Schließen gibt es einen Fehler im Log wegen nTimer.cancel.

Re: Status eines Items basierend auf Variable

Verfasst: 11. Jan 2023 21:40
von technick90
Ich korrigiere. Die Rule funktioniert noch nicht wie gewünscht.
Die Geschichte mit dem Timer funktioniert in dieser Konstellation wohl nicht wie gewünscht.
Für heute ist mein Kopf dicht.

Re: Status eines Items basierend auf Variable

Verfasst: 12. Jan 2023 02:52
von udo1toni
Dein Problem besteht darin, dass Du die Variable, in der Du den Zeiger auf den Timer speicherst, lokal definierst. Damit ist die Variable nicht mehr gültig, wenn die Rule beendet ist (das ist nach ein paar Millisekunden, nicht erst, wenn der Timer abläuft).

Der Weg über die Itemregistry ist eine Möglichkeit, das passende Item zu finden, Du kannst aber auch eine Group nutzen, in der Du alle betroffenen Items versammelst. Dann kannst Du mit

Code: Alles auswählen

GroupItem.members.filter[i|i.name == derberechnetestring].head
auf genau dieses Item als Item zugreifen.
.members liefert eine Liste aller Member von GroupItem. .filter[] liefert eine Liste (!) aller Items, auf die die angegebene(n) Bedingung(en) gilt/gelten.
.head schließlich liefert das erste Element der Liste, und zwar als natives Element, also in diesem Fall, da es eine Liste von Items ist, als Item. Du kannst diesen Bandwurm einfach einer Variablen zuweisen, um nacheinander auf verschiedene Aspekte des Items zuzugreifen, also z.B. .name, .state und vielleicht noch ein .sendCommand()

Re: Status eines Items basierend auf Variable

Verfasst: 12. Jan 2023 17:29
von technick90
Habe mich gerade noch zu global scope und local scope belesen. In meiner alten Rule habe ich die Variablen am Anfang definiert, dadurch ist es dann wohl global scope.
Dort gab es aber eine Variable pro Fenster, Beispiel:

Code: Alles auswählen

var Number nCountWohnzimmerFenster = 0
var Number nCountBadFenster = 0  
var Timer nTimerWohnzimmerFenster = null 
var Timer nTimerBadFenster = null  
Das möchte ich bei der neuen rule natürlich nicht mehr.
Wenn ich jetzt aber nur nTimer und nCount als global scope Variable definiert wird doch der Wert bei 2 gleichzeitig geöffneten Fenstern überschrieben oder nicht?
Anders formuliert, beim 2. geöffneten Fenster wird die Regel ja ein 2. Mal ausgeführt und überschreibt die Variable oder nicht?
Oder ordnet er die Variable und deren Wert trotzdem der Laufzeit jedes einzelnen Scripts zu?

Re: Status eines Items basierend auf Variable

Verfasst: 12. Jan 2023 17:42
von udo1toni
So genau hatte ich mir Deine Rule gar nicht angeschaut... Wenn Du mehrere voneinander unabhängige Timer brauchst, musst Du die als Array definieren und in der Rule jeweils den passenden Timer ermitteln.

Re: Status eines Items basierend auf Variable

Verfasst: 12. Jan 2023 21:49
von technick90
Das mit dem Timer klappt nun.

Code: Alles auswählen

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

rule "Fenster Status"
when                                          
  Member of Fenstersensoren changed                      
then 
val String itemLabel = triggeringItem.label.toString
val String itemName = triggeringItem.name.toString
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)) {
	  contactTimers.put(vTimerName, createTimer(now.plusMinutes(13)) [|             
			AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist seit 13 Minuten geöffnet")
      ])     
	}
  else {
contactTimers.get(vTimerName)?.cancel
   }
end
Nun fehlt mir aber noch der Count. Der wird benötigt um nach dem ersten Hinweis (hier 13) Minuten den Timer neu zu starten und nach erneuten Ablauf einen geänderten Text zu wiedergeben.
Sprich nach 13 Minuten soll die Meldung kommen: Fenster ist seit 13 Minuten geöffnet.
Nach 18 Minuten soll die Meldung kommen: Fenster ist länger als 13 Minuten geöffnet.

Ich weiß das ich das auch mit einem Array lösen muss, ich weiß aber nicht wie.

Re: Status eines Items basierend auf Variable

Verfasst: 13. Jan 2023 00:28
von udo1toni
Na ja, Du musst Dir halt genau wie beim Timer eine globale Map anlegen. Innerhalb des Lambda (der Code zwischen [| und ] musst Du zum einen prüfen, ob der passende Eintrag vorhanden ist, zum anderen musst Du je nach Ergebnis Deine Ausgabe abwandeln. Außerdem musst Du, wenn der Timer zum ersten Mal abläuft, den Timer per reschedule erneut ausführen lassen.

Re: Status eines Items basierend auf Variable

Verfasst: 13. Jan 2023 16:05
von technick90
Den Gedanken verstehe ich, aber trotz zahlreichen Googeln muss ich gestehen das ich es nicht hinbekomme.

Kann mir jemand mein Script entsprechend anpassen?

Re: Status eines Items basierend auf Variable

Verfasst: 13. Jan 2023 17:25
von udo1toni
Na hier:

Code: Alles auswählen

contactTimers.get(vTimerName)?.cancel;

  if ((triggeringItem.state == "Offen") && (SperrenItem.state != ON)) {
	  contactTimers.put(vTimerName, createTimer(now.plusMinutes(13)) [|             
			AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist seit 13 Minuten geöffnet")
      ])     
	}
  else {
contactTimers.get(vTimerName)?.cancel
   }
end
ergänzen...

Code: Alles auswählen

contactTimers.get(vTimerName)?.cancel;
if((triggeringItem.state == "Offen") && (SperrenItem.state != ON)) {
    contactList.put(vTimerName,true)
    contactTimers.put(vTimerName, createTimer(now.plusMinutes(13)) [|             
        if(contactList.get(vTimerName)) {
            contactList.put(vTimerName,false)
            AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist seit 13 Minuten geöffnet")
            contactTimers.get(vTimerName)?.reschedule(now.plusMinutes(5))
        } else {
            AlexaWohnzimmer_Ankuendigung.sendCommand(itemLabel + "ist seit mehr als 13 Minuten geöffnet")
        }
    ])     
} else {
    contactTimers.get(vTimerName)?.cancel
}
end
Und natürlich musst Du contactList global als Map anlegen, analog zu contactTimers.

Falls Du wiederkehrend alls 5 Minuten eine Warnung haben möchtest, musst Du das reschedule einfach außerhalb des if-Blocks verschieben, so dass es immer ausgeführt wird.