Incoming call popup under Ubuntu and Asterisk

When I worked in a survey firm, I was tasked with building a VOIP system to cut costs and to raise productivity. The biggest productivity drain in an outbound call center is the dialing time and getting someone on the line. By implementing an Asterisk server, we could control and expand the server to our needs. Furthermore, this meant we could have remote workers. We saved a bundle of money in long distance and in fixed costs. The hosted server and the bandwidth itself cost about 80$ a month, while the connectivity to the phone network was negligible and, more importantly, flexible. In other words, if it was a slow month, the cost was low, and conversely, if it was a very busy month, the costs were higher but the money was coming in.

Incoming call popup
Incoming call popup

Since it was my server and I was billing the company for it, I figured I could use the same server for my personal phones. So I decided to connect my PSTN numbers to this system. I could now use the server as my private VOIP server.

After configuring the VOIP server to my liking, I started to explore the Asterisk API and related Java and Python bindings. My first module was an interactive IVR system to manage callbacks from the survey outbound number. The callers could know who called them and remove their number from our calling lists.

Update (2012): Modified code for asterisk 1.6
Update (2014): Added code (end of page) for KDE

In the same vein, I wanted a caller ID popup on my laptop everytime somebody would call me. Since I use Ubuntu, I wanted the notifier to be tighly integrated with the OS. I used the native notifying system called LibNotifiy. This notifying system has bindings in python and is thus interfaceable from python. Asterisk is also interfaceable from python via an API and a python module. The first module (libnotify) should be installed by default on any recent Ubuntu distribution and the second module, StarPy, can be downloaded from here:  http://www.vrplumber.com/programming/starpy/

After installing the two preceding modules and lauching the following script, it will pop up a notification window each time you get a call. The configuration options are self-explanatory. The Asterisk Manager Interface and its firewall should be configured to allow access from the computer running the script. By adding the script in the startup application control panel, the script will launch at logon.

[sourcecode language=”python”]
#!/usr/bin/env python
”’
Created on 2009-11-15
@author: olivier@olihb.com
”’
from twisted.internet import gtk2reactor
gtk2reactor.install(True)
from twisted.internet import reactor
import pynotify
from starpy.manager import AMIFactory
from twisted.internet import task
import logging,gnome.ui,sys
#asterisk AMI login
server="asteriskServer"
port=5038
username="AMIusername"
secret="AMIpassword"
extensions={"SIP/1234":"First Phone",
"SIP/4321":"Second Phone"}
log = logging.getLogger("pyCalledMe2")
timeouttask=None
timeoutping=5
timeoutloop=120
class callMeFactory(AMIFactory):
cbconnect=None
def __init__(self):
AMIFactory.__init__(self, username, secret)
def connect(self):
df = self.login(server, port)
if self.cbconnect!=None:
df.addCallback(self.cbconnect)
def clientConnectionLost(self,connector,reason):
log.info("connection lost – connecting again")
reactor.callLater(1,self.connect)
def clientConnectionFailed(self,connector,reason):
log.info("connection failed – connecting again")
reactor.callLater(1,self.connect)
def onDial(protocol,event):
if ‘destination’ in event:
destination=event[‘destination’]
for s in extensions.keys():
if destination.startswith(s):
cid=event[‘callerid’] #if using asterisk 1.6, use calleridnum instead
cidname=event[‘calleridname’]
extname=extensions[s]
n=pynotify.Notification("Incoming call for "+extname,cidname+"\n"+cid,"call-start")
n.show()
def checknetlink(protocol):
def ontimeout():
log.info("timeout")
if dc.active():
dc.cancel()
timeouttask.stop()
protocol.transport.loseConnection()
def canceltimeout(*val):
if dc.active():
dc.cancel()
log.info("cancel timeout")
log.info(val)
def success(val):
pass
log.info("setting timeout")
dc = reactor.callLater(timeoutping,ontimeout)
df = protocol.ping()
df.addBoth(canceltimeout)
df.addCallback(success)
df.addErrback(ontimeout)
def onLogin(protocol):
df = protocol.registerEvent("Dial",onDial)
global timeouttask
timeouttask = task.LoopingCall(checknetlink,protocol)
timeouttask.start(timeoutloop);
return df
def main():
cm = callMeFactory()
cm.cbconnect=onLogin
cm.connect()
def killapp(*args):
reactor.stop()
return True
if __name__ == ‘__main__’:
#manager.log.setLevel( logging.DEBUG )
#log.setLevel(logging.INFO)
logging.basicConfig()
#exit script when gnome logs out
gnome.program_init ("pyCalledMe", "1.0")
client = gnome.ui.master_client()
client.connect("save-yourself", killapp)
#init libnotifier and reactor
pynotify.init("pyCalledMe");
reactor.callWhenRunning(main)
reactor.run()
[/sourcecode]
And for KDE:
[sourcecode language=”python”]
#!/usr/bin/env python
”’
Created on 2009-11-15
@author: olivier@olihb.com
”’
from twisted.internet import gtk2reactor
gtk2reactor.install(True)
from twisted.internet import reactor
import pynotify
from starpy.manager import AMIFactory
from twisted.internet import task
import logging,dbus
import signal
from starpy import manager
#asterisk AMI login
server="192.168.0.114"
port=5038
username="user"
secret="password"
extensions={"SIP/6002":"User1",
"SIP/6000":"User2"}
log = logging.getLogger("pyCalledMe2")
timeouttask=None
timeoutping=5
timeoutloop=30
class callMeFactory(AMIFactory):
cbconnect=None
def __init__(self):
AMIFactory.__init__(self, username, secret)
def connect(self):
df = self.login(server, port)
if self.cbconnect!=None:
df.addCallback(self.cbconnect)
def clientConnectionLost(self,connector,reason):
log.info("connection lost – connecting again")
reactor.callLater(1,self.connect)
def clientConnectionFailed(self,connector,reason):
log.info("connection failed – connecting again")
reactor.callLater(1,self.connect)
def onDial(protocol,event):
if ‘destination’ in event:
destination=event[‘destination’]
for s in extensions.keys():
if destination.startswith(s):
cid=event[‘calleridnum’]
cidname=event[‘calleridname’]
extname=extensions[s]
n=pynotify.Notification("Incoming call for "+extname,cidname+"\n"+cid,"call-start")
n.show()
def checknetlink(protocol):
def ontimeout():
log.info("timeout")
if dc.active():
dc.cancel()
timeouttask.stop()
protocol.transport.loseConnection()
def canceltimeout(*val):
if dc.active():
dc.cancel()
log.info("cancel timeout")
#log.info(val)
def success(val):
log.info("ping")
pass
log.info("setting timeout")
dc = reactor.callLater(timeoutping,ontimeout)
df = protocol.ping()
df.addBoth(canceltimeout)
df.addCallback(success)
df.addErrback(ontimeout)
def onLogin(protocol):
df = protocol.registerEvent("Dial",onDial)
global timeouttask
timeouttask = task.LoopingCall(checknetlink,protocol)
timeouttask.start(timeoutloop);
return df
def main():
cm = callMeFactory()
cm.cbconnect=onLogin
cm.connect()
def killapp(*args):
reactor.stop()
return True
if __name__ == ‘__main__’:
log.setLevel(logging.DEBUG)
logging.basicConfig()
#exit script when gnome logs out
signal.signal(signal.SIGTERM,killapp)
bus = dbus.SessionBus()
bus.call_on_disconnection(killapp)
#init libnotifier and reactor
pynotify.init("pyCalledMe");
reactor.callWhenRunning(main)
reactor.run()
[/sourcecode]

8 thoughts on “Incoming call popup under Ubuntu and Asterisk”

    1. I’m not sure why this wouldn’t work on Unity. Perhaps if you can let me know the errors you got I could make some suggestions, but I don’t have unity available myself. As far as I understand Unity includes Notify OSD, with which pynotify works.
      Are you sure the issue is not your version of Asterisk? There was a change to the AMI API, between Asterisk 1.4 and 1.6 I think. If you are using a more modern version of Asterisk you will need to change the line
      cid=event[‘callerid’]
      to
      cid=event[‘calleridnum’]
      If you can’t get things working you could try my callPopPy, which was originally based on pyCalledMe. It has the option to use an alternative, more portable notification method. It’s perhaps a bit more difficult to set up though:
      http://www.oak-wood.co.uk/callpoppy/

Leave a Reply

Your email address will not be published. Required fields are marked *