Asterisk For a Small Business (III)

By | 2012-08-31

Last time we left ourselves in the position of having two outgoing trunks available and four locally connected phone extensions. Let’s remind ourselves of these endpoints.

$ asterisk -r
hostname*cli> sip show peers
Name/username              Host           Dyn Forcerport ACL Port  Status     
andyp/andyp                192.168.1.82    D                       OK (100 ms)
wife/wife                  (Unspecified)   D   0                   UNKNOWN    
handytone1/handytone1      192.168.1.10    D                 5060  OK (5 ms)
handytone2/handytone2      192.168.1.10    D                 5062  OK (6 ms)
voiptalkOFFICE/222222222   77.240.48.94        N             5060  Unmonitored
voiptalkRESIDENTIAL/11111  77.240.48.94        N             5060  Unmonitored
6 sip peers [Monitored: 3 online, 1 offline Unmonitored: 2 online, 0 offline]

Let’s now start thinking about what we want our phone system to do (or rather, what I want mine to do, and hope that you want something similar).

  • andyp, wife, handytone1 and handytone2 should all be able to ring each other internally.
  • andyp, wife, and handytone1 shall make outgoing calls on voiptalkRESIDENTIAL by default.
  • handytone2 shall make outgoing calls on voiptalkOFFICE by default.
  • Incoming calls to voiptalkOFFICE shall ring andyp and handytone2
  • Incoming calls to voiptalkRESIDENTIAL shall ring andyp, wife, and handytone1.

That’ll do us as a reasonable start. This time we’ll deal with the first of these requirements.

Recall one of our local SIP endpoint definitions:

[handytone1](user,ht386)
    secret=handytonesecret1
    callerid="Residential" <2102>
    context=internal-residential
    mailbox=household@default

In particular note the “context” option. This tells Asterisk which extension context incoming calls from this endpoint should be “run” in. What does that mean? To understand that, we need to start looking in /etc/asterisk/extensions.conf. This file creates what Asterisk refers to as its “dialplan”. Don’t be confused when you see it referred to like that.

My suggestion is to delete or comment out everything not in the “[general]” section, and even that is a lot of cruft. It’s a thorough example configuration, but just causes us difficulties when we’re making our own setup. In [general] I suggest making sure you have the following options set:

[general]
static=yes
writeprotect=yes
clearglobalvariables=no

With that out of the way, let’s begin with defining the “internal-residential” context that we’ve placed andyp, wife and handytone1 in. While we’re at it, we’ll define “internal-business” for handytone2.

[internal-residential]
include => internal-extensions
[internal-business]    
include => internal-extensions

To make these two contexts we’ve simply included a third (as yet not defined) context, “internal-extensions”. We’ll see why this is sensible later. Let’s now define this common context, but we’ll just write one extension for now:

[internal-extensions]
exten => andyp,1,Dial(SIP/andyp,20)
exten => andyp,2,Verbose(1,No answer from SIP/andyp)
exten => andyp,3,Hangup

Extensions are like little programs, the form of the “exten” line is

DIALSTRING,PRIORITY,COMMAND

You can see then we’re defining the andyp extension, i.e. this is what to do when someone calls the andyp extension in the internal-extensions context. Asterisk doesn’t mind whether you use all numeric names or, as I have here, all alphabetic names in a dial string. When processing starts, the priority is at one, and is incremented after each command completes. So you can run multiple commands in response to a single DIALSTRING. In this case, we’ve made it so that if someone dials the string “andyp”, Asterisk runs the Dial() function which rings an endpoint, in this case “SIP/andyp”. Note that it didn’t have to be “SIP/andyp” there is nothing that stop us (except common sense) from having the “andyp” extension dial the “SIP/wife” endpoint. If after 20 seconds, there has been no answer, then the next priority runs. That priority uses the Verbose() function to issue a message at verbosity level 1. The final priority, 3, hangs up.

It can get tedious maintaining these priority numbers, so Asterisk offers a short cut in the form of the special, “n” priority.

[internal-extensions]
exten => andyp,1,Dial(SIP/andyp,20)
exten => andyp,n,Verbose(1,No answer from SIP/andyp)
exten => andyp,n,Hangup

This simply means, “one more than the previous”. So this is identical in function to the first version.

There are other special priorities, but one in particular is important to us here. The “hint” priority.

[internal-extensions]
exten => andyp,1,Dial(SIP/andyp,20)
exten => andyp,n,Verbose(1,No answer from SIP/andyp)
exten => andyp,n,Hangup
exten => andyp,hint,SIP/andyp

With this in place run the asterisk command line again.

hostname*CLI> reload
hostname*CLI> core show hints

    -= Registered Asterisk Dial Plan Hints =-
                  andyp@internal-extensions : SIP/andyp State:Idle            Watchers  0
----------------
- 1 hints registered

Asterisk hints form a connection between an arbitrarily named extension and an endpoint. This connection allows use of the SIP SUBSCRIBE command. SIP phones can ask to be notified of the presence or absence of another extension. But extensions are not endpoints, so when, say, SIP/wife sends a “SUBSCRIBE andyp” command, Asterisk (without the hint) has no way of knowing which endpoint’s presence should be reported. The hint fixes that problem – Asterisk will now know.

Back to our extensions.conf. Do you notice that we wrote “andyp” a lot in that definition? If we ever renamed “andyp” it’s a lot of work to change. It’s also makes copy and pasting this entry to create others a lot more work. Let’s address that.

[internal-extensions]
exten => andyp,1,Dial(SIP/${EXTEN},20)
exten => andyp,n,Verbose(1,No answer from SIP/${EXTEN})
exten => andyp,n,Hangup
exten => andyp,hint,SIP/${EXTEN}

“${EXTEN}” is an Asterisk variable. It is expanded when processing the line. “${EXTEN}” in particular is a built-in variable and it expands to the name of the current extension. So now, provided we name our extensions the same as our SIP endpoints, we have made our extension handling generic.

[internal-extensions]
exten => andyp,1,Dial(SIP/${EXTEN},20)
exten => andyp,n,Verbose(1,No answer from SIP/${EXTEN})
exten => andyp,n,Hangup
exten => andyp,hint,SIP/${EXTEN}
exten => wife,1,Dial(SIP/${EXTEN},20)
exten => wife,n,Verbose(1,No answer from SIP/${EXTEN})
exten => wife,n,Hangup
exten => wife,hint,SIP/${EXTEN}

Our use of a variable has improved things, but this is still a lot of duplicated code. Further, if we add a new feature to the andyp extension, we have to remember to add it to the wife extension too. Wouldn’t it be better if we just had one copy of the extension handling code? Yep.

[stdexten]
exten => s,1,Verbose(1,Start stdexten ${ARG1},${ARG2})
exten => s,n,Set(LOCAL(ext)=${ARG2})
exten => s,n,Set(LOCAL(dev)=${ARG1})
exten => s,n,Dial(${dev},20)
exten => s,n,Goto(s-${DIALSTATUS},1)
exten => s-NOANSWER,1,Verbose(1,No answer from ${ext})
exten => s-NOANSWER,n,Return()
exten => s-BUSY,n,Verbose(1,${ext} is busy)
exten => s-BUSY,n,Return()
exten => _s-.,1,Goto(s-NOANSWER,1)

[internal-extensions]
exten => andyp,1,Gosub(stdexten,s,1(SIP/${EXTEN},${EXTEN}))
exten => andyp,hint,SIP/${EXTEN}
exten => wife,1,Gosub(stdexten,s,1(SIP/${EXTEN},${EXTEN}))
exten => wife,hint,SIP/${EXTEN}

Now we’re cooking. We’ve made a fairly large handler that behaves identically for both andyp and wife and yet doesn’t duplicate any code. I won’t go into exactly how [stdexten] works, the Asterisk documentation will tell you if you are interested (it’s not rocket science though, you should be able to guess just by looking at it here).

We can now add all of our endpoints to internal-extensions and have their hints set and have them callable. Should you have a genuine SIP phone which supports text extensions, you would then be able to call any of them. However, we need to cater for numeric keypads too. We can do that easily. Here’s our completed [internal-extensions] with numeric aliases that match the callerid options we gave in sip.conf.

[internal-extensions]
; Numeric aliases
exten => 2100,1,Goto(andyp,1)
exten => 2101,1,Goto(wife,1)
exten => 2102,1,Goto(handytone1,1)
exten => 2103,1,Goto(handytone2,1)
; Named extensions
exten => andyp,1,Gosub(stdexten,s,1(SIP/${EXTEN},${EXTEN}))
exten => andyp,hint,SIP/${EXTEN}
exten => wife,1,Gosub(stdexten,s,1(SIP/${EXTEN},${EXTEN}))
exten => wife,hint,SIP/${EXTEN}
exten => handytone1,1,Gosub(stdexten,s,1(SIP/${EXTEN},${EXTEN}))
exten => handytone1,hint,SIP/${EXTEN}
exten => handytone2,1,Gosub(stdexten,s,1(SIP/${EXTEN},${EXTEN}))
exten => handytone2,hint,SIP/${EXTEN}

Reload from the command line interface and you should then be able to dial extensions from each other just like you would on a much more expensive PBX.

Next time we’ll look at making outgoing calls on different trunks for different extensions.

asterisk1 asterisk2 asterisk3 asterisk4 asterisk5

Leave a Reply