Hilfe bei der Gestaltung einer DSL Regel

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Benutzeravatar
scotty
Beiträge: 676
Registriert: 28. Apr 2020 04:44
Answers: 0

Re: Hilfe bei der Gestaltung einer DSL Regel

Beitrag von scotty »

Dank der freundlichen und andauernden Unterstützung unseres allseits hochgeschätzten Udo ist es gelungen, eine sehr gut funktionierende Regel zu erstellen. Prunkstück ist die Anzeige der Dauer meiner Telefonate in Stunden:Minuten:Sekunden. Weitere Parameter für jedes Gespräch sind: Name, Nummer, Datum und Zeit.

Voraussetzung für diese Rule ist ein richtig konfiguriertes Binding TR-064, der User muss auf ein Telefonbuch zugreifen können. Außerdem werden Dummy-Items benötigt, die gut zu unterscheiden sind. Da ich mich für die Anzeige der letzten 7 Gespräche (jeweils Ein- & Ausgang) entschieden habe, bedeutet dies jeweils 35 Items für Ein- bzw. Ausgangsgespräche.

Die Bezeichnungen für die Items sind innerhalb der Regel zu erfahren. Hier wurde das erste Ausgangstelefonat die Itembezeichnung Flag_1_Axxx und für das erste Eingangstelefonat Flag_1_Exxx gewählt. Die Gespräche 2 bis 7 müssen dann fortlaufend nummeriert werden.
Des Weiteren müssen Gruppen gebildet werden, für Ausgang gAFlags und für Eingang gEFlags.

Jetzt möchte ich noch einmal auf die Rule zurück kommen. Das Tolle ist, sie muss bei einer Erweiterung der Gesprächsanzahl nicht mehr verändertet werden. Es müssen lediglich weitere Items erstellt werden.

Hier der Code, er läuft bei mir unter Openhab 3.3 im Docker auf einer Synology:

Code: Alles auswählen

var Long lPhoneStart = 0
var Integer iSeconds = 0

rule "Gesprächsdauer messen"
when
    Item fritzCallRinging changed
then
    val strNew  = newState.toString
    val strPrev = previousState.toString

    if(strNew == "ACTIVE") {
        if(strPrev == "RINGING")
            logInfo("call", "Eingehender Anruf: Verbindung hergestellt")
        else if(strPrev == "DIALING")
            logInfo("call", "Ausgehender Anruf: Verbindung hergestellt")
        lPhoneStart = now.toInstant.toEpochMilli
    } else if(strPrev == "ACTIVE" && strNew == "IDLE") {
        logInfo("call", "Aktiver Anruf: Verbindung beendet")
        iSeconds = ((now.toInstant.toEpochMilli - lPhoneStart)/1000).intValue
        var strTime = ""
        if(iSeconds !== null)
            strTime = transform("JS","secinhms.js",iSeconds.toString)
        logInfo("call", "Beendigung des {}gangsgespräches nach {}",if(Flag_1_ADauer.state.toString=="...")"Aus" else "Ein",strTime)
        if(Flag_1_EDauer.state == "...")
            Flag_1_EDauer.postUpdate(strTime)
        else if(Flag_1_ADauer.state == "...")
            Flag_1_ADauer.postUpdate(strTime)

    }
end

rule "Eingehender Anruf"
when
    Item fritzCallRinging changed to RINGING
then
    if(fritzIncomingCall.state == UNDEF || fritzIncomingCall.state == NULL) return;    
    gEFlags.members.sortBy[ name ].reverseView.forEach[ i |
        val num = Integer::parseInt(i.name.split("_").get(1))
        val art = i.name.split("_").get(2)
        if(num > 1) {
            val myItem = gEFlags.members.filter[ j | j.name.split("_").get(1) == (num-1).toString && j.name.endsWith(art)].head
            i.postUpdate(myItem.state)
        }
    ]

    val incCall      = fritzIncomingCall.state as StringListType
 // val ourNumber    = incCall.getValue(0)
    val callerNumber = incCall.getValue(1)

    val tr064Actions = getActions("tr064","tr064:fritzbox:1")
    var callerName   = tr064Actions.phonebookLookup(callerNumber)
    if(callerName==callerNumber)
        callerName = "unbekannt"
    val callDate     = String.format("%1$td.%1$tm.%1$tY", now)
    val callTime     = String.format("%1$tH:%1$tM", now)

    Flag_1_EName.postUpdate(callerName)
    Flag_1_ETel.postUpdate(callerNumber)
    Flag_1_EDat.postUpdate(callDate)
    Flag_1_ETime.postUpdate(callTime)
    Flag_1_EDauer.postUpdate("...") // signalisiert, dass gerade telefoniert wird

    logInfo("call", "Eingang {} | {} | {} | {}",callerName,callerNumber,callDate,callTime)
end

rule "Ausgehender Anruf"
when
    Item fritzCallRinging changed to DIALING
then
    if(fritzOutgoingCall.state == UNDEF || fritzOutgoingCall.state == NULL) return;
    gAFlags.members.sortBy[ name ].reverseView.forEach[ i |
        val num = Integer::parseInt(i.name.split("_").get(1))
        val art = i.name.split("_").get(2)
        //logInfo("call","Move - zu Item {} Nummer ist {} Name endet mit {}",i.name,num,art)
        if(num > 1) {
            val myItem = gAFlags.members.filter[ j | j.name.split("_").get(1) == (num-1).toString && j.name.endsWith(art)].head
            i.postUpdate(myItem.state)
        }
    ]

    val outCall = fritzOutgoingCall.state as StringListType
    var String calledNumber = outCall.getValue(0)
    // var String ourNumber = outCall.getValue(1)

    val tr064Actions = getActions("tr064","tr064:fritzbox:1")
    var calledName = tr064Actions.phonebookLookup(calledNumber)
    if(calledName==calledNumber)
        calledName = "unbekannt"
    val callDate = String.format("%1$td.%1$tm.%1$tY", now)
    val callTime = String.format("%1$tH:%1$tM", now)

    Flag_1_AName.postUpdate(calledName)
    Flag_1_ATel.postUpdate(calledNumber)
    Flag_1_ADat.postUpdate(callDate)
    Flag_1_ATime.postUpdate(callTime)
    Flag_1_ADauer.postUpdate("...") // signalisiert, dass gerade telefoniert wird

    logInfo("call", "Ausgang {} | {} | {} | {}",calledName,calledNumber,callDate,callTime)
end
OH 3.4.5 im Docker auf Synology DS918+ mit USV, Reolink-RLC-511WA, Philips Hue, AVM Fritz!Box 6591C, Alexa, Logitech Harmony und diversen Shelly's

Benutzeravatar
scotty
Beiträge: 676
Registriert: 28. Apr 2020 04:44
Answers: 0

Re: [erledigt] Hilfe bei der Gestaltung einer DSL Regel

Beitrag von scotty »

Inzwischen ist mir doch noch etwas aufgefallen, was nach Möglichkeit korrigiert werden müsste:
Das Verschieben der ausgehenden und eingehenden Anrufe auf die nächste Position beginnt, wenn der Ruf aufgebaut wird. Sollte die Gegenstelle bzw. der eigene Anschluss dann allerdings besetzt sein, wird auf Position 1 das letzte Gespräch noch einmal geschrieben. Das heißt, im Fall eines Besetztzeichens steht an Position 1 und 2 ein identischer Eintrag. Eventuell müssten die Verschiebungen erst angestoßen werden, wenn das Gespräch beendet ist.
Lässt sich diese Reaktion durch eine If-Anweisung verhindern? Mir ist noch nichts konkretes dazu eingefallen, daher hoffe ich auf Hilfe.

Code: Alles auswählen

var Long lPhoneStart = 0
var Integer iSeconds = 0
var strFax = "0303123456 ..." // 19 Fax-Nummern

rule "Gesprächsdauer messen"
when
    Item fritzCallRinging changed
then
    val strNew  = newState.toString
    val strPrev = previousState.toString

    if(strNew == "ACTIVE") {
        if(strPrev == "RINGING")
            logInfo("call", "Eingehender Anruf: Verbindung hergestellt")
        else if(strPrev == "DIALING")
            logInfo("call", "Ausgehender Anruf: Verbindung hergestellt")
        lPhoneStart = now.toInstant.toEpochMilli
    } else if(strPrev == "ACTIVE" && strNew == "IDLE") {
        logInfo("call", "Aktiver Anruf: Verbindung beendet")
        iSeconds = ((now.toInstant.toEpochMilli - lPhoneStart)/1000).intValue
        var strTime = ""
        if(iSeconds !== null)
            strTime = transform("JS","secinhms.js",iSeconds.toString)
        logInfo("call", "Beendigung des {}gangsgespräches nach {}",if(Flag_1_ADauer.state.toString=="...")"Aus" else "Ein",strTime)
        if(Flag_1_EDauer.state == "...")
            Flag_1_EDauer.postUpdate(strTime)
        else if(Flag_1_ADauer.state == "...")
            Flag_1_ADauer.postUpdate(strTime)

    }
end

rule "Eingehender Anruf"
when
    Item fritzCallRinging changed to RINGING
then
    if(fritzIncomingCall.state == UNDEF || fritzIncomingCall.state == NULL) return;    
    gEFlags.members.sortBy[ name ].reverseView.forEach[ i |
        val num = Integer::parseInt(i.name.split("_").get(1))
        val art = i.name.split("_").get(2)
        if(num > 1) {
            val myItem = gEFlags.members.filter[ j | j.name.split("_").get(1) == (num-1).toString && j.name.endsWith(art)].head
            i.postUpdate(myItem.state)
        }
    ]

    val incCall      = fritzIncomingCall.state as StringListType
 // val ourNumber    = incCall.getValue(0)
    val callerNumber = incCall.getValue(1)

    val tr064Actions = getActions("tr064","tr064:fritzbox:1")
    var callerName   = tr064Actions.phonebookLookup(callerNumber)
    if(strFax.contains(callerNumber.toString))
        callerName = "Faxgerät"
    if(callerName==callerNumber)
        callerName = "unbekannt"
    val callDate     = String.format("%1$td.%1$tm.%1$tY", now)
    val callTime     = String.format("%1$tH:%1$tM", now)

    Flag_1_EName.postUpdate(callerName)
    Flag_1_ETel.postUpdate(callerNumber)
    Flag_1_EDat.postUpdate(callDate)
    Flag_1_ETime.postUpdate(callTime)
    Flag_1_EDauer.postUpdate("...") // signalisiert, dass gerade telefoniert wird

    logInfo("call", "Eingang {} | {} | {} | {}",callerName,callerNumber,callDate,callTime)
end

rule "Ausgehender Anruf"
when
    Item fritzCallRinging changed to DIALING
then
    if(fritzOutgoingCall.state == UNDEF || fritzOutgoingCall.state == NULL) return;
    gAFlags.members.sortBy[ name ].reverseView.forEach[ i |
        val num = Integer::parseInt(i.name.split("_").get(1))
        val art = i.name.split("_").get(2)
        if(num > 1) {
            val myItem = gAFlags.members.filter[ j | j.name.split("_").get(1) == (num-1).toString && j.name.endsWith(art)].head
            i.postUpdate(myItem.state)
        }
    ]

    val outCall = fritzOutgoingCall.state as StringListType
    var String calledNumber = outCall.getValue(0)
    // var String ourNumber = outCall.getValue(1)

    val tr064Actions = getActions("tr064","tr064:fritzbox:1")
    var calledName = tr064Actions.phonebookLookup(calledNumber)
    if(strFax.contains(calledNumber.toString))
        calledName = "Faxgerät"
    if(calledName==calledNumber)
        calledName = "unbekannt"
    val callDate = String.format("%1$td.%1$tm.%1$tY", now)
    val callTime = String.format("%1$tH:%1$tM", now)

    Flag_1_AName.postUpdate(calledName)
    Flag_1_ATel.postUpdate(calledNumber)
    Flag_1_ADat.postUpdate(callDate)
    Flag_1_ATime.postUpdate(callTime)
    Flag_1_ADauer.postUpdate("...") // signalisiert, dass gerade telefoniert wird

    logInfo("call", "Ausgang {} | {} | {} | {}",calledName,calledNumber,callDate,callTime)
OH 3.4.5 im Docker auf Synology DS918+ mit USV, Reolink-RLC-511WA, Philips Hue, AVM Fritz!Box 6591C, Alexa, Logitech Harmony und diversen Shelly's

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

Re: [erledigt] Hilfe bei der Gestaltung einer DSL Regel

Beitrag von udo1toni »

Das wird nicht gehen, es sei denn, die FRITZ!Box signalisiert das "Besetzt" auch. Aber wird auch ein erfolgreicher Aufbau ohne Besetzt signalisiert?
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

Benutzeravatar
scotty
Beiträge: 676
Registriert: 28. Apr 2020 04:44
Answers: 0

Re: [erledigt] Hilfe bei der Gestaltung einer DSL Regel

Beitrag von scotty »

Ich weiß es nicht so genau, glaube aber nicht. Das Problem ist allerdings sehr gravierend, deshalb werde ich weiter recherchieren und melde mich ggf. noch einmal.
OH 3.4.5 im Docker auf Synology DS918+ mit USV, Reolink-RLC-511WA, Philips Hue, AVM Fritz!Box 6591C, Alexa, Logitech Harmony und diversen Shelly's

Benutzeravatar
scotty
Beiträge: 676
Registriert: 28. Apr 2020 04:44
Answers: 0

Re: [erledigt] Hilfe bei der Gestaltung einer DSL Regel

Beitrag von scotty »

Ich bin von der Rule so begeistert, dass mir der letzte Fehler einfach keine Ruhe lässt. Ich denke, dass zunächst einmal die letzte Zeile dieses Code-Schnipsel's aus meiner Log Udo's Frage nach einem erfolgreichen Aufbau ohne Besetzt signalisiert:

Code: Alles auswählen

2022-11-04 16:51:33.670 [INFO ] [org.openhab.core.model.script.call  ] - Ausgang Irgendwer | 080012345678 | 04.11.2022 | 16:51

2022-11-04 16:51:38.368 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'fritzActiveCall' changed from UNDEF to 080012345678,

2022-11-04 16:51:38.368 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'fritzOutgoingCall' changed from 080012345678,123456 to UNDEF

2022-11-04 16:51:38.369 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'fritzCallRinging' changed from DIALING to ACTIVE

2022-11-04 16:51:38.372 [INFO ] [org.openhab.core.model.script.call  ] - Ausgehender Anruf: Verbindung hergestellt
Anschließend habe ich mich einmal selbst angerufen um zu sehen, welche Fehlermeldungen bei "Besetzt" der Log übergeben werden:

Code: Alles auswählen

2022-11-04 16:35:29.554 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'fritzbox_9502-3' failed: Could not cast UNDEF to org.openhab.core.library.types.StringListType; line 82, column 19, length 41 in fritzbox_9502

2022-11-04 16:35:29.647 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'fritzbox_9502-2' failed: Could not cast UNDEF to org.openhab.core.library.types.StringListType; line 47, column 24, length 41 in fritzbox_9502
Danach, so meine ich, müsste es einen neuen Ansatz geben. Zum Beispiel, über eine IF-Anweisung den Anruf ignorieren. Oder fällt interessierten Mitstreitern noch etwas anderes dazu ein?
OH 3.4.5 im Docker auf Synology DS918+ mit USV, Reolink-RLC-511WA, Philips Hue, AVM Fritz!Box 6591C, Alexa, Logitech Harmony und diversen Shelly's

Benutzeravatar
scotty
Beiträge: 676
Registriert: 28. Apr 2020 04:44
Answers: 0

Re: Hilfe bei der Gestaltung einer DSL Regel

Beitrag von scotty »

Für alle, die es interessiert: dank Udo's Hilfe ist es gelungen eine Lösung zu finden, die bei "Besetzt" keine Verschiebungen vornimmt. Eine Fehlermeldung in der Log gibt es bei mir zwar immer noch, aber die beeinflusst die Darstellung nicht.
Hier der neue Code:

Code: Alles auswählen

var Long lPhoneStart = 0
var Integer iSeconds = 0
var strFax = "03039976266 ..." // 19 Fax-Nummern

rule "Gesprächsdauer messen"
when
    Item fritzCallRinging changed
then
    val strNew  = newState.toString
    val strPrev = previousState.toString

    if(strNew == "ACTIVE") {
        if(strPrev == "RINGING")
            logInfo("call", "Eingehender Anruf: Verbindung hergestellt")
        else if(strPrev == "DIALING")
            logInfo("call", "Ausgehender Anruf: Verbindung hergestellt")
        lPhoneStart = now.toInstant.toEpochMilli
    } else if(strPrev == "ACTIVE" && strNew == "IDLE") {
        logInfo("call", "Aktiver Anruf: Verbindung beendet")
        iSeconds = ((now.toInstant.toEpochMilli - lPhoneStart)/1000).intValue
        var strTime = ""
        if(iSeconds !== null)
            strTime = transform("JS","secinhms.js",iSeconds.toString)
        logInfo("call", "Beendigung des {}gangsgespräches nach {}",if(Flag_1_ADauer.state.toString=="...")"Aus" else "Ein",strTime)
        if(Flag_1_EDauer.state == "...")
            Flag_1_EDauer.postUpdate(strTime)
        else if(Flag_1_ADauer.state == "...")
            Flag_1_ADauer.postUpdate(strTime)

    }
end

rule "Eingehender Anruf"
when
    Item fritzCallRinging changed to RINGING
then
    Thread.sleep(500)                                    // Wert eventuell auch noch erhöhen
    if(!(fritzIncomingCall.state instanceof StringListType)) return;
    val incCall      = fritzIncomingCall.state as StringListType   
    gEFlags.members.sortBy[ name ].reverseView.forEach[ i |
        val num = Integer::parseInt(i.name.split("_").get(1))
        val art = i.name.split("_").get(2)
        if(num > 1) {
            val myItem = gEFlags.members.filter[ j | j.name.split("_").get(1) == (num-1).toString && j.name.endsWith(art)].head
            i.postUpdate(myItem.state)
        }
    ]

    val callerNumber = incCall.getValue(1)

    val tr064Actions = getActions("tr064","tr064:fritzbox:1")
    var callerName   = tr064Actions.phonebookLookup(callerNumber)
    if(callerName==callerNumber)
        callerName = "unbekannt"
    val callDate     = String.format("%1$td.%1$tm.%1$tY", now)
    val callTime     = String.format("%1$tH:%1$tM", now)

    Flag_1_EName.postUpdate(callerName)
    Flag_1_ETel.postUpdate(callerNumber)
    Flag_1_EDat.postUpdate(callDate)
    Flag_1_ETime.postUpdate(callTime)
    Flag_1_EDauer.postUpdate("...") // signalisiert, dass gerade telefoniert wird

    logInfo("call", "Eingang {} | {} | {} | {}",callerName,callerNumber,callDate,callTime)
end

rule "Ausgehender Anruf"
when
    Item fritzCallRinging changed to DIALING
then
    Thread.sleep(500)                                    // Wert eventuell auch noch erhöhen
    if(!(fritzOutgoingCall.state instanceof StringListType)) return;
    val outCall = fritzOutgoingCall.state as StringListType
    gAFlags.members.sortBy[ name ].reverseView.forEach[ i |
        val num = Integer::parseInt(i.name.split("_").get(1))
        val art = i.name.split("_").get(2)
        //logInfo("call","Move - zu Item {} Nummer ist {} Name endet mit {}",i.name,num,art)
        if(num > 1) {
            val myItem = gAFlags.members.filter[ j | j.name.split("_").get(1) == (num-1).toString && j.name.endsWith(art)].head
            i.postUpdate(myItem.state)
        }
    ]

    var String calledNumber = outCall.getValue(0)

    val tr064Actions = getActions("tr064","tr064:fritzbox:1")
    var calledName = tr064Actions.phonebookLookup(calledNumber)
    if(calledName==calledNumber)
        calledName = "unbekannt"
    val callDate = String.format("%1$td.%1$tm.%1$tY", now)
    val callTime = String.format("%1$tH:%1$tM", now)

    Flag_1_AName.postUpdate(calledName)
    Flag_1_ATel.postUpdate(calledNumber)
    Flag_1_ADat.postUpdate(callDate)
    Flag_1_ATime.postUpdate(callTime)
    Flag_1_ADauer.postUpdate("...") // signalisiert, dass gerade telefoniert wird

    logInfo("call", "Ausgang {} | {} | {} | {}",calledName,calledNumber,callDate,callTime)
OH 3.4.5 im Docker auf Synology DS918+ mit USV, Reolink-RLC-511WA, Philips Hue, AVM Fritz!Box 6591C, Alexa, Logitech Harmony und diversen Shelly's

Antworten