Seite 3 von 3

Re: Hilfe bei der Gestaltung einer DSL Regel

Verfasst: 28. Okt 2022 16:09
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

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

Verfasst: 3. Nov 2022 00:38
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)

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

Verfasst: 3. Nov 2022 12:47
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?

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

Verfasst: 3. Nov 2022 13:41
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.

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

Verfasst: 4. Nov 2022 17:20
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?

Re: Hilfe bei der Gestaltung einer DSL Regel

Verfasst: 9. Nov 2022 20:00
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)