Fritzbox call overview

I did not find an editor either, so I wrote the svg source code and used this page to check how it looks:
https://www.w3schools.com/graphics/tryit.asp?filename=trysvg_myfirst. It is a little bit of try and error, but for the 4 icons it only took 30 minutes.

Afterwards you create an svg icon file with a text editor and insert the different icons in there. This is described in the matrix theme thread.

hi,
again thank you :slight_smile:
I added the colors, the fritzbox use for the icons and make the signs OUT, IN and MISSED bigger (my eyes are not so goodā€¦)

<symbol id="phone-in_100">
  <path fill="blue" d='M 25 15 L 20 20 C 0 40 60 100 80 80 l 5 -5 l -20 -20 l -5 5 C 55 65 35 45 40 40 l 5 -5'/>
  <path fill="blue" d='M 60 40 l 0 -26 l 6 6 l 18 -20 l 16 16 l -20 20 l 6 6'/>
</symbol>
<symbol id="phone-out_100">
  <path fill="green" d='M 25 15 L 20 20 C 0 40 60 100 80 80 l 5 -5 l -20 -20 l -5 5 C 55 65 35 45 40 40 l 5 -5'/>
  <path fill="green" d='M 88 11 l 0 24 l -6 -6 l -20 20 l -12 -12 l 20 -20 l -6 -6'/>
</symbol>
<symbol id="phone-missed_100">
  <path fill="red" d='M 25 15 L 20 20 C 0 40 60 100 80 80 l 5 -5 l -20 -20 l -5 5 C 55 65 35 45 40 40 l 5 -5'/>
  <path fill="red" d='M 61 41 l -6 -6 l 12 -12 l -12 -12 l 6 -6 l 12 12 l 12 -12 l 6 6 l -12 12 l 12 12 l -6 6 l -12 -12'/>
</symbol>

one problem remains. I see the caller appear and disapear in the widget, but the list of callers is not updated. The script works. When I refresh the site manually, the new list is shown.
I know, that I make something wrong, but after 2 hours of looking at the code, I donā€™t know, what it can bee.
Can somebody show me the right direction?

Bad news is there is no auto-reload. You can add a button that reloads the page, see Custom Javascript code Refresh button.

Thank you,
I added for now such a button.
But is it possible to include the code in the script or the rule?
I played around with the code, but dont manage it to work. But I am not very firm in such thingsā€¦

I just had the time to look at his again. In order to get the reload working you have to make several changes:

Create a String item called ListDate

Change the one rule to

rule "Generate Call Overview"
when
   Item fboxIncomingCall changed or
   Item fboxOutgoingCall changed 
then
   if (fboxIncomingCall.state.toString.isEmpty && fboxOutgoingCall.state.toString.isEmpty && !previousState.toString.isEmpty) {
     executeCommandLine("/etc/openhab2/scripts/fritzbox.sh update", 30000)
     ListDate.postUpdate(now.millis);
   }
end

so that it runs the update in the foreground and updates the item afterwards.

Change the part of the widget that includes the call list to:

<div oc-lazy-load="['/static/habpanel-reload.js']">
  <div ng-controller="MyWidgetCtrl as myctrl" ng-include="'http://openhabianpi:8080/static/calloverview.html?date=' + myctrl.listDate">
  </div>
</div>

Create the referenced file habpanel-reload.js in the openhab html folder with the following contents:

angular
 .module('app.widgets')
 .controller('MyWidgetCtrl', testCtrl);

testCtrl.$inject = ['$scope', 'OHService'];
function testCtrl($scope, OHService) {
  var vm = this;
  vm.listDate = "123";

  OHService.onUpdate($scope, 'ListDate', function () {
    var item = OHService.getItem('ListDate');
    if (item && item.state != vm.listDate) {
      console.log("ListDate updated: " + item.state);
      vm.listDate = item.state;
    }
  });
}

This makes sure that a copy of the openhab ListDate item value is stored as a copy in an angular variable listDate. This variable is referenced in the URL of the ng-include attribute to make sure the url changes. This triggers a reload.

1 Like

:slight_smile: I am happy :slight_smile:
It works as it should. Again thank you for your help and time, you spent.
I hope it was intersting for you too.

Hi Volker,

nice work. Iā€™m really thankful to use your script and to integrate this in the matrix theme.
While trying the feature to listen to the TAM messages I didnā€™t get it work to play the sound over my raspberry where OH2 is running.
Therefore I built a solution to play the sound on the device your having the openhab webpage open (e.g. smartphone, tablet or pc).

I made two little changes:

  1. I saved the .wav-files in the following directory in order to get access to it via static url
    #openhabdir#/conf/html/sounds/

  2. To play the messages I use the html audio player. Therefore I add an audio element, when thereā€™s an entry with a TAM message. By clicking the entry the audio player appears. You can start it. Afterwards you can make it disappear by clicking. (Since Iā€™m not the html expert there is some potential for optimization :slight_smile: )

openhab_TAM

Here is the script.

Change 1 at line 182-185: Get the path to the sound file
Change 2 at line 209-211: Adapt html to insert the audio player

#!/bin/bash
# 
# Script that downloads the call list from a fritz box and generates 
# html code than can be embedded into a habpanel widget that uses the 
# matrix-theme (https://community.openhab.org/t/custom-theme-in-habpanel-2-1-example/31100)
#
# If calls have been recorded, it additionally downloads the call recordings
# and adds a call to the widget that allows to play back the recording using
# the default audio sink of openHAB.
#
# Check and adapt the following environment variables to your needs:

# fritzbox URL for tr-064 calls
BASEURL="http://fritz.box:49000"

# user:password used to authenticate
USER="user:pw"
# openhab directory containing sounds subdirectory
OPENHAB_DIR=/opt/YourOpenhabDir
# call overview file
OUT=$OPENHAB_DIR/conf/html/calloverview.html
# temporary directory
TMP=/tmp

# number of calls to show
COUNT=20
# number of answering machines
TAM_COUNT=1
# URL of the SVG file containing the icons
SVG_URL=/static/fritzbox.svg
# icon name for recorded calls
CASSETTE_NAME=cassette_100
# svg viewbox for recorded calls
CASSETTE_BOX="0 0 100 100"
# icon name for incoming calls
CALL_IN_NAME=phone-in_100
# svg viewbox for incoming calls
CALL_IN_BOX="0 0 100 100"
# icon name for outgoing calls
CALL_OUT_NAME=phone-out_100
# svg viewbox for outgoing calls
CALL_OUT_BOX="0 0 100 100"
# icon name for missed calles
CALL_MISSED_NAME=phone-missed_100
# svg viewbox for missed calls
CALL_MISSED_BOX="0 0 100 100"

### Function definitions ###

function getElement() {
  text=$1
  name=$2
 
  echo "$text" | sed -e "s/.*<$name>//" -e "s#</$name>.*##"
}

function arrayContains() {
  local e
  for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
  return 1
}

function soapCall() {
  URL=$1
  URN=$2
  ACTION=$3
  ELEMENT=$4
  PARAMETERS=${5:-}

  cat >$TMP/soapEnvelope-$$ <<EOF
<?xml version='1.0' encoding='utf-8'?>
<s:Envelope s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>
 <s:Body>
  <u:$ACTION xmlns:u='$URN'>${PARAMETERS}</u:$ACTION>
 </s:Body>
</s:Envelope>
EOF

  RESPONSE=$(curl -s --anyauth --user $USER $URL -H 'Content-Type: text/xml; charset="utf-8"' -H "SoapAction:$URN#$ACTION" --data @$TMP/soapEnvelope-$$)
  rm $TMP/soapEnvelope-$$

  if [ "x${ELEMENT}x" != "xx" ]; then
    echo $RESPONSE | grep $ELEMENT | sed -e "s/.*<$ELEMENT>//" -e "s#</$ELEMENT>.*##"
  else 
    echo $RESPONSE
  fi
}

function removeTamMessages() {
  rm $TMP/tam${1:-[0-9]}.xml 2>/dev/null
}

function downloadMissingTamMessages() {
  tamidx=0
  while [ $tamidx -lt $TAM_COUNT ]; do 
    if [ ! -f $TMP/tam${tamidx}.xml ]; then
      echo -n "downloading message list for TAM $tamidx..."
      URL=$(soapCall "$BASEURL/upnp/control/x_tam" "urn:dslforum-org:service:X_AVM-DE_TAM:1" GetMessageList NewURL "<NewIndex>${tamidx}</NewIndex>")
      wget --quiet -O - $URL | awk '/<Message>/,/<\/Message>/ { printf $0 } /<\/Message>/ { print }'>$TMP/tam${tamidx}.xml 
      echo "$(stat -c%s $TMP/tam${tamidx}.xml) bytes."
    fi

    tamidx=$(expr $tamidx + 1)
  done
}

function getTamMsg() {
  mdate=$1

  for tamxml in $TMP/tam${2:-[0-9]}.xml; do
    while read message; do
      date=$(getElement "$message" Date)

      if [ "${date}" == "${mdate}" ]; then
        echo "$message"
        return 0
      fi
    done < $tamxml
  done

  return 1
}

function updateCallOverview() {
  downloadMissingTamMessages

  URL=$(soapCall "$BASEURL/upnp/control/x_contact" "urn:dslforum-org:service:X_AVM-DE_OnTel:1" GetCallList NewCallListURL)

  echo "<div class=\"section\">">$OUT
  echo "<div class=\"title\"><div class=\"name\">Anrufliste</div></div>">>$OUT
  echo -n "<div class=\"controls\" ng-init=\"">>$OUT

  for i in `seq 0 $COUNT`; do
    echo -n "rec${i}='icon on';">>$OUT
  done 
  
  echo "\">">>$OUT
  echo "<table>">>$OUT

  callidx=0
  echo -n "downloading call list..."
  wget --quiet -O - $URL | grep Call > $TMP/calls.xml
  echo "$(stat -c%s $TMP/calls.xml) bytes."

  while read call; do
    type=$(getElement "$call" Type)
	#echo "type $type"
    caller=$(getElement "$call" Caller)
    #echo "caller $caller"
	called=$(getElement "$call" Called)
	#echo "called $called"
    name=$(getElement "$call" Name)
	#echo "name $name"
    date=$(getElement "$call" Date)
	#echo "date $date"
    duration=$(getElement "$call" Duration)
	#echo "duration $duration"
    path=$(getElement "$call" Path)
	#echo "path $path"
	device=$(getElement "$call" Device)
	echo "device $device"
	
    message=$(getTamMsg "$date")

    [ "x${message}x" != "xx" ] && isTamMsg=1 || isTamMsg=0
    [ "x$(getElement "$message" New)x" == "x1x" ] && isNewTamMsg=1 || isNewTamMsg=0

    echo "processing call $date (isTamMsg=$isTamMsg isNewTamMsg=$isNewTamMsg)..."

    if [ "$path" == "$call" ]; then
      path=""

      # in case this call has no associated recording, check if there is a TAM message with the
      # same time and skip this call 
      if [ $isTamMsg -eq 1 ]; then
        echo "skipped (also has a TAM message)."
        continue
      fi

      echo "<div class=\"widget\">">>$OUT
    else 
      soundfile=${OPENHAB_DIR}/conf/html/sounds/`basename $path`.wav
	  sf=`basename $path`.wav 
	  soundfilepur="/static/sounds/$sf"
	  echo "sounfilepur $soundfilepur"

      # collect all current soundfiles to be able to delete the old ones
      files[callidx]=$soundfile

      if [ ! -f $soundfile ]; then
        echo -n "fetching ${soundfile}..."

        curl -s -o $soundfile "${BASEURL}$path&$SID"

        echo "$(stat -c%s $soundfile) bytes."
      fi
      echo "<div class=\"widget\" ng-click=\"isReplyFormOpen = !isReplyFormOpen\">">>$OUT
    fi

    # 1 incoming
    # 2 missed
    # 3 outgoing
    # 9 active incoming
    #10 rejected incoming
    #11 active outgoing 
    if [ "x${path}x" != "xx" ]; then
      if [ $isNewTamMsg -ne 1 ]; then
	  #<audio controls style=\"width: 200px;\"><source src=\"${soundfilepur}\" ></audio>
        echo "<div class=\"icon off\"><svg viewBox=\"${CASSETTE_BOX}\"><use xlink:href=\"${SVG_URL}#${CASSETTE_NAME}\"></svg><audio ng-init=\"isReplyFormOpen = false\" ng-show=\"isReplyFormOpen\" controls style=\"width: 200px;\"><source src=\"${soundfilepur}\" ></audio></div>">>$OUT
      else 
        echo "<div class=\"{{rec$callidx}}\"><svg viewBox=\"${CASSETTE_BOX}\"><use xlink:href=\"${SVG_URL}#${CASSETTE_NAME}\"></svg><audio ng-init=\"isReplyFormOpen = false\" ng-show=\"isReplyFormOpen\" controls style=\"width: 200px;\"><source src=\"${soundfilepur}\" ></audio></div>">>$OUT
      fi
    elif [ $type -eq 1 -o $type -eq 9 ]; then
      echo "<div class=\"icon off\"><svg viewBox=\"${CALL_IN_BOX}\"><use xlink:href=\"${SVG_URL}#${CALL_IN_NAME}\"></div>">>$OUT
    elif [ $type -eq 2 ]; then
      echo "<div class=\"icon off\"><svg viewBox=\"${CALL_MISSED_BOX}\"><use xlink:href=\"${SVG_URL}#${CALL_MISSED_NAME}\"></div>">>$OUT
    elif [ $type -eq 3 -o $type -eq 11 ]; then
      echo "<div class=\"icon off\"><svg viewBox=\"${CALL_OUT_BOX}\"><use xlink:href=\"${SVG_URL}#${CALL_OUT_NAME}\"></div>">>$OUT
    fi

    echo "<div class=\"name\">">>$OUT
    if [ $type -eq 1 ]; then
      echo "${name:-$caller} ($duration)">>$OUT
    elif [ $type -eq 2 -o $type -eq 9 ]; then
      echo "${name:-$caller}">>$OUT
    elif [ $type -eq 3 ]; then
      echo "${name:-$called} ($duration)">>$OUT
    elif [ $type -eq 11 ]; then
      echo "${name:-$called}">>$OUT
    fi
    echo "</div>">>$OUT

    echo "<div class=\"valueGroup\"><div class=\"value\">$date</div></div>">>$OUT
    echo "</div>">>$OUT
    
    callidx=$(expr $callidx + 1)
	
    if [ $callidx -eq $COUNT ]; then
      break
    fi
  done < $TMP/calls.xml
  rm $TMP/calls.xml

  echo "</div></div>">>$OUT

  # remove old files
  for file in ${OPENHAB_DIR}/conf/html/sounds/rec.[0-9].[0-9][0-9][0-9].wav; do
    arrayContains "$file" "${files[@]}"
    if [ $? -ne 0 -a -f $file ]; then
      rm ${file} 2>/dev/null
      echo "removed old recording $file."
    fi 
  done
}

function mark() {
  file=${OPENHAB_DIR}/conf/html/sounds/$1

  tam=$(echo $1 | cut -d "." -f 2)
  index=$(echo $1 | cut -d "." -f 3)

  soapCall "$BASEURL/upnp/control/x_tam" "urn:dslforum-org:service:X_AVM-DE_TAM:1" MarkMessage dummy "<NewIndex>$tam</NewIndex><NewMessageIndex>$index</NewMessageIndex>"
}

### the fun starts here ###
source `dirname $0`/lockRoutines
exlock_now || exit 1

echo "lock obtained"

SID=$(soapCall "$BASEURL/upnp/control/deviceconfig" "urn:dslforum-org:service:DeviceConfig:1" "X_AVM-DE_CreateUrlSID" NewX_AVM-DE_UrlSID)

echo $SID
echo $#

removeTamMessages

if [ $# -eq 1 -a "$1" == "update" ]; then
  updateCallOverview
elif [ $# -eq 2 -a "$1" == "mark" ]; then
  mark $2
  updateCallOverview
else
  echo "`basename $0` update | mark <file>"
  exit 1
fi

removeTamMessages

Improvements are welcome :wink:

It depends on which audio sink you have configured. I use Web Audio, which allows to listen to the recorded calls on the device with the browser with my script. No changes needed.

Ok, so that was my lack of knowledge. I didnā€™t know this is possible. I will give it a try.

All, this is my first post and I have been playing around with Openhab 2 and HabPanel since 2 weeks now. Thank you all for providing this great scripts and tweaks. Please let me briefly share how I tweaked the scripts and rules to have the last calls read into items and presentend them in a template:

(1) I took the initial script posted at the start of this thread and added one line at the end to create a short file with 10 lines only. Then I wrote a 1-liner called extrac.sh which gives back only one line of the caller log, by providing the line number, e.g. ā€œ./extract.sh 5ā€ gives you call number 5 from the short file.

grep ā€œ^1|^2|^3|^4ā€ -m 10 /etc/openhab2/persistence/fritzcalls.txt > /etc/openhab2/persistence/fritzcalls-short.txt

extract.sh:

#!/bin/bash
head -$1 /etc/openhab2/persistence/fritzcalls-short.txt | tail -1 | grep ''

(2) Items definition:

Group gFritzCalls

String fritzCall1 ā€œCall 1ā€ (gFritzCalls)
String fritzCall2 ā€œCall 2ā€ (gFritzCalls)
String fritzCall3 ā€œCall 3ā€ (gFritzCalls)
String fritzCall4 ā€œCall 4ā€ (gFritzCalls)
String fritzCall5 ā€œCall 5ā€ (gFritzCalls)
String fritzCall6 ā€œCall 6ā€ (gFritzCalls)

(3) Rules: obviously the one getting (and shortening) the Fritz caller log. And then the one raeding the files into the above items by going through the group:

rule ā€œUpdate calllistā€
when
Time cron ā€œ0 0/2 * ? * * *ā€ // every two minutes
then
logInfo(ā€œFritzboxā€," Calllist wird neu geladen")
executeCommandLine("/etc/openhab2/scripts/callmonitor.sh")

end

rule ā€œUpdate callitemsā€
when
Time cron ā€œ0 0/5 * * * ?ā€
then

		val index = 1 
		gFritzCalls.members.forEach [ item | 
		postUpdate(item,executeCommandLine("/etc/openhab2/scripts/extract.sh " + index, 1000))
		index++
		]

end

(4) Template in HabPanel, having the phone icons stored as fritz-1.gif etc. representing the call type from the call-list. Splitting the items by ā€œ;ā€

<table class="table-standard">
  <caption>Anrufe</caption>
  <tr>
  <th>Typ</th>
  <th>Datum</th>
  <th>Name</th>
  <th>Nummer</th>
  <th>Dauer</th>
  </tr>
<tr ng-repeat="item in itemsInGroup('gFritzCalls')" align="left" cellpadding="2px" border="none">
    <td><img src="/static/icons/own/fritz-{{itemValue(item.name).split(';')[0]}}.gif"</td>
  <td>{{itemValue(item.name).split(';')[1]}}</td>
  <td>{{itemValue(item.name).split(';')[2]}}</td>
  <td>{{itemValue(item.name).split(';')[3]}}</td>
  <td>{{itemValue(item.name).split(';')[6]}}</td>
  </tr>

(5) Result: > I am very happy with the result!

2 Likes

Did someone use this script in HABpanel without the matrix theme?

I want to have it in a nice widget there, but I do not really know how :confused:

Greetings

Hi StefanJ, all

I did like you approach of the extract.shā€¦and the rule passing it to the 6 last caller item.
Took me a bit to get it to run as I am not familiar with Linux and these script languages.

So the basics are now running and the 6 call items are properly populated every 5Minā€¦whilst the list is pulled from the fritzbox every 2Minā€¦

Now coming the my 2 issues with habpanel. I do use OH2.2 displaying on GoogleChrome.
Created the icons on /etc/openhab2/icons/classic as I donā€™t know where the ā€œstaticā€ folder is located.

Problem #1
Icons do not display. I tried .svg , .png and .gif ā€¦ none of them are displayed. Do they need to be of a specific size e.g.48x48 or so ? Mine are 200x200 I thinkā€¦ Any suggestions welcome.

Problem #2
Cellspacing does not work on the table . it looks crap compared to yoursā€¦Tried several on cellspacing and cellpadding but I am an angularJS rookie. Any suggestions welcome too

See result picture and current widget code belowā€¦

<table class="table-standard">
  <caption>Anrufe</caption>
  <tr>
  <th>Typ</th>
  <th>Datum</th>
  <th>Name</th>
  <th>Nummer</th>
  <th>Dauer</th>
  </tr>
<tr ng-repeat="item in itemsInGroup('ZZ_FritzCalls')" align="left" cellpadding="2px" border="none">
    <td><img src="/etc/openhab2/icons/classic/fritz-{{itemValue(item.name).split(';')[0]}}.svg"</td>
  <td>{{itemValue(item.name).split(';')[1]}}</td>
  <td>{{itemValue(item.name).split(';')[2]}}</td>
  <td>{{itemValue(item.name).split(';')[3]}}</td>
  <td>{{itemValue(item.name).split(';')[6]}}</td>
  </tr>

Why you donā€™t listen to the CallMonitor of the Fritzbox ā€¦ Downloading every Minute is Bad :wink:

This is my very old Perl Script that listen to the Internal CallMonitor of the Fritzbox

The complete Script are to big ā€¦ so a paste only the Listening Part :wink:

#!/usr/bin/perl -w

#Version 0.8
#written by eiGelbGeek 2017

#FritzBox_IP
my $FRITZBOX="192.168.20.1"; 

#Hier wird der Fritzbox Anrufmonitor belauscht
my $sock = new IO::Socket::INET (
        PeerAddr => $FRITZBOX,
        PeerPort => '1012',
        Proto => 'tcp'
        );
        die "Could not create socket: $!\n" unless $sock;


while(<$sock>)
        {            
            if ($_ =~ /RING/)
            {
               #... Do something
            }
            
            if ($_ =~ /CALL/)
            {
                #... Do something
            }
            
            if ($_ =~ /CONNECT/)
            {
               #... Do something
            }
            
            if ($_ =~ /DISCONNECT/)
            {
                #... Do something
            }
        }

Hi Kevin,

whats the difference between a perl script that is permanently doing a while loop to a rule that is triggered every 2 min ? Is the rule more CPU intensive ?..

For incoming calls I could trigger it via the Item fbox_Ringing changed from OFF to ON rule (see below) but then I guess I will run into the same issue. How do I get access to the last 6 calls without downloading the caller list?

Is there a possibility of incCall.getValue(1), incCall.getValue(2), incCall.getValue(2), incCall.getValue(3) ā€¦etc.? However this one is only triggered when I receive a call, or ?

rule "Eingehender Anruf"
when
  Item fbox_Ringing changed from OFF to ON
then
	// Daten des CallType auslesen
	val incCall = fbox_IncomingCall.state as StringListType
	val callerNumber = incCall.getValue(1)
	
	// Die Variablen anlegen und mit der Nummer bzw. Namen des Anrufers fĆ¼llen
	LastCallName   = fbAnrufName.state.toString()
	
	// Die eigene Rufnummer aus der Variable entfernen
	LastCallName = LastCallName.removeStart("999999##")                   //(99999=your tel #no)
	
	// PrĆ¼fen ob der Anrufer unbekannt ist
	if(LastCallName.startsWith("Name not found for")) {
		// Den Namen mit Unbekannt fĆ¼llen
		LastCallName = "Unbekannt"
	}
	// Die Daten in die Items eintragen
	postUpdate(fbox_LastNumber, callerNumber)
	postUpdate(fbox_LastName, LastCallName)
	logInfo("RuleLastCall", "Der Anruf von " + callerNumber + " (" + LastCallName + ")" + " wurde als Letzter Anrufer gespeichert.")
end

The UI display problem I managed in the meanwhile with ā€œcellpadding=ā€œ25pxā€ cellspacing=ā€œ25pxā€ border=ā€œnoneā€ā€

Hi, thank you for that tutorial and all your work!
I changed the script for me to use items (updated via REST api). By that, I do not need the extra html file and also not the habpanel-reload.js for the reload. And for me it getā€™s easier to understand what happens :smiley:
Another advantage (and the reason I changed it) is that I can use my standard widget with 4 rows, which you can see here:


New messages are shown by a blinking icon, playback works and after playing, the icon stops blinking and gets grey.
I only want to show the missed calls and the recorded messages (the last 4 events).
Here are the items:

String AbMessage
Call   fboxIncomingCall    "Incoming call: [%1$s to %2$s]"     {fritzboxtr064="callmonitor_ringing" } 
Call   fboxOutgoingCall    "Outgoing call: [%1$s to %2$s]"     {fritzboxtr064="callmonitor_outgoing" }
String Anruf_1_Name "Anruf 1 Name [%s]"
String Anruf_2_Name "Anruf 2 Name [%s]"
String Anruf_3_Name "Anruf 3 Name [%s]"
String Anruf_4_Name "Anruf 4 Name [%s]"
String Anruf_1_DatumZeit "Anruf 1 Zeit [%s]"
String Anruf_2_DatumZeit "Anruf 2 Zeit [%s]"
String Anruf_3_DatumZeit "Anruf 3 Zeit [%s]"
String Anruf_4_DatumZeit "Anruf 4 Zeit [%s]"
String Nachricht_1_Datei "Nachricht 1 Datei [%s]"
String Nachricht_2_Datei "Nachricht 2 Datei [%s]"
String Nachricht_3_Datei "Nachricht 3 Datei [%s]"
String Nachricht_4_Datei "Nachricht 4 Datei [%s]"
Switch Nachricht_1_Neu "Nachricht 1 Neu"
Switch Nachricht_2_Neu "Nachricht 2 Neu"
Switch Nachricht_3_Neu "Nachricht 3 Neu"
Switch Nachricht_4_Neu "Nachricht 4 Neu"

Hereā€™s the script:

#!/bin/bash
#
# Check and adapt the following environment variables to your needs:

# fritzbox URL for tr-064 calls
BASEURL="http://fritz.box:49000"
# user:password used to authenticate
USER="user:pw"
# openhab directory containing sounds subdirectory
OPENHAB_DIR=/etc/openhab2
# temporary directory
TMP=/tmp

# number of calls to show
COUNT=40
# number of answering machines
TAM_COUNT=1


### Function definitions ###

function getElement() {
  text=$1
  name=$2
 
  echo "$text" | sed -e "s/.*<$name>//" -e "s#</$name>.*##"
}

function arrayContains() {
  local e
  for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
  return 1
}

function soapCall() {

  URL=$1
  URN=$2
  ACTION=$3
  ELEMENT=$4
  PARAMETERS=${5:-}

  cat >$TMP/soapEnvelope-$$ <<EOF
<?xml version='1.0' encoding='utf-8'?>
<s:Envelope s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>
 <s:Body>
  <u:$ACTION xmlns:u='$URN'>${PARAMETERS}</u:$ACTION>
 </s:Body>
</s:Envelope>
EOF

  RESPONSE=$(curl -s --anyauth --user $USER $URL -H 'Content-Type: text/xml; charset="utf-8"' -H "SoapAction:$URN#$ACTION" --data @$TMP/soapEnvelope-$$)

  rm $TMP/soapEnvelope-$$

  if [ "x${ELEMENT}x" != "xx" ]; then
    echo $RESPONSE | grep $ELEMENT | sed -e "s/.*<$ELEMENT>//" -e "s#</$ELEMENT>.*##"
  else 
    echo $RESPONSE
  fi
}

function removeTamMessages() {
  rm $TMP/tam${1:-[0-9]}.xml 2>/dev/null
}

function downloadMissingTamMessages() {
  tamidx=0
  while [ $tamidx -lt $TAM_COUNT ]; do 
    if [ ! -f $TMP/tam${tamidx}.xml ]; then
      echo -n "downloading message list for TAM $tamidx..."
      URL=$(soapCall "$BASEURL/upnp/control/x_tam" "urn:dslforum-org:service:X_AVM-DE_TAM:1" GetMessageList NewURL "<NewIndex>${tamidx}</NewIndex>")
	  wget --quiet -O - $URL | awk '/<Message>/,/<\/Message>/ { printf $0 } /<\/Message>/ { print }'>$TMP/tam${tamidx}.xml 
      echo "$(stat -c%s $TMP/tam${tamidx}.xml) bytes."
    fi

    tamidx=$(expr $tamidx + 1)
  done
}

function getTamMsg() {
  mdate=$1

  for tamxml in $TMP/tam${2:-[0-9]}.xml; do
    while read message; do
      date=$(getElement "$message" Date)

      if [ "${date}" == "${mdate}" ]; then
        echo "$message"
        return 0
      fi
    done < $tamxml
  done

  return 1
}

function updateCallOverview() {
  downloadMissingTamMessages

  URL=$(soapCall "$BASEURL/upnp/control/x_contact" "urn:dslforum-org:service:X_AVM-DE_OnTel:1" GetCallList NewCallListURL)

  callidx=0
  echo -n "downloading call list..."
  wget --quiet -O - $URL | grep Call > $TMP/calls.xml
  echo "$(stat -c%s $TMP/calls.xml) bytes."

  zaehler=0
  while read call; do
    type=$(getElement "$call" Type)
    caller=$(getElement "$call" Caller)
    called=$(getElement "$call" Called)
    name=$(getElement "$call" Name)
    date=$(getElement "$call" Date)
    duration=$(getElement "$call" Duration)
    path=$(getElement "$call" Path)

    message=$(getTamMsg "$date")

    [ "x${message}x" != "xx" ] && isTamMsg=1 || isTamMsg=0
    [ "x$(getElement "$message" New)x" == "x1x" ] && isNewTamMsg=1 || isNewTamMsg=0

    echo "processing call $date (isTamMsg=$isTamMsg isNewTamMsg=$isNewTamMsg)..."

    if [ "$path" == "$call" ]; then
      path=""

      # in case this call has no associated recording, check if there is a TAM message with the
      # same time and skip this call 
      if [ $isTamMsg -eq 1 ]; then
        echo "skipped (also has a TAM message)."
        continue
      fi

    else 
      soundfile=${OPENHAB_DIR}/sounds/`basename $path`.wav
	  soundfilename=`basename $path`

      # collect all current soundfiles to be able to delete the old ones
      files[callidx]=$soundfile

      if [ ! -f $soundfile ]; then
        echo -n "fetching ${soundfile}..."

        curl -o $soundfile "${BASEURL}$path&$SID"

        echo "$(stat -c%s $soundfile) bytes."
      fi
	  
    fi

    # 1 incoming
    # 2 missed
    # 3 outgoing
    # 9 active incoming
    #10 rejected incoming
    #11 active outgoing 
    if [ "x${path}x" != "xx" ]; then
	  zaehler=$((zaehler+1))
      if [ $isNewTamMsg -eq 1 ]; then
		curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "ON" "http://openhabianpi:8080/rest/items/Nachricht_${zaehler}_Neu/state"
	  else
		curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "OFF" "http://openhabianpi:8080/rest/items/Nachricht_${zaehler}_Neu/state"
	  fi
	  curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "$soundfilename" "http://openhabianpi:8080/rest/items/Nachricht_${zaehler}_Datei/state"
	  curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "${name:-$caller}" "http://openhabianpi:8080/rest/items/Anruf_${zaehler}_Name/state"
	  curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "$date" "http://openhabianpi:8080/rest/items/Anruf_${zaehler}_DatumZeit/state"
    elif [ $type -eq 2 ]; then
	  zaehler=$((zaehler+1))
	  curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "OFF" "http://openhabianpi:8080/rest/items/Nachricht_${zaehler}_Neu/state"
	  curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "none" "http://openhabianpi:8080/rest/items/Nachricht_${zaehler}_Datei/state"
	  curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "${name:-$caller}" "http://openhabianpi:8080/rest/items/Anruf_${zaehler}_Name/state"
	  curl -s -X PUT --header "Content-Type: text/plain" --header "Accept: application/json" -d "$date" "http://openhabianpi:8080/rest/items/Anruf_${zaehler}_DatumZeit/state"
    fi
    
    callidx=$(expr $callidx + 1)
    if [ $callidx -eq $COUNT ]; then
      break
    fi
    if [ $zaehler -eq 4 ]; then
	  break
	fi
  done < $TMP/calls.xml
  rm $TMP/calls.xml
  echo $zeile

  # remove old files
  for file in ${OPENHAB_DIR}/sounds/rec.[0-9].[0-9][0-9][0-9].wav; do
    arrayContains "$file" "${files[@]}"
    if [ $? -ne 0 -a -f $file ]; then
      rm ${file} 2>/dev/null
      echo "removed old recording $file."
    fi 
  done
}

function mark() {
  file=${OPENHAB_DIR}/sounds/$1

  tam=$(echo $1 | cut -d "." -f 2)
  index=$(echo $1 | cut -d "." -f 3)

  soapCall "$BASEURL/upnp/control/x_tam" "urn:dslforum-org:service:X_AVM-DE_TAM:1" MarkMessage dummy "<NewIndex>$tam</NewIndex><NewMessageIndex>$index</NewMessageIndex>"
}

### the fun starts here ###
source `dirname $0`/lockRoutines
exlock_now || exit 1

echo "lock obtained"

SID=$(soapCall "$BASEURL/upnp/control/deviceconfig" "urn:dslforum-org:service:DeviceConfig:1" "X_AVM-DE_CreateUrlSID" NewX_AVM-DE_UrlSID)

removeTamMessages

if [ $# -eq 1 -a "$1" == "update" ]; then
  updateCallOverview
elif [ $# -eq 2 -a "$1" == "mark" ]; then
  mark $2
  updateCallOverview
else
  echo "`basename $0` update | mark <file>"
  exit 1
fi

removeTamMessages

And I needed to change the ā€œplay recorded callā€ rule:

rule "Play recorded call"
when
   Item AbMessage received command
then
   if (receivedCommand.toString == "1") {
      playSound(Nachricht_1_Datei.toString)
	  executeCommandLine("/etc/openhab2/scripts/fritzbox.sh mark " + Nachricht_1_Datei.toString)
   }
   if (receivedCommand.toString == "2") {
      playSound(Nachricht_2_Datei.toString)
	  executeCommandLine("/etc/openhab2/scripts/fritzbox.sh mark " + Nachricht_2_Datei.toString)
   }
   if (receivedCommand.toString == "3") {
      playSound(Nachricht_3_Datei.toString)
	  executeCommandLine("/etc/openhab2/scripts/fritzbox.sh mark " + Nachricht_3_Datei.toString)
   }
   if (receivedCommand.toString == "4") {
      playSound(Nachricht_4_Datei.toString)
	  executeCommandLine("/etc/openhab2/scripts/fritzbox.sh mark " + Nachricht_4_Datei.toString)
   }
   
end

And the items are shown in a widget:
anrufe.widget.json (4.5 KB)
(preview please see my first link in this post)

Maybe that helps one of youā€¦ Have fun :grinning:

1 Like

Hi to all,
get this message after loading the script:

2018-09-22 03:24:36.057 [INFO ] [.eclipse.smarthome.model.script.rule] - lock obtained

downloading message list for TAM 0...wget: missing URL

Usage: wget [OPTION]... [URL]...

Try `wget --help' for more options.

0 bytes.

downloading call list...wget: missing URL

Usage: wget [OPTION]... [URL]...

Try `wget --help' for more options.

0 bytes.

Can anyone help?
Thanks and Greetings,
Markus

Okay, found it. :slight_smile:
https doesnā€™t work.
But know i have this problem:

2018-09-22 03:58:11.886 [WARN ] [smarthome.model.script.actions.Audio] - Failed playing audio file: Unsupported file extension!

I use the playsound rule from Jochen.
Thanks for help,
Markus

@der_optimist : Thanks for the scripts, this made things easier, the list is working for me.

For those struggeling with no data being shown in the widget and items not getting updated:
Be aware that the name of the openhab server is hardcoded in the calls to the REST API.

I added a small variable in the top section

OPENHAB_SERVER=<your_servername_here>

and replaced

openhabianpi

in the curl statements with

$OPENHAB_SERVER

For the port value 8080 you could do something similar in case you change the standard port.
This is easier to maintain and you wont run in the same (stupid me) issue that things do not work after copy&paste :wink:

@der_optimist
Is the code for the anrufe widget complete? Because it does not close the outer table.

Also, could you please share the used icons?

Thanks!

Hello,

Iā€™m new to OpenHab and tried your code. It works very fine, except the ā€œGenerate call overviewā€ rule. I have a FB 7590 with Fritz!OS 7.01, and there the fboxIncomingCall.state or fboxOutgoingCall.state is newer empty, it contains a ā€œ,ā€. I believe the items hold a StringListType with the caller number and called number.
So I changed the rule from

to

rule "Generate Call Overview"
when
   Item fboxIncomingCall changed or
   Item fboxOutgoingCall changed 
then
   
   if ((fboxIncomingCall.state.toString == ",") && (fboxOutgoingCall.state.toString == ",") && (previousState.toString != ",")) {
     	logInfo("Fritz Call", "update call overview")
	executeCommandLine("/etc/openhab2/scripts/fritzbox.sh update")
   }
end

Perhaps there is a better way, but at least it works for me;-)