Asterisk For a Small Business (VI)

By | 2012-09-28

Last time I showed you how to add voicemail facilities to your incoming lines. We concentrated on the local end, how a single access number could pickup voicemail from different mailboxes depending on the extension it was dialled from. I didn’t discuss the receiving side very much, and simply gave you this template:

[external]
exten => voiptalkRESIDENTIAL,1,Verbose(1,Incoming call on callbackextension=${EXTEN})
exten => voiptalkRESIDENTIAL,n,Queue(residential,cnrtk,,,30)
exten => voiptalkRESIDENTIAL,n,Voicemail(household)
exten => voiptalkOFFICE,1,Verbose(1,Incoming call on callbackextension=${EXTEN} from ${CALLERID(all)})
exten => voiptalkOFFICE,n,Queue(business,cnrtk,,,30)
exten => voiptalkOFFICE,n,Voicemail(business)

This form has an error in it. Queue() rings your extension, you answer and have a conversation, then hang up. Your caller hasn’t hung up yet – the Queue() call returns and they are put through to Voicemail(). That’s unlikely to be what you want, when you hang up your extension that’s the end of the call, and the telephone system should hang up on the caller as well. We’ll deal with that problem, and reduce our code duplication by using a facility we have used before, subroutines.

[stdqueue]
exten => s,1,Verbose(1,Start stdqueue ${ARG1},${ARG2},${ARG3})
exten => s,n,Set(LOCAL(ext)=${ARG1})
exten => s,n,Set(LOCAL(queue)=${IF($[${ISNULL(${ARG2})}]?${ext}:${ARG2})})
exten => s,n,Set(LOCAL(mbx)=${IF($[${ISNULL(${ARG3})}]?${ext}:${ARG3})})
exten => s,n,Queue(${queue},${QUEUEFLAGS},,,${QUEUETIMEOUT})
; QUEUESTATUS The status of the call as a text string, one of TIMEOUT | FULL |
;    JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE
exten => s,n,Goto(s-${QUEUESTATUS},1)
exten => s-CONTINUE,1,Return()
exten => s-TIMEOUT,1,Voicemail(${mbx},u)
exten => s-TIMEOUT,n,Return()
exten => s-FULL,1,Voicemail(${mbx},b)
exten => s-FULL,n,Return()
; Treat anything else as no answer
exten => _s-.,1,Goto(s-TIMEOUT,1)

This is very similar to the stdexten subroutine we used in asterisk3 to standardise dialling an extension. This time the core of the subroutine is Queue() instead of Dial(), but the principle is the same. Note in particular

  • Voicemail() is only called on error conditions.
  • we have passed the appropriate type flag (u or b) to Voicemail() depending on the queue fault.
  • the queue to call and the fallback mailbox are parameters, but have reasonable defaults if they aren’t present.
  • the flags to the Queue() call are now a global variable, ${QUEUEFLAGS}, which you should set as you wish (probably cnrtkx).
  • the queue timeout is now a global variable, ${QUEUETIMEOUT}.

We can now update our [external] context to use this subroutine.

[external]
exten => voiptalkRESIDENTIAL,1,Gosub(stdqueue,s,1(${EXTEN},residential))
exten => voiptalkOFFICE,1,Gosub(stdqueue,s,1(${EXTEN},business))

We’ve given the name of the queue to call, and stdqueue takes care of setting the mailbox to the same name when we don’t supply an explicit mailbox.

One extra convenience I find is to move the stdqueue calls to the internal-extensions context and then Goto() them from the external context. That means you can then easily test your queues with internal calls only. Let’s look at the part of our dialplan that’s doing all the mapping.

[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)
exten => 2200,1,Goto(residential,1)
exten => 2201,1,Goto(business,1)
; Named extensions -- endpoints named after sip.conf sections
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}
; Named extensions -- ring groups
exten => residential,1,Gosub(stdqueue,s,1(${EXTEN}))
exten => business,1,Gosub(stdqueue,s,1(${EXTEN}))

[internal-residential]
include => internal-extensions
exten => _0X.,1,Dial(SIP/voiptalkRESIDENTIAL/${EXTEN})

[internal-business]
include => internal-extensions
exten => _0X.,1,Dial(SIP/voiptalkOFFICE/${EXTEN})

[external]
exten => voiptalkRESIDENTIAL,1,Goto(internal-residential,residential,1)
exten => voiptalkOFFICE,1,Goto(internal-business,business,1)

What have we achieved then?

  • Isolated all the call handling inside subroutines stdexten and stdqueue
  • Mapped the endpoints to extensions
  • Supplied hints to asterisk to let it map endpoints to extensions (which we made easy by giving them the same name ${EXTEN}=SIP/${EXTEN}).
  • Mapped the external lines to ring groups
  • Mapped the ring groups to extensions, which can be dialled internally as well as externally
  • Given everything a numeric alias
  • Made dial-out use different outgoing lines depending on which extension is used (set using the context= option in sip.conf)
  • Made it possible to map any endpoint onto any mailbox (set using the mailbox= option in sip.conf)

In short: we’re nicely organised. Adding new extensions, external lines or groups is a copy-and-paste job.

Next time I’ll talk about getting an Asterisk-generated dialtone.

asterisk1 asterisk2 asterisk3 asterisk4 asterisk5 asterisk6 asterisk7

Leave a Reply