Fritzbox call overview

call
fritzbox
habpanel
Tags: #<Tag:0x00007fd316942708> #<Tag:0x00007fd316942550> #<Tag:0x00007fd316942398>

(Dennis Dollinger) #1

Hi all,

just wanted to make a short tutorial about my fritzbox call overview i presented few months ago:

So all the magic is done by this things:

  • A script downloading the latest information from your fritzbox
  • A rule triggering the script as often as you want
  • A html page with a bit of javascript code which transforms the data in a webpage
  • A simple habpanel template widget which displays the html page

1. The script downloading the information from your fritzbox
This script is a mixture of some scripts I found.
All in all it requests a challenge from the fritzbox so you can get an SID, which is necessary to download the calllist information in .csv format.
I placed it here: openhabfolder/conf/scripts

!/bin/sh -x

IP="fritz.box"
UID="YOURFRITZBOXNAME"
SECRET="YOURPASSWORD"

# Challenge abholen

CHALLENGE=`wget -O - "http://$IP/login_sid.lua" 2>/dev/null | sed 's/.*<Challenge>\(.*\)<\/Challenge>.*/\1/'`

# login aufbauen und hashen

CPSTR="$CHALLENGE-$SECRET"

MD5=`echo -n $CPSTR | iconv -f ISO8859-1 -t UTF-16LE | md5sum -b | awk '{print substr($0,1,32)}'`

RESPONSE="$CHALLENGE-$MD5"

URL_PARAMS="username=$UID&response=$RESPONSE"

# login senden und SID herausfischen

SID=`wget -O - "http://$IP/login_sid.lua?$URL_PARAMS" 2>/dev/null | sed 's/.*<SID>\(.*\)<\/SID>.*/\1/'`

echo SID=$SID

echo TEMP = "http://$IP/fon_num/foncalls_list.lua?sid=$SID&csv="

wget -O /your/path/where/the/file/should/be/saved/name_of_file.txt "http://$IP/fon_num/foncalls_list.lua?sid=$SID&csv="
  1. The rule triggering the script
    In this case I created a rule, which executes the download script every minute
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.scripts.actions.*

rule "update calllist"
when
	Time cron "0 0/3 * * * ?" //every minute
then
	logInfo("Fritzbox"," Calllist wird neu geladen")
	executeCommandLine("sudo /path/to/your/script/name_of_your_script.sh")
end

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CallList</title>
    
	<style>
		.call-table {
			font-family: Segoe UI;
			-webkit-font-smoothing: antialiased;
			font-size: 100%;
			width: auto;
			overflow: auto;
			display: block;
            border-collapse: collapse;
            width: 100%;

		}
		.call-table th 
		{
			background-color: transparent;
			font-weight: normal;
			color: white;
			padding: 5px 30px;
			text-align: center;
			font-size: 115%;
            
		}
		
		.call-table td 
		{
			background-color: transparent;
			padding: 5px 5px;
			color: white;
            text-align:center;
            
		}

        .call-table tr {
            border-bottom: 1px solid white;
            
        }

	</style>
	
	<script language="javascript" type="text/javascript" src="jquery-3.1.1.js"></script>
		
    <script language="javascript" type="text/javascript">

        $(document).ready(function ()
	{
            read();
        });

        function read()
        {
		
			//Getting the downloaded txt-file from the fritzbox
			//Adapt the path to match your requirements
			xmlHttp = new window.XMLHttpRequest();
			xmlHttp.open("GET", "../relative/path/to/your/file/you/downloaded/name_of_your_file.txt", false);
			xmlHttp.send(null);
			xmlDoc = xmlHttp.response;
			
			var calls = new Array();
			
			var lines = xmlDoc.split('\n');
			
			//read each line
			//for(var i = lines.length - 1;i >= 0;i--)
			for(var i = 0; i < lines.length; i++)
			{
				if(lines[i])
				{
					console.log("->" + lines[i]);
					//split the string by ;
					singleValues = lines[i].split(";");
					
					//to skip the first lines (header) we ignore every line not beginning with a number
					if(!isNaN(singleValues[0])) 
					{
						//Loggings
						/*console.log(singleValues[0]);	//CallType
						console.log(singleValues[1]);	//Date
						console.log(singleValues[2]);	//ResolvedCallerName (if known)
						console.log(singleValues[3]);	//Called number or calling number
						console.log(singleValues[4]);	//*Nebenstelle*?!
						console.log(singleValues[5]);	//Called Number (If you have more than one phone number activated you can see which one was called or was calling)
						console.log(singleValues[6]);	//Duration
						*/					
						
						
						//create a call-object
						var call = new Object();
						call.calltype = singleValues[0];
						call.date = singleValues[1];
						call.resolvedname = singleValues[2];
						call.externnumber = singleValues[3];
						call.nebenstelle = singleValues[4];
						call.internnumber = singleValues[5];
						call.duration = singleValues[6];
						
						
						//add single call to array
						calls.push(call);
																					
					}
					
				}
				
			}
			
			var jsonArray = JSON.parse(JSON.stringify(calls))
			
			createTable(jsonArray);
        }
		
		//Creates the html table with the content
		function createTable(json)
		{
			//config
			var myNumber = 123456;			//at our home we use two different phone numbers. I just want to have my calls displayed. Therefore I'll filter for this number
//in case you don't want to filter don't use it or insert a number which does not exist
			
			//consts
			var trstart = "<tr>";
			var trend = "</tr>"
		
			//Every call in the json object
			$.each(json, function (index, value) 
			{
                                //checking if the number is the one you want the data is filtered for
                                //if you don't want to filter please delete the following 2 lines
				if (value.internnumber.indexOf(myNumber) == -1)
					return;
			
				var calltype = createTableCell("calltype", value.calltype);
				var date = createTableCell("date", value.date);
				var resolvedname = createTableCell("resolvedname", value.resolvedname);
				var externnumber = createTableCell("externnumber", value.externnumber);
				var nebenstelle = createTableCell("nebenstelle", value.nebenstelle);
				var internnumber = createTableCell("internnumber", value.internnumber);
					internnumber = internnumber.replace("Internet: ", "");
					var duration = createTableCell("duration", value.duration);

			    //Icon
				if (value.calltype == "1") //Type 1 = Eingehender Anruf
				    calltype = createTableCell("calltype", '<img src="callin.gif" />');
				else if (value.calltype == "2") //Type 2 = Nicht angenommener eingehender Anruf
				    calltype = createTableCell("calltype", '<img src="callinfailed.gif" />');
				else if (value.calltype == "4") //Type 4 = Ausgehender Anruf
				    calltype = createTableCell("calltype", '<img src="callout.gif" />');
								
				//Add the concatenated string at the end of our table
				$('#call-table tr:last').after(trstart + calltype + date + resolvedname + externnumber + nebenstelle + internnumber + duration + trend);
				
			});
			
				
		}
		
		//returns a table cell (td) with css class and content. cssclass if you want to customize your table
		function createTableCell(cssclass, content)
		{
			if(cssclass)
			{
				return "<td class=\"" + cssclass + "\">" +
					content + 
					"</td>";
			}
			
						
			return "<td>" +
						content + 
						"</td>";
		}
        
    </script>

</head>
<body>
	<table id="call-table" class="call-table">
		<thead>
			<th></th>
			<th>Datum</th>
			<th>Name</th>
			<th>Rufnummer</th>
			<th>Nebenstelle</th>
			<th>Eigene Rufnummer</th>
			<th>Dauer</th>
		</thead>
		
		<tbody>
			
		</tbody>
		
	</table>

</body>
</html>

3.The showed html page takes the downloaded data and transforms it to a html layout. At this point you can do better than me and make a prettier layout. I’m curious. Place it in the html folder under conf:

openhabfolder/conf/html

-I use jQuery (v 3.1.1). So please locate an appropriate file in the same folder. Find a version on https://code.jquery.com/jquery/

-Sorry for the css in the same file
-Sorry for the bad code

  1. Create a template widget in habpanel, referring the created html page
<div>
<iframe style=" width: 100%;height: 500px;" src="static/path/to/your/html/page.html">
  
</iframe>
</div>

I hope the instructions are complete and everyone who is interested can rebuild this little helper.
If not I’m willing to do my best to help you :slight_smile:

(Sorry for the bad formatting. Between 3. and 4. there is a little formatting problem :frowning: )


Examples of HABPanel Solutions
(Yannick Schaus) #2

Great! Thanks for sharing this!
Is there a reason why you didn’t use a “frame” widget rather than a template? It is supposed to offer similar functionality.


(Dennis Dollinger) #3

I’m not sure anymore why I chose the the template widget.
In my test environment I have both widget in use. Maybe it was because I had some trouble with the layout, which I wanted to solve by using the inline css.

Nevertheless, both are working :wink:


(Andreas Kuhn) #4

Hi Dennis,
i got this working. Thank you very much.
I had to add 4 more files to the same folder as the html page is located in:

  • jquery-3.1.1.js
  • callin.gif
  • callinfailed.gif
  • callout.gif

The jquery can be found via google no problem. The 3 others were first a problem but are also easy in the end. Simply go to your FirtzBox Call Statistics Webpage and save it as offline webpage.I used chrome doing so and by saving it you will also get the .gif files you need.

BUT one more issues i could not solve yet.
I’m testing an frame and template widget to display the statistic website. I’m using an iPad as control tablet and the website is exploding the widget container, means my 300+ entries are displayed as long long list and not a as defined within the container and scrollable :frowning:
I have tested a few browsers on iOS and it doesn’t matter what i use, it making it not better.

Do you have any idea here? As temp solution i’m limiting the table to 10 lines by replacing:
for(var i = 0; i < lines.length; i++)
with
for(var i = 0; i < 10; i++)

BR
Andreas


(Dennis Dollinger) #5

Hi Andreas,

your example should exactly do what you want. Since the entries are sorted by date your code should the 10 latest calls.

A few weeks ago a read, that now it’s possible to execute your own javascript code in your Habpanel widget. Maybe this is a chance to make a custom widget, which should solve the problem of formatting.

Dennis


(Andreas Kuhn) #6

Hi Dennis,

limiting it to 10 did indeed work fine and now looks good.

BUT, after having it now running a couple days i noticed the website will not update and reflect the latest calls.
The .txt file is up to date and is gathered every 2 mintes, but the html website seems not refreshed again.

Does this fully work for you? Could you maybe limit yours as well to 10 and see if this is the problem?
Unfortunately i have not much experience with javascript, so i’m not able to write this directly into a own widget :frowning:

Andreas


(Andreas Kuhn) #7

Soooo i got a bit further.
I was looking al the time to http://openhab:9001 and in the Log i saw every Minute the entry “Calllist wird neu geladen” which is the LogInfo Entry in the rules file. So the rules file seems to be executed, but then i looked at the timestamp of my .txt file and this one is old.
So the line executeCommandLine(“sudo /path/to/your/script/name_of_your_script.sh”) in the rules file is not executed.
I have set the permissions of the script to 777, so it should be possible for every user to execute it.

When i run the script manual via console i get the latest data and the .txt file is updated/created at all.
I have tried to delete the file so see what happen, but when the rule is running and logged in the log the .txt is not created.
I have tested without sudo but same issue.
Any other idea i can test?

Andreas


(Volker Bier) #8

Hi Dennis,

thanks for posting this. It inspired me to write a script that parses the calllist and generates html code that uses the nice habpanel template posted here: Custom Theme in HabPanel 2.1 Example. The result looks pretty good:

Instead of wiring the call to update the page to a cron expression, I have used an item changed trigger on the related fritzbox call monitor items of the tr-064 binding. This way the code is generated once after a new call has been made/received.

Regards,
Volker


(Volker Bier) #9

Hi Andreas,
there should be no need to call sudo when invoking the script. Root most likely has a different environment than the user you are using to test the script. Call the script directly (without sudo) and test it from the command line as openhab user to make sure it works and has needed permissions.
Copy the script path from the rule and paste it to the shell to make sure there is no typo.
I am using a script in the exact same scenario without root and after setting execute permissions there is nothing else to do.
Regards,
Volker


(Dennis Dollinger) #10

Hi Volker,

I like your Habpanel widget. I’m also following the custom theme post from ppmpk.
Would you mind to post your code? I’m would love to have my layout like yours.

Regards,
Dennis


(Volker Bier) #11

I will see if I can do that in the evening. But I have lots of other things to do, so you probably have to wait until tomorrow.


(Kersten Tams) #12

moin,
I want to use your code and try to setup it to my system.
I have a problem with the script (I think). When I execute it, the fritzbox return as the SID 0000000000. Can that be possible?
After this the text file is nearly empty (16 bytes).
Here is the output of the script. Can you help me please to figure out what goes wrong?

./fritzCallScript.sh: Zeile 1: !/bin/sh: Datei oder Verzeichnis nicht gefunden
./fritzCallScript.sh: Zeile 4: UID: Schreibgeschützte Variable.
SID=0000000000000000
TEMP = http://fritz.box/fon_num/foncalls_list.lua?sid=0000000000000000&csv=
--2017-08-02 15:52:00--  http://fritz.box/fon_num/foncalls_list.lua?sid=0000000000000000&csv=
Auflösen des Hostnamen »fritz.box (fritz.box)«... 2002:4fc2:2f9b:0:3681:c4ff:fece:731d, 192.168.178.1, fd00::3681:c4ff:fece:731d
Verbindungsaufbau zu fritz.box (fritz.box)|2002:4fc2:2f9b:0:3681:c4ff:fece:731d|:80... verbunden.
HTTP-Anforderung gesendet, warte auf Antwort... 200 OK
Länge: nicht spezifiziert [text/html]
In »»/etc/openhab2/html/fritzCall.txt«« speichern.

/etc/openhab2/html/fritzCall.txt                                        [ <=>                                                                                                                                                                 ]      16  --.-KB/s   in 0s     

2017-08-02 15:52:00 (256 KB/s) - »/etc/openhab2/html/fritzCall.txt« gespeichert [16]

I only change the UID and SECRET vars and the path to the text file.
The 16 bytes in text file are:

{"pid":"logout"}

Thanks for any tipp.


(Volker Bier) #13

Okay, here is the script. I have added the ability to play back the recorded calls yesterday. In case there
is a recorded call, you will see a cassette icon. It is blue as long as you have not yet listened to the call using openHAB, grey otherwise.

Call it fritzbox.sh, put it in the scripts folder in the openhab folder (on my system /etc/openhab2/scripts). Make it owned by openhab user and make it executable or adapt the rules according to the new location.

The top contains some environment variable definitions you may have/like to change, e.g. the username:password or the url for connecting to the fritzbox.

Updated version of the script a few posts down.

It sources another file that realizes a concurrency guard (I have downloaded it from somewhere on the internet). Call it lockRoutines and store it in the same folder:

## Copyright (C) 2009 Przemyslaw Pawelczyk <przemoc@gmail.com>
##
## This script is licensed under the terms of the MIT license.
## https://opensource.org/licenses/MIT
#
# Lockable script boilerplate

### HEADER ###

LOCKFILE="/var/lock/`basename $0`"
LOCKFD=99

# PRIVATE
_lock()             { flock -$1 $LOCKFD; }
_no_more_locking()  { _lock u; _lock xn && rm -f $LOCKFILE; }
_prepare_locking()  { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }

# ON START
_prepare_locking

# PUBLIC
exlock_now()        { _lock xn; }  # obtain an exclusive lock immediately or fail
exlock()            { _lock x; }   # obtain an exclusive lock
shlock()            { _lock s; }   # obtain a shared lock
unlock()            { _lock u; }   # drop a lock

### BEGIN OF SCRIPT ###

You will need the following rules. The first one triggers an update of the html code whenever a call is over, and the second one allows you to listen to the recorded calls by clicking on the tape icon on the page. The recording is then played using the default audio sink:

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")
   }
end

rule "Play recorded call"
when
   Item AbMessage received command
then
   playSound(receivedCommand.toString)
   executeCommandLine("/etc/openhab2/scripts/fritzbox.sh mark " + receivedCommand.toString)
end

For this to work, you need the following items (the latter two need the fritzboxtr064 binding with callmonitor enabled):

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" }

Additionally, a SVG icon file has to be prepared that contains the icons for recorded, missed, incoming and outgoing calls. Names and sizes can be configured in the top part of the script.

The habpanel widget code (this needs some more items from the fritzbox addon which I just copied from the addon description page and an additional phone icon):

<div class="section">
  <div class="title"><div class="name">Fritzbox</div></div>
  <div class="controls">
    <div class="widget">
			<div class="icon off"><svg viewBox="-70 -50 600 600"><use xlink:href="/static/matrix-theme/icons.svg#phone_474"></use></svg></div>
			<div class="name">Verpasste Anrufe</div>
			<div class="valueGroup"><div class="value">{{itemValue('fboxMissedCalls')}}</div></div>			
  	</div>
    <div class="widget">
			<div class="icon off"><svg viewBox="-70 -50 600 600"><use xlink:href="/static/matrix-theme/icons.svg#phone_474"></use></svg></div>
			<div class="name">Anrufbeantworter</div>
			<div class="valueGroup"><div class="value">{{itemValue('fboxTAM0NewMsg')}}</div></div>			
  	</div>
    <div class="widget" ng-if="itemValue('fboxIncomingCallResolved') != 'NULL'">
			<div class="icon off"><svg viewBox="-70 -50 600 600"><use xlink:href="/static/matrix-theme/icons.svg#phone_474"></use></svg></div>
			<div class="name">Anruf von</div>
			<div class="valueGroup"><div class="value">{{itemValue('fboxIncomingCallResolved')}}</div></div>			
  	</div>
    <div class="widget">
			<div class="icon off"><svg viewBox="-70 -50 600 600"><use xlink:href="/static/matrix-theme/icons.svg#phone_474"></use></svg></div>
			<div class="name">Handy **** im WLAN</div>
			<div class="valueGroup"><div class="value">{{itemValue('fboxMacOnlineI')}}</div></div>			
  	</div>
    <div class="widget">
			<div class="icon off"><svg viewBox="-70 -50 600 600"><use xlink:href="/static/matrix-theme/icons.svg#phone_474"></use></svg></div>
			<div class="name">Handy **** im WLAN</div>
			<div class="valueGroup"><div class="value">{{itemValue('fboxMacOnlineV')}}</div></div>			
  	</div>
  </div>
</div>
<div ng-include="'http://openhabianpi:8080/static/calloverview.html'"></div>

A remaining problem is that the icon color does not change when you listen to a recorded message. In order to get this to work I would have to manipulate the icon class somehow, or trigger a reload of the page.

I hope I have not forgotten anything. Have fun.

Edit: I just updated the script to fix the problem with the icon color.


(Andreas Kuhn) #14

Hi Volker,
wow thanks for sharing your Panel and script. I will test this later.
I did a quick test without sudo and it’s still not working.

I know i have to enable the “openhab” user before i can use it and i have to set a pw. I have read this in a few other posts but never have done it. Do you have noted down the steps to get this user ready to be used via “su -l openhab” ?

BR
Andi


(Volker Bier) #15

The user does not necessarily have to be called openhab, it is just the user that is running the openhab process. I use raspianpi as OS and this creates the openhab user and sets up openhab to start with this user. If you use something else, check which user you need by typing

ps -ef |grep openhab2

This should show you the openhab java process which should like this:

openhab  16217 16066 10 Jul22 ?        1-06:49:32 /usr/bin/java -Dopenhab.home=/usr/share/openhab2 -Dopenhab.conf=/etc/openhab2 -Dopenhab.runtime=/usr/share/openhab2/runtime -Dopenhab.userdata=/var/lib/openhab2 -Dopenhab.logdir=/var/log/openhab2 -Dfelix.cm.dir=/var/lib/openhab2/config -Djetty.host=0.0.0.0 -Dorg.ops4j.pax.web.listening.addresses=0.0.0.0 -Dorg.osgi.service.http.port=8080 -Dorg.osgi.service.http.port.secure=8443 -Djava.awt.headless=true -Dgnu.io.rxtx.SerialPorts=/dev/ttyElero:/dev/ttyJeelink:/dev/ttyS0:/dev/ttyAMA0 -Djava.endorsed.dirs=/usr/lib/jvm/java-8-oracle/jre/jre/lib/endorsed:/usr/lib/jvm/java-8-oracle/jre/lib/endorsed:/usr/share/openhab2/runtime/lib/endorsed -Djava.ext.dirs=/usr/lib/jvm/java-8-oracle/jre/jre/lib/ext:/usr/lib/jvm/java-8-oracle/jre/lib/ext:/usr/share/openhab2/runtime/lib/ext -Dkaraf.instances=/usr/share/openhab2/runtime/instances -Dkaraf.home=/usr/share/openhab2/runtime -Dkaraf.base=/var/lib/openhab2 -Dkaraf.data=/var/lib/openhab2 -Dkaraf.etc=/var/lib/openhab2/etc -Dkaraf.restart.jvm.supported=true -Djava.io.tmpdir=/var/lib/openhab2/tmp -Djava.util.logging.config.file=/var/lib/openhab2/etc/java.util.logging.properties -Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true -classpath /usr/share/openhab2/runtime/lib/boot/org.apache.karaf.diagnostic.boot-4.0.8.jar:/usr/share/openhab2/runtime/lib/boot/org.apache.karaf.jaas.boot-4.0.8.jar:/usr/share/openhab2/runtime/lib/boot/org.apache.karaf.main-4.0.8.jar:/usr/share/openhab2/runtime/lib/boot/org.osgi.core-6.0.0.jar org.apache.karaf.main.Main

In the first column is the name of the user running openhab. This is the one you need.

P.S. I updated the script to fix the icon problem.

Edit: One more thing that might help finding the problem: if you change the call to the script to

val result = executeCommandLine(“sudo /path/to/your/script/name_of_your_script.sh”, 5000)
logInfo("rule", result)

you will get the stdout and stderr output from the call in the openhab log.


(Volker Bier) #16

Another script update. I have now added functionality that marks new recorded calls as old on the fritzbox (once you listen to them with habpanel) so that connected handsets (and the fritzbox itself) will stop blinking.

#!/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="openhab:openhab"
# openhab directory containing sounds subdirectory
OPENHAB_DIR=/etc/openhab2
# call overview file
OUT=$OPENHAB_DIR/html/calloverview.html
# temporary directory
TMP=/tmp

# number of calls to show
COUNT=10
# number of answering machines
TAM_COUNT=1
# URL of the SVG file containing the icons
SVG_URL=/static/matrix-theme/icons.svg
# icon name for recorded calls
CASSETTE_NAME=cassette_65
# svg viewbox for recorded calls
CASSETTE_BOX="0 0 65 65"
# icon name for incoming calls
CALL_IN_NAME=phone-in_512
# svg viewbox for incoming calls
CALL_IN_BOX="-50 -50 612 612"
# icon name for outgoing calls
CALL_OUT_NAME=phone-out_512
# svg viewbox for outgoing calls
CALL_OUT_BOX="-50 -50 612 612"
# icon name for missed calles
CALL_MISSED_NAME=phone-missed_512
# svg viewbox for missed calls
CALL_MISSED_BOX="-50 -50 612 612"

### 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)
    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

      echo "<div class=\"widget\">">>$OUT
    else 
      soundfile=${OPENHAB_DIR}/sounds/`basename $path`.wav

      # 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=\"rec${callidx}='icon off'; sendCmd('AbMessage', '`basename $soundfile`')\">">>$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
        echo "<div class=\"icon off\"><svg viewBox=\"${CASSETTE_BOX}\"><use xlink:href=\"${SVG_URL}#${CASSETTE_NAME}\"></div>">>$OUT
      else 
        echo "<div class=\"{{rec$callidx}}\"><svg viewBox=\"${CASSETTE_BOX}\"><use xlink:href=\"${SVG_URL}#${CASSETTE_NAME}\"></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}/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

This is only tested with one TAM on the fritzbox, but should work with more than one as well. I have now stopped working on this, as it has all the functionality i need.

Regards,
Volker


(Andreas Kuhn) #17

Hi Volker,
i am using openhabian and my process is started with the “openhab” user so this was the correct hint. I performed an sudo -u openhab ./fritzboxcalls.sh and it was working. So the user couldn’t be the problem.

I then tried your next hint:
val result = executeCommandLine(“sudo /path/to/your/script/name_of_your_script.sh”, 5000)
logInfo(“rule”, result)

And this pointed me to a problem in the script.
For the wget path i had this in place, which worked fine when it got executed manually:
wget -O …/html/fritzboxcalls.txt “http://$IP/fon_num/foncalls_list.lua?sid=$SID&csv=

But called via the rules file i had to change to the full path:
wget -O /etc/openhab2/html/fritzboxcalls.txt “http://$IP/fon_num/foncalls_list.lua?sid=$SID&csv=

So this variant is now working and i try to get your script now running.
Thanks again

BR
andi


(Kersten Tams) #18

hi,
thank you for this wunderfull work :slight_smile:
The script and rules works fine. I only have a problem with the svg file. No icons are shown and the calloverview.html also not.
Can you please share the svg file too?
Thamks in advance.


(Volker Bier) #19

Hi Kersten,

the icons are not free, so i cannot share them with you. But i drew some myself. Save the following text as fritzbox.svg to the matrix theme dir:

<?xml version="1.0" encoding="utf-8"?>
<!--   -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
         viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<symbol id="phone_100">
  <path 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'/>
</symbol>
<symbol id="phone-in_100">
  <path 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 d='M 60 40 l 0 -12 l 3 3 l 10 -10 l 6 6 l -10 10 l 3 3'/>
</symbol>
<symbol id="phone-out_100">
  <path 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 d='M 79 21 l 0 12 l -3 -3 l -10 10 l -6 -6 l 10 -10 l -3 -3'/>
</symbol>
<symbol id="phone-missed_100">
  <path 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 d='M 61 41 l -3 -3 l 6 -6 l -6 -6 l 3 -3 l 6 6 l 6 -6 l 3 3 l -6 6 l 6 6 l -3 3 l -6 -6'/>
</symbol>
<symbol id="cassette_100">
  <polyline style="fill:none;stroke-width:3" points="20,20 80,20"/>
  <polyline style="fill:none;stroke-width:3" points="10,13 13,10, 87,10 90,13 90,70 10,70 10,13"/>
  <polyline style="fill:none;stroke-width:3" points="20,70 30,55 70,55 80,70"/>
  <circle style="fill:none;stroke-width:3" cx="30" cy="38" r="8" />
  <circle style="fill:none;stroke-width:3" cx="70" cy="38" r="8" />
</symbol>
</svg>

Adapt the location in the script and change the icon names and svg view boxes:

# URL of the SVG file containing the icons
SVG_URL=/static/matrix-theme/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"

I hope this works. And be a little bit forgiving, it was my first attempt at drawing svg icons.


(Kersten Tams) #20

hi,
WOW!
Thank you very very much.
The icons looks very nice and all works now without problems.
What program do you use to create svg pictures? I use debian linux and failed to find a program to create, edit or even display the icons you created.
Perhaps I can colorize the icons :wink: