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
andhandytone2
should all be able to ring each other internally.andyp
,wife
, andhandytone1
shall make outgoing calls onvoiptalkRESIDENTIAL
by default.handytone2
shall make outgoing calls onvoiptalkOFFICE
by default.- Incoming calls to
voiptalkOFFICE
shall ringandyp
andhandytone2
- Incoming calls to
voiptalkRESIDENTIAL
shall ringandyp
,wife
, andhandytone1
.
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.