Paging Phones with Asterisk and C#

by Nicholas Piasecki on November 4th, 2008

Working as a programmer at a small e-commerce business means that, well, I’m not exactly programming all day, sitting at a computer with our order processing application open. Sometimes we’re all back in the warehouse picking orders, checking in items, or rearranging things. So it’s nice to have some sort of cue when new orders arrive during the middle of the day that need to be picked.

For about a year, I had things set up so that our order processing application polls the server for new orders every 5 minutes. If it detects that new orders have been placed since the last time it checked, then the software would use the Windows Speech API (SAPI) to speak an announcement like “There are six new orders” aloud. Pretty cool.

But there was one annoyance. Since each application was polling individually, you’d here a small echo as each employee’s individual copy of our application polled the server at a slightly different time and made the announcement through their computer’s speakers. Also, if you were on the phone with a customer and you forgot to disable the announcement in your copy of the application, the customer would hear a loud, disembodied voice saying “There are nine new orders” in a dull monotone. This was usually followed by the customer asking, “What the hell was that?!”

Since the feature was implemented, I built a VOIP phone system for our internal use in the office based on Asterisk. Asterisk has provided some great integration points; for example, when a customer calls us, Asterisk reports the caller ID to our application running on an employee’s machine, which can then display the correct customer account automatically. It’s pretty neat to have the customer’s orders on your screen before even saying hello. In a roundabout way, I was able to integrate Asterisk in the reverse: to have our software tell Asterisk to call some phones and play a message.

The easiest way to solve the problem of everyone’s machines making the new orders announcement at different times is to, well, only have one machine do that. So I wrote a quick and dirty console application that runs as a scheduled task on a server. That scheduled task then “pokes” Asterisk such that Asterisk pages the warehouse phones (leaving the office phones quiet, so as not to disturb employees helping customers) and announces something like “BEEP! Four new. BEEP!” before automatically disconnecting.

How did I accomplish this? The secret is to use Asterisk’s “.call” file capability. A call file is simply a plain text INI-esque file that you drop into a directory that Asterisk is always monitoring. If you’re familiar with the PickUp directory in Microsoft Exchange, it’s the exact same thing except for a phone system, located in /var/spool/asterisk/outgoing/ on our phone server.

Here’s an example of what a generated call file looks like for our scenario:

Channel: Local/99@incoming-from-server/n
CallerID: Skiviez Paging Service <9999>
Context: incoming-from-server
Extension: 1
MaxRetries: 0
Set: NumOrders=8

The scheduled task generates a call file like the one above and then just copies it over to the outgoing directory on the phone server (which I just shared via Samba). This file is a little confusing, so here’s the lowdown on what everything means:

  • Channel is the device that is doing the “dialing.” In our paging scenario, it’s the device that’s doing the talking. Here, I’ve used “local,” which means to create a made-up, temporary device that is “speaking” the dialplan at extension 99 of context “[incoming-from-server]“. More on this in a bit. (The “n” parameter tells it not to optimize away any context-level variables. If we left it out, then we wouldn’t be able to access our “NumOrders” variable described below from extension 99.)
  • CallerID is just what the phones that are being paged will display while connected.
  • Context and Extension indicate the context and extension that represents the device that “picks up” the dialed call. Note that this is NOT the same thing as the phones that we are trying to page with our message. This will all make sense in a minute.
  • Finally, MaxRetries tells Asterisk not to care if the page fails for any reason, and the Set is a variable that we pass to the context of the device that “dials” the call. In this case, we’re passing the number of orders that should be announced aloud over the page.

To see how this makes sense, let’s look at the corresponding entry in the extensions.conf file:

[incoming-from-server]
; This is what "picks up" the call file. It pages the available
; phones.
exten => 1,1,Answer()
exten => 1,2,SIPAddHeader(Call-Info: answer-after=0)
exten => 1,3,Page(SIP/9500&SIP/9501&SIP/9502|q)
exten => 1,4,Hangup()
 
; This is the local extension that the call file dials. All it
; has to do is pick up; it'll the dump the call into extension 1
; of this section as instructed by the call file.
exten => 99,1,Answer()
exten => 99,2,Wait(3)
exten => 99,3,Playback(beep)
exten => 99,4,SayNumber(${NumOrders})
exten => 99,5,Playback(local/new)
exten => 99,6,Wait(1)
exten => 99,7,Playback(beep)
exten => 99,8,Hangup()

So here’s what happens when the call file gets dropped. Asterisk picks up and then pretends to be a phone at extension 99. That phone dials another pretend phone at extension 1.

The extension 1 phone adds a header that’s necessary for our Grandstream GXP-2000 phones to pick up the page immediately, and then it pages the warehouse phones SIP/9500, SIP/9501, and SIP/9502 (these numbers aren’t real, but are useful for this example). They connect and start listening in on the conversation going on with the pretend phone at extension 1.

Meanwhile, the phone at extension 99 has waited around doing nothing for 3 seconds (since it takes a second or two for the warehouse phones to all pick up the page call from extension 1). Then it plays a beep, says the number of orders, says the word new, beeps again, and hangs up. The phone at extension 1 hears this, and since the (real) paged phones are listening to the pretend phone at extension 1, they playback the message that the pretend phone at extension 99 just said: “BEEP! 8 new. BEEP!”

Phew! It certainly took me a few minutes to figure it out, let me tell you, because I sure got myself spun around a few times. (This fake phone dials this fake phone which pages real phones which listen to the fake phone….) But, in the end, I took something that worked (our individual application instances making announcements) to something that works better (a single device paging phones simultaneously, which sounds more professional and is louder).

Is it Rube Goldberg-esque? Sure. But it Simply Works, and that’s good enough for me.

2 Comments
  1. Azam Ali permalink

    Great Article, Can you please tell, how you managed to integrate C# code with asterisk. as you said

    “Asterisk reports the caller ID to our application running on an employee’s machine, which can then display the correct customer account automatically. It’s pretty neat to have the customer’s orders on your screen before even saying hello.”

    and how you managed to run asterisk on windows platform, while they say it work on linux. I am looking on such examples. thanks.

  2. I just use C# to generate a text file ending in .call that the program generates and copies to Asterisk’s pickup directory. You can also use AGI to be notified of events. As far as actually getting C# to run on the Linux server itself and integrate with Asterisk, I’m not sure if that’s possible. I wouldn’t try to attempt it.

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS