For a while I had an
Asterisk-based VoIP setup at work, that was used for occasional experimenting with SIP and H.323 clients and forwarding local (area codes 303 and 720) calls to my celphone that still has area code 650 (SF Bay Area). It was ridiculously trivial for such a cool technology, so I have decided to put it to a more fitting use, or at least do something more or less practical. That meant, of course, that main office phone should be no longer directly connected to the outside line, and that Asterisk should take over handling of all incoming calls.
I have taken a punchdown tool, and switched the lines on our phone panel -- originally outside and inside lines were connected, now outside line goes to the FXO port, internal phone line to the FXS port.
The problem is,
Zoom 5801 SIP adapter that we have is FXO/FXS in name only. FXS is a device that talks to the phone while FXO talks to the outside phone network. PBX is a small phone switch, in this case a computer running Asterisk. It's usually assumed that a call coming from either of those interfaces can reach the VoIP PBX and be handled there. PBX in its turn can send a call through either of those interfaces -- inside or outside. This is not what Zoom 5801 does, at least within its documented capabilities. FXS part works great -- it turns a local analog phone into a SIP device, passes caller ID, ring, etc. FXO is umm... lacking. By default outside call comes straight to the analog phone, bypassing everything that even remotely looks like VoIP. A user can configure a mode where the call from outside is answered, and the caller hears another dialtone that is treated exactly like someone calling from the analog phone. Since this configuration would be a horrible security hole, it's possible to add a numeric password or restriction by called ID (as if it ever was hard to fake a caller ID), however the line is still treated exactly as an internal phone, as opposed to an outside caller that wants to hear something meaningful, not a dialtone. So that mode is of no help at all.
Another problem related to this was with the caller ID. After traveling through kinda-FXO-kinda-FXS bridge caller ID becomes mangled in some way that our analog phone doesn't like, and displays "incomplete data". So even letting those calls pass through means that I am going to lose functionality. Fortunately, there is a simple solution. I have an account with Voicepulse that allows free incoming for $11/mo. The phone number for that account (720-206-0669) can become a new primary incoming number at no additional cost. Our current main phone number (303-400-2936) can be simply
forwarded to it by pressing *72, waiting for a dialtone, then calling 720-206-0669. Since 720 and 303 are local area codes, call forwarding is free, and incoming calls at the new number are also free, it does not affect the cost of those calls, however it means that:
1. All calls to the main number will go through VoIP.
2. While an incoming call is passed through Asterisk, analog line still can be used to make an outgoing call or to send a fax.
The fax part is important because VoIP doesn't like passing faxes over anything but a direct link to the phone network or 64Kbps protocol. VoIP providers avoid 64Kbps protocols because they take exactly the same amount of bandwidth as regular phones, so fax is a separate service that I don't have.
I have configured Zoom 5801 to send all calls to Asterisk except ones dialed to 303 and 720 area codes without a 1, so local line still works as outgoing for them. Faxmodem was moved back to the outside line, so nothing that it can do will affect voice calls that go through Asterisk.
So now incoming calls to either number end up at Voicepulse, where they are passed to my Asterisk server over IAX2, the native Asterisk protocol, outgoing calls to local area codes go straight to the analog line, outgoing calls to everything else go through Asterisk. See something missing? There is no way to actually turn the call forwarding on because neither a phone nor Asterisk can dial *72. Again, the solution is simple -- one of the poorly documented configuration pages of Zoom 5801 lists a prefix code for forced dialing out from the phone, it's #8, so *72 becomes #8, wait for dialtone, *72, wait again for dialtone, then a number. Good enough for a one-time operation.
Now, the interesting part -- Asterisk itself. There would be no point in configuring a VoIP PBX at my work if it didn't handle at least two features -- menus and following the user as he becomes available at the different phones. Say, when I am at my desk at work, I can use Ekiga softphone, and the calls will be local. When I am at Starbucks, Paris on Platte or Cafe Sole, I may have a laptop or 802.11b phone on, so calls will go over a less reliable and slower network but will be still free. And if I am driving, the only way to reach me is over a celphone, what means potentially worse quality, dropouts, airtime charges plus 1 cent per minute for outgoing VoIP calls that go through Voicepulse.
Asterisk configuration files look more or less normal for a Unix application (ironically a format known as win.ini) -- except one,
extensions.conf. Its format is a reminder to all mortals that they are dealing not with just some piece of open source software, but a telephony application, something that belongs to a realm of unspeakable evil that could only descend from Ma Bell monopoly on passing vibrations over a distance.
The overall format is the same win.ini-like, with sections corresponding to contexts, and lines corresponding to
commands that have extensions and priorities defined for them. For anything to happen, extension (whatever was dialed somewhere) should match with some line within the context (that is initially assigned to a call by its origin, and may change as the call progresses). Sequence of commands is determined by a number called "priority".
In other words, the whole thing is a program. In a language. That is not designed as a programming language. I would say that this design sucks, however my all previous experience with telephony software says that this is more or less the only way how telephony is ever done -- with programming languages designed not like programming languages, definitions that are about as intuitive as genders in foreign language for someone speaking English, and data structures designed as if they are supposed to be carved in stone every time a call arrives somewhere. And multiple layers of changes that accumulated between versions due to implementations, incremental changes and reimplementations of both ideas and formats. Thankfully there is no Gordian knot of references to ITU documents that reference other ITU documents, that in practical application never get implemented exactly like they are written, so compared to other pieces of computer telephony software this is extremely simple, streamlined and easy to use. Hooray!
To make a menu, I needed voice prompts. I speak with a Russian accent, so I can never be sure if a phrase assembled from pieces that I have recorded in my own voice is understandable -- and if it is, an accent would be very distracting. On the other hand,
Festival's voices sound robotic but are neutral enough to be understood by everyone. Asterisk can call Festival directly, so initially when I was testing things, I just made Festival synthesize all speech on the fly. Once things became usable, I have made a script:
text2gsm:
#!/bin/sh
echo -n "Enter the text to pronounce > "
read text
echo ${text} | text2wave | sox -t wav - -r 8000 -c1 -v4 "/var/lib/asterisk/sounds/local/$1.gsm"
if [ -f "/var/lib/asterisk/sounds/local/$1.gsm" ]
then
ls -l "/var/lib/asterisk/sounds/local/$1.gsm"
else
exit 1
fiAn obvious reason for pre-made prompts was to reduce the load on the server, however it's more important that while Festival is talking Asterisk is deaf to the DTMF (key) tones being entered by the caller, so one has to wait for menu prompts to stop before entering a choice. Playing pre-made files with a Background command has no such problem, so I have replaced all calls to Festival except one (where it recites the Caller ID) to Background playing the files.
Another problem that I have found was more of an obstacle for testing. Caller ID for all "my" extensions was set to my celphone number, so every time I called myself and the call was forwarded to a cellular phone, Verizon seen my caller ID and thought, I am checking my voice mail. Voice mail system asked for a password, and a celphone didn't even ring, so before calling me this "program" had to check for my caller ID and replace it with an impossible number that I have also configured into a celphone to recognize with a special ringtone.
In the end, relevant sections of
extensions.conf looked like this:
[outgoing]
exten => _1NXXNXXXXXX,1,Set(CALLERID(all)=Belits Computer Systems <7202060669>)
exten => _1NXXNXXXXXX,n(calloutside),Dial(IAX2/xxxxxxxxxx:xxxxxxxxxx@connect01.voicepulse.com/${EXTEN})
exten => _1NXXNXXXXXX,n,Dial(IAX2/xxxxxxxxxx:xxxxxxxxxx@connect02.voicepulse.com/${EXTEN})
; Call from outside to the main number, company menu system
[voicepulse-user]
exten => _1NXXNXXXXXX,1,Answer()
exten => _1NXXNXXXXXX,n,Wait(1)
exten => _1NXXNXXXXXX,n,Goto(incoming-menu|s|1)
[incoming-menu]
;"Welcome to Belits Computer Systems."
;"Your phone number is"
exten => s,1,Background(local/welcome1)
exten => s,n,Festival(${CALLERIDNUM})
;"For office press one, for Valeriy press two, for Alex press zero"
exten => s,n(prompt),Background(local/welcome2)
;"Calling Alex"
exten => 0,1,Background(local/callingalex)
exten => 0,n,GotoIf($["${CALLERIDNUM}" = "6505555555"]?mangle:pass)
exten => 0,n(mangle),Set(CALLERID(number)=0001337734)
exten => 0,n(pass),Dial(SIP/abkphone&SIP/abelitswifi&SIP/ablaptop,60,t)
exten => 0,n,Goto(outgoing|16505555555|calloutside)
;"Calling Valeriy"
exten => 2,1,Background(local/callingvaleriy)
exten => 2,n,Goto(outgoing|17205555555|calloutside)
;"Calling Office"
exten => 1,1,Background(local/callingoffice)
exten => 1,n,Dial(SIP/belits-pstn,60,t)
;"Office phone does not answer."
exten => 1,n,Background(local/noansweroffice)
exten => 1,n,Goto(incoming-menu|s|prompt)
;"Extension"
exten => _x,1,Playback(local/extension)
exten => _x,n,Playback(local/digits/${EXTEN})
;"does not exist"
exten => _x,n,Playback(local/doesnotexist)
exten => _x,n,Goto(incoming-menu|s|prompt)
;"Extension hash does not exist."
exten => _#,1,Background(local/exthashdoesnotexist)
exten => _#,n,Goto(incoming-menu|s|prompt)
;"Extension star does not exist."
exten => _*,1,Background(local/extstardoesnotexist)
exten => _*,n,Goto(incoming-menu|s|prompt)
;"Thank you for calling Belits Computer Systems."
;"Goodbye"
exten => t,1,Playback(local/thankyouforcalling)
exten => t,n,Wait(1)
exten => t,n,HangupThis is all very simple, and does not include any voice mail, external checks for availability of users, time, etc., but it serves its purpose well, and can be more or less easily extended. For a few hours of work dealing with a "language" like this it's decent and far above IVR monstrosities that I have seen before, so again I have to admit that this is what counts as the state of the art in computer telephony now. Seriously.
Tags: hardware, software, voip