Absinthe hat geschrieben: ↑2. Okt 2022 23:59
das bedeutet, dass ich keine Variablen innerhalb der Rule aufmachen sollte, wenn ich eine Mischung aus UI-Rule und DSL-Code betreibe?
Nein. Du kannst Variablen und Konstanten nach Belieben anlegen. Nur kannst Du keine globalen Variablen anlegen.
Streng genommen sind die globalen Variablen in *.rules Dateien auch nicht wirklich global. Sie sind auf die Rules beschränkt, welche sich in der gleichen Datei befinden.
Typischer Workaround: Du nutzt statt einer globalen Variablen ein Item. Das funktioniert für Werte, die übergeben werden sollen, aber leider nicht für den Timer, eine Timer Variable enthält einen Zeiger auf einen Eintrag im openHAB Scheduler.
Solange Du eine Variable nur innerhalb einer Rule verwenden willst, gibt es auch die Möglichkeit, den Wert vom letzten Durchlauf erneut zu laden (wobei ich mir nicht sicher bin, ob das in der DSL funktioniert, oder doch nur mit ECMA Code).
Das Item wäre z.B. ein Weg, die bereits generierte Alarmmeldung für den Versand zwischenzuspeichern.
Zwei Rules sollten reichen. Es gäbe dafür verschiedene Ansätze, z.B. auch mit einem weiteren Switch Item (im Beispiel AlarmTimer), welches per expiration Timer ein postUpdate(OFF) sendet.
Rules:
Code: Alles auswählen
configuration: {}
triggers:
- id: "1"
configuration:
groupName: Group_Bewegungsmelder
state: ON
type: core.GroupStateChangeTrigger
actions:
- inputs: {}
id: "2"
configuration:
type: application/vnd.openhab.dsl.rule
script: >
val mySwitch = Group_Bewegungsmelder_ExpirationTimer.members.filter[i|i.name.startsWith(triggeringItem.name)].head
if(mySwitch.state == OFF && (Haus_Alarmanlage_Manuell.state == ON || Group_Anwesenheitsstatus.state == OFF)){
var String ItemNameON = triggeringItem.label.toString
val String strMessage = "ACHTUNG: Bewegung erkannt durch " + ItemNameON
myMessage.postUpdate(strMessage)
AlarmTimer.postUpdate(ON)
mySwitch.postUpdate(ON)
}
type: script.ScriptAction
configuration: {}
triggers:
- id: "1"
configuration:
ItemName: AlarmTimer
state: OFF
type: core.GroupStateChangeTrigger
actions:
- inputs: {}
id: "2"
configuration:
type: application/vnd.openhab.dsl.rule
script: >
if(Haus_Alarmanlage_Manuell.state == ON || Group_Anwesenheitsstatus.state == OFF){
val strMessage = myMessage.state.toString
val mailActions = getActions("mail","mail:smtp:Mail_SMTP")
val pushActions = getActions("pushover", "pushover:pushover-account:test")
mailActions.sendMail("test@test.de", "Nachricht von Zuhause",strMessage)
pushActions.sendPriorityMessage(strMessage,"Nachricht von zuhause",1)
myMessage.postUpdate("")
}
type: script.ScriptAction
Das sind natürlich zwei getrennte Rules, die auch als getrennte Rules über die UI angelegt werden müssen.
Das String Item myMessage speichert die Alarmmeldung, das Item AlarmTimer wird beim Auftreten des Alarms gesetzt, nach Ablauf der hinterlegten Zeit löst es durch das postUpdate(OFF) die zweite Rule aus, welche dann nochmals die Bedingungen abprüft.
In einer DSL Text Rule ginge das alle wesentlich eleganter
wobei das natürlich auch Geschmackssache ist:
Code: Alles auswählen
var Timer tAlarm = null // Zeiger auf Scheduler Eintrag
var String strAlarmItem = "" // Speicher für gemeldete Items
rule "Alarm melden"
when
Member of Group_Bewegungsmelder changed to ON or
Item Haus_Alarmanlage_Manuell changed to OFF or
Item Group_Anwesenheitsstatus changed to ON
then
if(Haus_Alarmanlage_Manuell.state == OFF && Group_Anwesenheitsstatus.state == ON) { // Falls deaktiviert
tAlarm?.cancel // lösche Timer, falls vorhanden
tAlarm = null // lösche den Zeiger
strAlarmItem = "" // entferne den Itemnamen
return; // und brich die Rule ab.
}
if(triggeringItem === null) { // Rule wurde nicht durch Member getriggert
return;
}
val mySwitch = Group_Bewegungsmelder_ExpirationTimer.members.filter[i|
i.name.startsWith(triggeringItem.name)
].head
if(mySwitch.state != OFF){ // wurde bereits alarmiert?
return; // dann Abbruch
}
mySwitch.postUpdate(ON) // Alarmfrequenz begrenzen
if(strAlarmItem != "") // Falls schon ein Alarm besteht
strAlarmItem = strAlarmItem + ", " // ergänze die Aufzählung
strAlarmItem = strAlarmItem + triggeringItem.label // Itemnamen zwischenspeichern
if(tAlarm !== null) // Falls Timer bereits läuft
return; // nicht erneut erzeugen
tAlarm = createTimer(now.plusSeconds(60),[| // Lege Timer an
val String strMessage = "ACHTUNG: Bewegung erkannt durch " + strAlarmItem // Meldetext festlegen
val mailActions = getActions("mail","mail:smtp:Mail_SMTP") // Handle für Mail holen
val pushActions = getActions("pushover", "pushover:pushover-account:test") // Handle für Push holen
mailActions.sendMail("test@test.de", "Nachricht von Zuhause",strMessage) // Mail versenden
pushActions.sendPriorityMessage(strMessage,"Nachricht von zuhause",1) // Push versenden
strAlarmItem = "" // Itemnamen entfernen
tAlarm = null // und Zeiger auf Scheduler entfernen
])
end
Sieht schlimm aus...
Diese Rule kann allerdings schon etwas mehr
Zunächst löst die Rule nicht nur aus, wenn einer der Bewegungsmelder ausgelöst hat, sondern auch, wenn die Schalter für die Scharf Schaltung deaktiviert wurden.
Wird die Rule ausgelöst, so prüft der Code zunächst, ob der Alarm deaktiviert ist. Ist das der Fall, so wir ein eventuell laufender Timer abgebrochen und die Variablen geleert. Anschließend wird die Rule sofort abgebrochen. Das heißt, wenn Du nach Hause kommst, wird der Alarmzustand in dem Moment beendet, wo Dein Smartphone erkannt wurde, nicht erst, wenn der Timer abläuft.
Im nächsten Schritt prüft die Rule, ob triggeringItem null ist. Ist das der Fall, so wurde die Rule durch einen der Schalter ausgelöst und es gibt nichts zu tun (es gab ja keinen Alarm, sondern nur eine Deaktivierung eines Schalters, der andere ist aber noch aktiv, sonst wäre die Rule schon abgebrochen).
Jetzt steht also fest, dass ein Bewegungsmelder ausgelöst hat und auch alarmiert werden soll. Aber darf der Alarm überhaupt weitergereicht werden? das wird nun geprüft, über die zugehörigen Expiration Items, wie gehabt. Ist also der zugehörige Switch ON, kann die Rule wieder abgebrochen werden.
Nun steht endgültig fest, dass es sich um einen zu meldenden Vorfall handelt.
Es könnte nun aber sein, dass bereits ein anderer Alarm auf die Meldung wartet! Dem trägt die Rule Rechnung, indem sie prüft, ob in der Variablen, in der das Itemlabel des Alarms gehalten wird schon etwas steht. Ist das der Fall, so ergänzt sie den String einfach. Auf diese Weise geht kein Name verloren-
Sollte nun der Timer schon existieren, kann die Rule beendet werden.
Nur, wenn noch kein Timer existiert (tAlarm === null) wird der Timer angelegt und die Rule ist fertig.
Wenn der Timer abläuft, werden ohne Wenn und Aber die Meldung raus gegeben und die Variablen geleert.
Einrückungen im Code dienen nur der Lesbarkeit
EDIT: Semikolon hinter return ergänzt...