Caller ID from Asterisk to all phones, MythTV’s and SqueezeBoxes
Well, I had some spare time and finally got round to getting CallerID sorted on my phones. I’ve had it enabled on the line for a while now, and even had it doing database lookups to display a name on the phone handsets as well as the number, tho’ that broke when I upgraded Asterisk. I have, however, sorted out that problem and expanded everything a little.
Now, when the phone rings, the number is looked up in a database of known numbers, then sent out to all the phone handsets as well as any active MythTV frontend or Squeezebox on the network! If we are watching the TV a box pops up with the time, date, number, and if found, name of the incoming call. Likewise if we are in the library where we can’t actually hear the phone, then the Squeezebox display will change to the name and number and we can decide wether to answer it or not!
All this is done using a feature in Asterisk call AGI, or Asterisk Gateway Interface. This is very similar to CGI scripts found on web pages. AGI scripts are run from the Asterisk dialplan to do various things. In this case the AGI perl script is run and the incoming call number is passed to it. This number is looked up in a database to see if its a known number. Wether it is known or not, the script sets the full CID info in Asterisk.
It then procedes to use a utility call cidbcast that is in the contrib directory of MythTV. This cidbcast sends out a UDP broadcast over the network to any listening machine, in this case the MythTV backend server which has the mythudprelay program running, another one from the MythTV contrib directory. This picks up the broadcast and sends the message to all MythTV frontends using mythtvosd.
Lastly the AGI script connects to the SlimServer which controls all the Squeezeboxes. Talking over the CLI port (9090) on the server it gets a list of all current players and proceeds to send a simple two line display message consisting of the number and name. The majority of this part of the script is a simple cut and paste of the relevant parts of the Squeezebox plugin written by Max Spicer.
All in all this is a fairly simple thing to implement, but is very useful for those as lazy as me!
Script
#!/usr/bin/perl use DBI; use IO::Socket; # MySQL settings for asterisk my $dbhost = 'localhost'; my $dbuser = 'asterisk'; my $dbpass = 'asterisk'; my $dbname = 'asterisk'; # Slimserver Settings my $serverAddress = 'mythbe-1'; my $serverPort = 9090; my $maxVolume = 75; my $displayTime = 30; my $debug = 0; # Mythtv Setting my $mythxmlfile = '/usr/lib/asterisk/cidbcast.xml'; ################ # Get name from mysql for the incoming number # $|=1; #Get the initial data my %input; while() { chomp; last unless length($_); if (/^agi_(\w+)\:\s+(.*)$/) { $input{$1} = $2; } } my $dbh = DBI->connect ("dbi:mysql:host=$dbhost:database=$dbname","$dbuser","$dbpass") or die "Can't connect to database: $DBI::errstr\n"; my $sth = $dbh->prepare( "SELECT cid FROM known_numbers WHERE source='$input{callerid}'" ); $sth->execute(); @row = $sth->fetchrow_array(); $cidname = @row[0]; print "SET CALLERID \"@row\"<$input{callerid}>"; ############### # Broadcast info to MythTV my $command; $command = "cidbcast --once --file=$mythxmlfile 6947 $input{callerid} \"$cidname\" line"; system("$command &> /dev/null"); ################ # Send out to squeezeboxes # Code ripped from Squeezebox CID plugin by Max Spicer # http://www.thespicers.net/cid.html # my $socket = IO::Socket::INET->new (PeerAddr => $serverAddress, PeerPort => $serverPort, Proto => 'tcp', Type => SOCK_STREAM) or die 'Couldn\'t connect to server'; # Get the number of players my $playerCount = sendAndReceive('player count ?'); $debug && print "$playerCount players found\n"; # Display message on each player and adjust volume if necessary for (my $i = 0; $i < $playerCount; $i++) { my $playerId = sendAndReceive("player id $i ?"); # Put the players display at max brightness if it's currently 0 my $powerState = sendAndReceive("$playerId power ?"); my $brightnessPref = $powerState ? 'powerOnBrightness' : 'powerOffBrightness'; my $brightness = sendAndReceive("$playerId playerpref $brightnessPref ?"); $debug && print "brightness: $brightness\n"; if ($brightness == 0) { sendAndReceive("$playerId playerpref $brightnessPref 4"); } $cidname =~ s/\s/%20/g; $debug && print("Sending: $playerId display Incoming%20call:%20$input{callerid} $cidname $displayTime\n"); sendAndReceive("$playerId display Incoming%20call:%20$input{callerid} $cidname $displayTime"); # Drop the volume if necessary my $playerMode = sendAndReceive("$playerId mode ?"); $debug && print "playerMode: $playerMode\n"; if ($playerMode eq "play" && sendAndReceive("$playerId mixer volume ?") > $maxVolume) { $debug && print "Decreasing volume\n"; sendAndReceive("$playerId mixer volume $maxVolume"); } } $debug && print "\n"; exit 1; # Send given cmd to $socket and return answer with original command removed from # front if present. Routine nicked from code by Felix Mueller. :-) sub sendAndReceive { my $cmd = shift; return if( $cmd eq ""); print $socket "$cmd\n"; $debug > 1 && print "Sent $cmd to server\n"; my $answer = <$socket>; $debug > 1 && print "Server replied: $answer\n"; $answer =~ s/$cmd //i; $answer =~ s/\n//; return $answer; }
Database table structure
mysql> describe known_numbers; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | source | varchar(13) | | PRI | | | | cid | varchar(26) | | | | | +--------+-------------+------+-----+---------+-------+