Benutzerdefinierte Aktion erstellen

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
alex200580
Beiträge: 6
Registriert: 17. Jun 2019 11:14
Answers: 0

Benutzerdefinierte Aktion erstellen

Beitrag von alex200580 »

Guten Morgen,

ich benötige Hilfe bei folgendem Vorhaben:

Ich möchte aus verschiedenen .rules Dateien heraus eine Aktion starten können. Genauer gesagt sollen Durchsagen auf verschiedenen Abspielgeräten ausgeführt werden. Ich möchte in den .rules Dateien also eine Aktion wie z.B.

Durchsage('Wohnzimmer', 'Die Luftfeuchtigkeit ist zu hoch, bitte lüften!') verwenden.

(Analog zu z.B. sendTelegram("Bot","Text"); oder sendNotifiaction("User","Text")

mit dem ersten String gebe ich den Player an, mit dem zweiten den Durchsagetext. Mit der Aktion wird dann erst die Uhrzeit geprüft, (nächtliche Durchsagen sind nicht so cool), dann wird auf dem betreffenden Gerät ein Gong abgespielt, 2 Sekunden später dann der Text durchgesagt.
Aktuell führe ich das alles in den vielen Regeln separat durch. Um die Regeln zu vereinfachen und übersichtlicher zu gestallten möchte ich das mit der "benutzerdefinierten Aktion" lösen. Ich habe bereits versucht das mit einer Procedure zu lösen, ohne Erfolg. Auch ein Script kommt nicht in Frage, da man keine Argumente übergeben kann.

Hat jemand einen Lösungsansatz?

Ich verwende OH2.5.4 Release Build auf einem WIN 10 Rechner

Schon mal danke für eure Ideen,

Alex
von udo1toni » 10. Mai 2020 23:38
Also das eine ist der Unterschied zwischen genericItem und String. das genericItem ist eben genau das: ein Item. Wenn Du ein Item übergibst, musst Du natürlich auch die Eigenschaft .name abfragen.

Das Andere ist der Wunsch, die Funktion dateiübergreifend zu verwenden. Vermutlich ist es dafür das Einfachste, eine Rule zu programmieren (und eben kein Lambda) und diese Rule gezielt zu triggern, nachdem Du die benötigten Parameter abgeladen hast. Es böte sich an, das über eine Gruppe zu erledigen.
Die Items:

Code: Alles auswählen

Group gAnsage
String ansage_Kalli (gAnsage)
String ansage_WZ (gAnsage)
Ein Label brauchen wir hier nicht, denn die Items müssen nirgendwo angezeigt werden.
Eine Rule:

Code: Alles auswählen

// Globale Variablen zu Beginn der Datei definieren!
var Timer tPause = null
var String strTarget = ""
var String strText = ""

rule "Ansage mit Gong" 
when
    Member of gAnsage received update
then
    if(Mute.state==ON) return;
    
    strTarget = "chromecast:chromecast:GoogleHomeMini" + triggeringItem.name.split("_").get(1)
    strText = triggeringItem.state.toString
    playSound(strTarget,"Metal_Gong.mp3")
    tPause?.cancel
    tPause = createTimer(now.plusSeconds(2), [|
        say(""+strText ,"googletts:deDEStandardB" ,strTarget)
    ])
end
Nun benutzt Du einfach immer ein ansage_Kalli.postUpdate("Text") bzw. ansage_WZ.postUpdate("Text").
Die Rule triggert in beiden Fällen. Zuerst prüft die Rule, ob Mute ON ist. Ist das der Fall, beendet sich die Rule.
Anschließend füllt sie die Variable strTarget mit dem korrekten String.
Die letzten Zeichen entsprechen dabei dem Teil des Itemnamens nach dem _
In die globale Variable strText kommt der zu sprechende Text.
Nun spielt die Rule den Gong auf dem passenden Gerät aus und legt die Pause ein.
Sobald der Timer abgelaufen ist, folgt der say-Befehl auf das passende Device.
Wichtig ist hier natürlich, dass die beiden Strings vorher in globale Variablen gerettet werden, damit sie im Timer zur Verfügung stehen. Man könnte auch speichern, welches der Geräte eine Ansage machen soll und im Timercode entsprechend verzweigen, das ist aber umständlicher.

Es gibt natürlich auch einen Pferdefuß... Es darf immer nur eine Meldung auf einen Player ausgegeben werden, es müssen immer mindestens 2 Sekunden (und ein paar hundertstel) vergehen, bis der Nächste Player oder die nächste Meldung ausgegeben werden kann.
Wenn es eine solche Einschränkung nicht geben darf, wird die Rule wesentlich komplexer, da dann die Texte in ein globales Array gerettet werden müssen, welches für jeden Player einen Slot und einen Timer bereithält. Über dieses Array muss der Timer dann entscheiden, für welchen Slot er zuständig ist (das heißt, im Array muss auch ein Zeitstempel mit rein) um dann entsprechend auszuspielen. Der Aufwand wird erheblich sein, vermutlich ist es dann einfacher, für jeden Player eine einzelne Rule nach dem Schema zu erstellen. Vorteil: Der Timer weiß, welches Item er auslesen muss, man kann sich dann die globalen Strings ersparen. Nachteil: der Code sieht nicht mehr sehr effizient aus...
Gehe zur vollständigen Antwort

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

Re: Benutzerdefinierte Aktion erstellen

Beitrag von udo1toni »

Du kannst dazu Lambdas verwenden. Das Lambda wird quasi wie eine globale Variable zu Beginn des Datei definiert: Schau mal hier:
https://community.openhab.org/t/reusabl ... otes/15888
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

alex200580
Beiträge: 6
Registriert: 17. Jun 2019 11:14
Answers: 0

Re: Benutzerdefinierte Aktion erstellen

Beitrag von alex200580 »

Hallo,

mit Lambda habe ich es schon versucht:

Code: Alles auswählen

val Ansage= [ GenericItem strPlayer, GenericItem strAnsagetext |
    if ( strPlayer == "WZ") {
        if (Mute.state==OFF){
            playSound("chromecast:chromecast:GoogleHomeMiniWZ","Metal_Gong.mp3")
            createTimer(now.plusSeconds(2))[|
                say(""+strAnsagetext,"googletts:deDEStandardB" ,"chromecast:chromecast:GoogleHomeMiniWZ")
            ]
        }
    }
    if ( strPlayer == "Kalli") {
        if (Mute.state==OFF){
            playSound("chromecast:chromecast:GoogleHomeMiniKalli","Metal_Gong.mp3")
            createTimer(now.plusSeconds(2))[|
                say(""+strAnsagetext,"googletts:deDEStandardB" ,"chromecast:chromecast:GoogleHomeMiniKalli")
            ]
        }    
    }
    
]  
strPlayer und strAnsagetext habe ich als String-Items angelegt. Selbst wenn ich o.g. Code direkt in einer Regeldatei einfüge (Das möchte ich ja eigentlich nicht, sondern nur einmal angelegt) bekomme ich
{
"resource": "/t:/conf/rules/Sonoff_RF.rules",
"owner": "_generated_diagnostic_collection_name_#0",
"code": "org.eclipse.xtext.diagnostics.Diagnostic.Linking",
"severity": 8,
"message": "The method or field Ansage is undefined",
"startLineNumber": 53,
"startColumn": 13,
"endLineNumber": 53,
"endColumn": 19
}
Die Sonoff_RF.rules wertet eingehende RF-Codes aus und führt entsprechende Aktionen aus. Habe den Aufruf hier test weise auf einen Funktaster gelegt:

Code: Alles auswählen

if(RFBridge.state == '8B22B2')//3-fach Taster (2) Taste links
        {   logInfo("RULE","3-fach Taster 2 Taste links")
        
            Ansage.apply('WZ','Testdurchsage')
    }

Die Beispiele in deinem Link behandeln leider keine String Items die übergeben werden, keine Ahnung ob das so überhaupt funktionieren würde.

Gruß,
Alex

alex200580
Beiträge: 6
Registriert: 17. Jun 2019 11:14
Answers: 0

Re: Benutzerdefinierte Aktion erstellen

Beitrag von alex200580 »

Update:

folgendes funktioniert:

Code: Alles auswählen

val Ansage= [ String strPlayer, String strAnsagetext |
    if ( strPlayer == "WZ") {
        if (Mute.state==OFF){
            playSound("chromecast:chromecast:GoogleHomeMiniWZ","Metal_Gong.mp3")
            createTimer(now.plusSeconds(2))[|
                say(""+strAnsagetext,"googletts:deDEStandardB" ,"chromecast:chromecast:GoogleHomeMiniWZ")
            ]
        }
    }
    if ( strPlayer == "Kalli") {
        if (Mute.state==OFF){
            playSound("chromecast:chromecast:GoogleHomeMiniKalli","Metal_Gong.mp3")
            createTimer(now.plusSeconds(2))[|
                say(""+strAnsagetext,"googletts:deDEStandardB" ,"chromecast:chromecast:GoogleHomeMiniKalli")
            ]
        }    
    }
    
]  
Wenn ich das oben in der Sonoff_RF.rules einfüge funktioniert der folgende Aufruf

Code: Alles auswählen

if(RFBridge.state == '8B22B2')//3-fach Taster (2) Taste links
        {   logInfo("RULE","3-fach Taster 2 Taste links")
        
            Ansage.apply('WZ','Testdurchsage')
    }
Allerdings müsste ich das dann ja in jeder meiner .rules Datei einfügen in der ich eine Sprachdurchsage programmiert habe. Genau das will ich ja nicht. Ist es nicht möglich die Lamda Definition einmal Global (für alle .rules Dateien) anzulegen und zu nutzen?

Gruß,
Alex

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

Re: Benutzerdefinierte Aktion erstellen

Beitrag von udo1toni »

Also das eine ist der Unterschied zwischen genericItem und String. das genericItem ist eben genau das: ein Item. Wenn Du ein Item übergibst, musst Du natürlich auch die Eigenschaft .name abfragen.

Das Andere ist der Wunsch, die Funktion dateiübergreifend zu verwenden. Vermutlich ist es dafür das Einfachste, eine Rule zu programmieren (und eben kein Lambda) und diese Rule gezielt zu triggern, nachdem Du die benötigten Parameter abgeladen hast. Es böte sich an, das über eine Gruppe zu erledigen.
Die Items:

Code: Alles auswählen

Group gAnsage
String ansage_Kalli (gAnsage)
String ansage_WZ (gAnsage)
Ein Label brauchen wir hier nicht, denn die Items müssen nirgendwo angezeigt werden.
Eine Rule:

Code: Alles auswählen

// Globale Variablen zu Beginn der Datei definieren!
var Timer tPause = null
var String strTarget = ""
var String strText = ""

rule "Ansage mit Gong" 
when
    Member of gAnsage received update
then
    if(Mute.state==ON) return;
    
    strTarget = "chromecast:chromecast:GoogleHomeMini" + triggeringItem.name.split("_").get(1)
    strText = triggeringItem.state.toString
    playSound(strTarget,"Metal_Gong.mp3")
    tPause?.cancel
    tPause = createTimer(now.plusSeconds(2), [|
        say(""+strText ,"googletts:deDEStandardB" ,strTarget)
    ])
end
Nun benutzt Du einfach immer ein ansage_Kalli.postUpdate("Text") bzw. ansage_WZ.postUpdate("Text").
Die Rule triggert in beiden Fällen. Zuerst prüft die Rule, ob Mute ON ist. Ist das der Fall, beendet sich die Rule.
Anschließend füllt sie die Variable strTarget mit dem korrekten String.
Die letzten Zeichen entsprechen dabei dem Teil des Itemnamens nach dem _
In die globale Variable strText kommt der zu sprechende Text.
Nun spielt die Rule den Gong auf dem passenden Gerät aus und legt die Pause ein.
Sobald der Timer abgelaufen ist, folgt der say-Befehl auf das passende Device.
Wichtig ist hier natürlich, dass die beiden Strings vorher in globale Variablen gerettet werden, damit sie im Timer zur Verfügung stehen. Man könnte auch speichern, welches der Geräte eine Ansage machen soll und im Timercode entsprechend verzweigen, das ist aber umständlicher.

Es gibt natürlich auch einen Pferdefuß... Es darf immer nur eine Meldung auf einen Player ausgegeben werden, es müssen immer mindestens 2 Sekunden (und ein paar hundertstel) vergehen, bis der Nächste Player oder die nächste Meldung ausgegeben werden kann.
Wenn es eine solche Einschränkung nicht geben darf, wird die Rule wesentlich komplexer, da dann die Texte in ein globales Array gerettet werden müssen, welches für jeden Player einen Slot und einen Timer bereithält. Über dieses Array muss der Timer dann entscheiden, für welchen Slot er zuständig ist (das heißt, im Array muss auch ein Zeitstempel mit rein) um dann entsprechend auszuspielen. Der Aufwand wird erheblich sein, vermutlich ist es dann einfacher, für jeden Player eine einzelne Rule nach dem Schema zu erstellen. Vorteil: Der Timer weiß, welches Item er auslesen muss, man kann sich dann die globalen Strings ersparen. Nachteil: der Code sieht nicht mehr sehr effizient aus...
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

alex200580
Beiträge: 6
Registriert: 17. Jun 2019 11:14
Answers: 0

Re: Benutzerdefinierte Aktion erstellen

Beitrag von alex200580 »

Guten Morgen,

vielen Dank für die Lösung. Das ist genau das, was ich brauche. Und so simpel... :shock:
Da habe ich vor lauter Bäumen den Wald nicht gesehen. :roll:

Es kommt nur äußerst selten vor das Ansagen gleichzeitig generiert werden, deshalb ist die Lösung so wie sie ist perfekt!

Ganz herzlichen Dank für deine Bemühungen,

Gruß,
Alex

Antworten