Asterisk 1.4 - The Professional's Guide

By Colman Carpenter , David Duffett , Ian Plain and 1 more
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. The Dialplan

About this book

Asterisk is the leading Open Source Telephony application and PBX software solution. It represents an effective, easy-to-administer, and accessible platform for running enterprise telephony requirements. The real world, however, offers numerous hurdles when running Asterisk in the commercial environment including call routing, resilience, or integrating Asterisk with other systems. This book will show you some of the ways to overcome these problems.

As the follow-up to Packt's highly successful 2005 title Building Telephony Systems with Asterisk, this book presents the collected wisdom of Asterisk Professionals in the commercial environment.

Aimed at Administrators and Asterisk Consultants who are comfortable with the basics of Asterisk operation and installation, this book covers numerous hands-on topics such as Call Routing, Network Considerations, Scalability, and Resilience – all the while providing practical solutions and suggestions. It also covers more business-related areas like Billing Solutions and a Winning Sales Technique. Even if your interest or experience with Asterisk is lower level, this book will provide a deeper understanding of how Asterisk operates in the real world.

Asterisk is deployed across countless enterprises globally. Running on Linux, it has constantly demonstrated its resilience, stability, and scalability and is now the advanced communication solution of choice to many organizations and consultants.

With a foreword from Mark Spencer, the man behind Asterisk, this book presents the accumulated wisdom of three leading Asterisk Consultants and shows the reader how to get the most out of Asterisk in the commercial environment. Over the course of eleven chapters, this book introduces the reader to topics as diverse as Advanced Dial Plans, Network Considerations, and Call Routing, through to Localization, DAHDI, Speech Technology, and Working with a GUI. The book also covers the more nebulous aspects of being an Asterisk professional such as evaluating customer requirements and pitching for contracts.

This book represents the wisdom and thoughts of front line consultants. The knowledge they impart will prove informative, thought provoking and be of lasting interest to Asterisk professionals.

Publication date:
August 2009
Publisher
Packt
Pages
284
ISBN
9781847194381

 

Chapter 1. The Dialplan

The dialplan is the routing core of an Asterisk server. Its sole role is to look at what is dialed, and route the call to its destination. This is the core of any telephony system and Asterisk is no different.

The dialplan is made up of three elements—extensions, contexts, and priorities. An extension is number or pattern that the dialed number is to be matched against and a context is a collection of extensions (and possibly other included contexts too). Each extension will have one or more priorities, each of which appear on a separate line, and the priority sequence always starts with the priority "1".

If you have read Building Telephony Systems with Asterisk, you will know how to use extensions, priorities, contexts, and included contexts to handle incoming and outgoing calls as well as to set up features such as:

  • Call Queues

  • Call Parking

  • Direct Inward Dialling

  • Voicemail

  • Automated Phone Directory

  • Conference Rooms

In this chapter, we will build on this knowledge by looking at:

  • Significant updates since Asterisk 1.2

  • Pattern ordering within and between contexts

  • Extending the dialplan with variables

  • The DEVSTATE() function

  • The SYSTEM application

We will then use this knowledge to provide examples of:

  • Advanced call routing with the DEVSTATE() function

  • Call routing based on the time of the day

  • Using multiple ADSL lines within Asterisk to boost call capacity

Dialplan location

The dialplan is primarily defined in the extensions.conf file. This can also include additional files that are added into it using the #include directive. For instance, systems using the FreePBX GUI will have extensions_additional.conf, extensions_custom.conf, and extensions_override_freepbx.conf as standard files, which have been added using #include into the extensions.conf file. We must also remain aware of files such as the features.conf file, as they also include numbers that can be dialed such as codes for Pickup and Call Parking, and so form part of the dialplan.

A list of standard and optional Asterisk configuration files can be found at http://www.voip-info.org/wiki/view/Asterisk+config+files.

 

Dialplan location


The dialplan is primarily defined in the extensions.conf file. This can also include additional files that are added into it using the #include directive. For instance, systems using the FreePBX GUI will have extensions_additional.conf, extensions_custom.conf, and extensions_override_freepbx.conf as standard files, which have been added using #include into the extensions.conf file. We must also remain aware of files such as the features.conf file, as they also include numbers that can be dialed such as codes for Pickup and Call Parking, and so form part of the dialplan.

A list of standard and optional Asterisk configuration files can be found at http://www.voip-info.org/wiki/view/Asterisk+config+files.

 

Extensions and contexts


Being familiar with Asterisk, you will have a good working understanding of extensions and contexts already. They are, of course, the very heartbeat of Asterisk, and as such they are probably subject to the most change from version to version, as Asterisk evolves to cater for new hardware, software, and more complex working practices. So let's have a quick review of extensions and contexts, pointing out significant changes in versions 1.4 and 1.6, before we proceed to the more advanced techniques and uses.

Pattern matching

Within the dialplan, matching can be either direct or partial against a pattern. Normally in a PBX, these patterns are numeric. But with Asterisk, they can also be alphanumeric or even just alpha. For example 2000, DID01234123456, and Main_number are all valid extensions. As very few phones contain alphabetic keys, the last two are typically only used for incoming DID channels. For the majority of this chapter, we will stick to numeric patterns.

Let's start to explore pattern matching by looking at an extremely simple dialplan:

[context_1]
exten => 123,1,Answer()
exten => 123,n,SayDigits(999${CALLERID(num)})
exten => 123,n,Hangup()

In this dialplan, when a user with a context of context_1 dials 123, they will hear 999 and their caller ID will be read back to them.

Now let's look at a slightly more complex context:

[context_1]
exten => _1X.,1,Answer()
exten => _1X.,n,SayDigits(${EXTEN}${CALLERID(num)})
exten => _1X.,n,Hangup()
exten => 123,1,Answer()
exten => 123,n,SayDigits(123${CALLERID(num)})
exten => 123,n,Hangup()

You might expect that 123 would match against the _1X. extension, as that appears first in the context. However, the way Asterisk orders the dialplan when loading means that exact matches are checked for before pattern matches. Hence if you dial 123, it matches against the 123 pattern first and not the _1X. pattern. This pattern would only route the call if an exact match did not exist in the context.

Note

It is sensible not to use the pattern _. as a catch-all pattern, as this will catch the Asterisk special extensions like i, t, h as well. It is far better to use the _X pattern.

Once understood, pattern matching is pretty straightforward and does what we expect. However, if you introduce included contexts into the mix, things may work in a way you did not expect and the order needs to be thought through carefully. In particular, it's crucial to understand that Asterisk only checks included contexts after checking for exact matches and pattern matches in the local context. The following example illustrates this:

[context_1]
include => context_2
exten => _1X.,1,Answer()
exten => _1X.,n,SayDigits(${EXTEN}${CALLERID(num)})
exten => _1X.,n,Hangup()
include => context_3
exten => 123,1,Answer()
exten => 123,n,SayDigits(123${CALLERID(num)})
exten => 123,n,Hangup()

The above dialplan is sorted internally by Asterisk shown as follows, and you can see that though the included contexts are at the top and in the middle, the local context is read first, then the included contexts are read in the order that they were added. Hence, in this case, a dial string of 122 would be matched by the _1X. pattern before the included contexts are searched.

'123' => 1. Answer()
2. SayDigits(123${CALLERID(num)})
3. Hangup()
'_1X.' => 1. Answer()
2. SayDigits(${EXTEN}${CALLERID(num)})
3. Hangup()
Include => 'context_2'
Include => 'context_3'

Note

If you have a catch-all pattern in your dialplan, consider putting it into a separate context. You can then use the include directive to append that context to the end of the active context, thus ensuring that all of the other pattern matching is attempted first.

One of the most powerful tools you will use on the Asterisk command line is dialplan show <exten>@<context>. For example:

dialplan show [email protected]_1

This will show you the matching order that Asterisk will use for the given extension in the specified context, and if there are matches in any included contexts, those contexts will be explicitly identified.

Finally, in a context you may have a switch statement, which includes the dialplan of an external system into the local dialplan. In essence, it's an include for remote systems. Though typing dialplan show will always show the switch statement at the bottom, the defined context on the remote system is searched after the local context on your system and before any local included contexts! So again, you have to be very careful as to what is the context on the remote system as this will be searched before your included contexts.

The syntax of the switch state is as follows:

switch =>IAX2/user:[key]@server/context

The user and key are defined in the called server's iax.conf file, and the context is, of course, in the server's dialplan.

Why use contexts?

In our examples so far we could have achieved the desired results very easily without the use of multiple contexts. The simple functionality we have looked at could be carried out in a single, all-encompassing context. In practice, this approach could be applicable for systems with a very limited number of users and trunks, and with very restricted functionality, as there may not be a need to restrict the calling habits of a subset of users.

Use of contexts becomes desirable when we need to offer different options to different users. This is likely to be most applicable in medium and large companies, where you may have "users" ranging from the CEO down to an emergency phone in a lift. However, it can also be the case in smaller companies, where you might want to restrict home workers from making international calls for instance. When you get many different types of users, writing a distinct dialplan for each becomes problematic. The sheer size and complexity of the dialplan will make code management very complicated.

To simplify things, we first need to think about what makes the dialplan for each extension different. Then we need to think about what remains the same for each extension, as this needs to be made to work as well. What we often find is that most of these differences can be stored and called in two main ways:

  • The user's context

  • Variables linked to that user

We will come to variables shortly, but the grouping of extensions into contexts allows us to separate concise and distinct functions from each other. In doing so, we can control very tightly which contexts are used in each scenario, and also implement one "master" copy of each distinct function, aiding maintenance of the code.

Call barring made simple

To illustrate, let's expand our context a bit and use call barring as an example. We will initially have three levels for this example local, national, and international.

These are defined as follows:

  • Any number starting with a 1-9 is local

  • Anything starting with a 00 is international.

  • Anything else starting with a 0 is national or a mobile number.

This is a simplified example, and uses the UK format of dial prefixes.

We have in this example three contexts local_num, national_num and international_num. These would correspond to the levels of access we have decided on for our users. For example, an executive phone would be allowed access to all numbers whereas a phone on the shop floor may only be allowed access to local numbers.

We will create the three contexts shown as follows. All we are doing in our example is reading back 1, 2, or 3 to indicate the pattern that has been matched followed by the number dialed ${EXTEN}.

[local_num]
Exten => _Z.,1,Answer()
Exten => _Z.,n,SayDigits(1${EXTEN})
Exten => _Z.,n,Hangup()
;
[national_num]
Exten => _0Z.,1,Answer()
Exten => _0Z.,n,SayDigits(2${EXTEN})
Exten => _0Z.,n,Hangup()
;
[international_num]
Exten => _00X.,1,Answer()
Exten => _00X.,n,SayDigits(3${EXTEN})
Exten => _00X.,n,Hangup()

For each context we could write an ordered list to cover all patterns, but it is much neater to create a master context for each user. For example:

[local]
Include => local_num
[national]
Include => national_num
Include => local_num
[international]
Include => international_num
Include => national_num
Include => local_num

Therefore, in the previous example, a user with the national context can dial a normal national number, but not an international number. A user with the international context has the ability to dial both numbers.

This is a pretty simple example with just three level of access, but the modular nature due to the use of contexts allows us to expand it very quickly and easily. For example, we have a user 1000 (our CEO) and he can dial internationally. We also have 1098 and 1099, which are users on the shop floor, and can dial reception and the emergency services.

In this example, we give our CEO a context of [supauser],while the shop floor has a context of [emergencyuser].

The [supauser] context has to be able to dial everything, so it looks like this:

[supauser]
include => premium_num ; allows dialing to premium rate numbers
include => international_num ; allows international dialing
include => national_num ; allows national calls
include => mobile_num ; allows calls to mobile phones
include => local_num ; allows local rate calls
include => free_num ; allows free calls such as 800 or operator services
include => internal_num ; allows the calling of extensions
include => emergency ; allows calls to the emergency services
include => default ; allows access to system features

The shop floor just has the following context:

[emergencyuser]
include => emergency ; allows calls to emergency services reception.

As you can see, we can mix and match these contexts to cover many different types of extensions. Although you may be asking, "Will this really save me time?" well, let's look at two examples. Firstly, our supplier reduces the cost of UK 0870 numbers to free in the evenings as has happened in the UK with BT(British Telecom). Secondly, we also want the shop floor phone (1099) to be able to dial extensions and toll free calls, but not change the dialplan for 1098.

We will deal with the simplest of these extensions (1099) first. All we need to do is change the context associated with this user to a new context called [freeuser]:

[freeuser]
include => free_num ; allows calls to free numbers
include => internal_num ; allows the calling of extensions
include => emergency ; allows calls to the emergency services
include => default ; allows access to system features .

This is a fast and easy change, which will have no effect on other shop floor users.

And to the change to 0870 numbers, this once again can be put into effect very simply. The only change is that evening and weekend calls are now free. Therefore, we could put it into a [free] context. Although, it isn't always free. It is free only at weekends which would not be suitable. Hence, for this we use the GotoIfTime application, which sets the context, extension, and priority in the channel based on the system time, day, date, and month supplied by the OS.

By adding the following to the free context, users can now dial 0870 numbers at the defined times.

exten => _0870XXXXXXX,1,GotoIfTime(17:59-08:00,mon-fri,*,*?national, ${EXTEN},1)
exten => _0870XXXXXXX,1,GotoIfTime(*,sat-sun,*,*?national,${EXTEN},1)

In this case, we have made a change for all users who also have a context allowing both local and free calls (as their context includes the free context).

Time and day call routing

The GotoIfTime() application can introduce some powerful functionality into your dialplan if used properly. An example that follows is for a support company where calls are routed to the call centre or staff member on call at a specific time. The customer had centers round the globe and we routed the calls to whichever center was open at that time of day.

[folthesun]
;
;This section sets the constants and variables for numbers and times
;Nine timezones are defined to allow for 4 a day and sat and sun working
;At present there are 6 destinations for NA AU and EMEA
;
exten => s,1,set(__tzone1=00:00-07:59)
exten => s,n,set(__tzone2=08:00-17:30)
exten => s,n,set(__tzone3=17:31-23:59)
exten => s,n,set(__tzone4=17:31-23:59)
exten => s,n,set(__tzone5=00:00-23:59)
exten => s,n,set(__tzone6=00:00-23:59)
exten => s,n,set(__tzone7=00:00-23:59)
exten => s,n,set(__tzone8=00:00-23:59)
exten => s,n,set(__tzone9=00:00-23:59)
;
exten => s,n,set(_dest1=01234123456) ;dest1 emea_pager
exten => s,n,set(_dest2=001765412345) ;dest2 na_pager
exten => s,n,set(_dest3=006165453457) ;dest3 au_pager
exten => s,n,set(_dest4=08441231234) ;dest4 uk_no
exten => s,n,set(_dest5=001744519651) ;dest5 na_no
exten => s,n,set(_dest6=006118954654) ;dest6 au_no
;
exten => s,n,set(dialpre=9) ;dialing prefix
;
exten => s,n,set(dialcon=international) ;dialing context
;
exten => s,n,Goto(ftstimeing,s,1)
;
[ftstimeing]
;
;This sections runs though the days of the week and checks the time
;against DOW and time
;
exten => s,1,GotoIfTime(${tzone1}|mon|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|mon|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|mon|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|mon|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|tue|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|tue|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|tue|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|tue|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|wed|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|wed|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|wed|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|wed|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|thu|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|thu|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|thu|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|thu|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|fri|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|fri|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|fri|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|fri|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone5}|sat|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone6}|sun|*|*?dest1,1)
;
;Fall through point
exten => s,n,Goto(dest1,1)
contextGotoIfTime() application, using;
;Dialed using the Local channel so call handling is observered
;
exten => dest1,1,Noop(Calling ${dest1})
exten => dest1,n,Dial(Local/${dialpre}${dest1}@${dialcon})
exten => dest1,n,Hangup()
exten => dest2,1,Noop(Calling ${dest2})
exten => dest2,n,Dial(Local/${dialpre}${dest2}@${dialcon})
exten => dest2,n,Hangup()
exten => dest3,1,Noop(Calling ${dest3})
exten => dest3,n,Dial(Local/${dialpre}${dest3}@${dialcon})
exten => dest3,n,Hangup()
exten => dest4,1,Noop(Calling ${dest4})
exten => dest4,n,Dial(Local/${dialpre}${dest4}@${dialcon})
exten => dest4,n,Hangup()
exten => dest5,1,Noop(Calling ${dest5})
exten => dest5,n,Dial(Local/${dialpre}${dest5}@${dialcon})
exten => dest5,n,Hangup()
exten => dest6,1,Noop(Calling ${dest6})
exten => dest6,n,Dial(Local/${dialpre}${dest6}@${dialcon})
exten => dest6,n,Hangup()
exten => i,1,Hangup()
exten => t,1,Hangup()
exten => h,1,Hangup()

This can be expanded to include public holidays, if required. It can be possible to handle many years' public holidays in one line. For example, between the years 2009 and 2016, the UK's summer public holiday falls on the dates between the 25th and 31st of August and is always a Monday. Therefore, we have something like this:

GotoIfTime(*,Mon,25-31,Aug?dest1,1)

This will catch all UK summer public holidays, and as there are no other Mondays in August clashing with these dates, it's a set-and-forget for many years (just don't forget to change it after 2016!). The same goes for the majority of other public holidays except for Easter.

For these variable dates, we can resort back to the internal database to store the details and then use the GotoIf() application to check if the date is a holiday.

 

Variables


Variables are key to making the dialplan and system work in a manner that a user expects. The user would expect the system to know everything they have set on their extension, and not have to enter codes or dial special access numbers.

There are a number of places in which variables can be stored including the dialplan, sip.conf, iax.conf, chan_DAHDI.conf (in version1.6), and the Asterisk database (AstDB). For example, if we have a number of static dial strings we wish to store for each type of call and carrier we use, and then use them in a number of sections, the [globals] section of the extensions.conf file is the obvious place to declare them. If we wish to set a variable when a call is initiated from a SIP device, external caller ID or account codes are a good example, the setvar command in the sip.conf file is ideal for that purpose. Just remember that it won't work for calls sent to that device just when the calls are made. Finally, the AstDB is great for variables that are more transient in nature, such as call counts.

Inheritance of channel variables through the dialplan

On occasion, when using complicated dialplans you may wish for a variable's value to be kept as the call progresses. This is achieved by adding a _ [underscore] or a __ [double underscore] before the variable name.

A single _ will cause that variable to be inherited into the channel that started from the original channel, for example:

Set(_name1=value1)

If you want the variable to be inherited to all child channels indefinitely, then add __ before the variable name. For example:

Set(__name2=value2)

This should not be confused with setting the variable with the g option, as this sets it as a global variable. Doing so makes the variable available to all channels globally.

So, you may ask "why might we store dial strings as a variable?" The simple reason is that it allows a minimal amount of code for dialing all numbers, but still allows for different classes of restriction, by which we mean allowing different users to have different restrictions in what they can and cannot dial.

To pass these variables we will use a macro. Macros are like a template that we can use for repeated tasks, and they allow the passing of variables in an ordered fashion to the macro context. The call will jump to the s extension. The calling extension, context, and priority are stored in ${MACRO_EXTEN}, ${MACRO_CONTEXT}, and ${MACRO_PRIORITY} respectively. Arguments passed are accessed as ${ARG1}, ${ARG2}, and so on within the Macro. While a Macro is being executed, it becomes the context, so you must be able to handle the h, i, and t extensions if required within that context.

Let's build our small macro dialplan. We have a variable defined in the globals section of the extensions.conf file as follows:

[globals]
INT_CALL=IAX2/[email protected]_out/
INT_CALL_ID=01234123456 ; default international callerID
INT_CALL_LIMIT=5 ; Limit on the number of calls

In the context that we use for dialing, we have:

; International long distance through trunk
exten => _90.,1,Macro(outdial,${INT_CALL})

Here, we have defined the macro we are going to pass the call to, along with a single variable we defined in the globals section (the value of the calling extension can be retrieved within the macro by using ${MACRO_EXTEN}).

The macro context looks like this:

[macro-outdial]
exten => s,4,Dial(${ARG1}${MACRO_EXTEN:1},180)

This is the same as the dial string:

exten => s,4,Dial(IAX2/[email protected]_out/01234123456,180)

We have seen that we can pass one dial string, but let's now pass other variables to the Dial() application, such as a backup route for outgoing calls, and the caller ID we want to use for the call.

exten => _90.,1,Macro(outdial,${INTCALL},${INT_CALL_ID},${INT_CALL_LIMIT})
[macro-outdial]
exten => s,1,Set(GROUP()=OUTBOUND_GROUP) ;Set Group
exten => s,2,GotoIf($[${GROUP_COUNT(OUTBOUND_GROUP)} > ${ARG3}]?103) ;Exceeded?
exten => s,3,Set(CALLERID(num)=${ARG2})
exten => s,4,Dial(${ARG1}${MACRO_EXTEN:1},180)

Now it's time to bring some .conf file variables into the mix. Using the setvar facility in the sip.conf, iax.conf and chan_dahdi.conf files, we can set variables specific for every user such as unique caller ID, call limits, whether we want to record the call, account codes. Basically, anything that will help you handle calls more efficiently.

setvar=account_code=2206
setvar=callidnum=01234123456
setvar=tenantID=2

Note

One problem using .conf files is that the relevant channel module needs to be reloaded after a change, and in the case of DAHDI, Asterisk would need to be restarted. This isn't too much of an issue but the need can be removed by using the AstDB for storing commonly changed settings, such as caller ID and recordings.

You may think that all this variable use is over-complicated, but consider a system that supports multiple tenants. Using these techniques, you will only need one dialplan for multiple tenants instead of one per tenant. Simply set the tenantID in the relevant .conf file and then store the tenants' features in the globals section of the dialplan and in the AstDB, and all calls will go out as that tenant group. The concept is the same for other scenarios, such as departments that require cross charging of telephone costs.

Using the AstDB

Setting and retrieving variables in the AstDB is very simple and achieved through the use of the Set() application. Variables can exist in splendid isolation or be grouped into families. The syntax for setting a variable is:

Set(DB(family/variable)=value)

Retrieving the variable's value is equally as simple:

Set(result=${DB(family/variable)})

So, let's have a look at how we can implement a simple multi-tenant dialplan using multiple variable stores:

INT_CALL1=IAX2/[email protected]_out_1/
INT_CALL2=IAX2/[email protected]_out_1/
INT_CALL_LIMIT1=5 ; Limit on the number of calls
INT_CALL_LIMIT2=5 ; Limit on the number of calls
exten => _90[1-2]XXXXXXXXX,1,Set(INTCALL=INTCALL${tenantID})
exten => _90[1-2]XXXXXXXXX,n,Set(INT_CALL_LIMIT=INT_CALL_LIMIT${tenantID})
exten => _90[1-2]XXXXXXXXX,n,Macro(outdial,${INTCALL}, ${callidnum},${INT_CALL_LIMIT})

As we can see, we have been able to cut down the amount of code and make it universal for different types of users and systems. Using a macro lets us pass an ordered list of arguments. It is easiest to think of macro arguments as a list of variables since they are handled the same way.

Note

Due to the way macro is implemented, it executes the priorities contained within it via a sub-engine, and a fixed per-thread memory stack allowance. Macros are limited to seven levels of nesting. It can be possible that stack-intensive applications in deeply-nested macros could cause Asterisk to crash. Take this into account and be very careful when nesting macros.

 

Dialplan features and additions


In this section, we are going to look at the DEVSTATE() function and the System() application. We will see how we can check and change the "status" of devices with the DEVSTATE() function and use the system application to cause scripts on the server to be run.

func_devstate

The func_devstate application allows the status of a peer to be known before you dial it. This is very useful in many applications. We will cover a few of them here but you will be able to find many more.

The func_devstate application is part of Asterisk 1.6, but Russell Bryant (of Digium) has a back-ported version for Asterisk 1.4. This can now be found at:

http://svn.digium.com/community/russell/asterisk-1.4/func_devstate-1.4/func_devstate.c

For most Linux distributions, installing the function is pretty simple:

cd /usr/src/asterisk/funcs
wget http://svn.digium.com/community/russell/asterisk-1.4/func_devstate-1.4/func_devstate.c
cd ..
make clean
./configure
make menuselect
Choose option -> 6. Dialplan Functions
Then make sure that you have an entry like 8.func_devstate
make
make install

Note

If under Dialplan Functions, the DEVSTATE() function does not show up, you will need to edit the menuselect-tree to add it.

<member name="func_devstate" displayname="Gets or sets a device state in the dialplan" remove_on_change="funcs/func_devstate.o funcs/func_devstate.so">

Then compile Asterisk as shown previously.

What can we use the DEVSTATE() function for?

The DEVSTATE() function is versatile, allowing us to check and/or set the status of a device, as its name suggests. One very common use is to activate phone lamps, showing users if they have set a feature such as DND or call forwarding. In the following examples, we will look at both setting and checking methods:

The function reports on, or can set, the following states:

NOT_INUSE
INUSE
BUSY
INVALID
UNAVAILABLE
RINGING
RINGINUSE
ONHOLD
Outgoing trunk selection

The application can be used here to check that an outgoing peer is "available" and not "down", before you send a call to it. This is useful if you have peers or remote systems that are on variable quality connections.

exten => _90.,1,Macro(outdial,${PRIDIAL},${INT_CALL_ID},${INT_CALL_LIMIT},${BAKDIAL},${PRIPEER})
[macro-outdial]
exten => s,1, Set(GROUP()=OUTBOUND_GROUP) ;Set Group
exten => s,2,GotoIf($["${DEVSTATE(${ARG5})}"="UNAVALIABLE"]?s,7)
exten => s,3, GotoIf($[${GROUP_COUNT(OUTBOUND_GROUP)} > ${ARG3}]?103)
exten => s,4, Set(CALLERID(num)=${ARG2})
exten => s,5, Dial(${ARG1}${MACRO_EXTEN:1},180)
exten => s,6, Hangup()
exten => s,7, Dial(${ARG4}${MACRO_EXTEN:1},180)
exten => s,8, Hangup()
exten => s,n, Noop(call Limit exceeded)

This example is an expansion of our previously used macro and has a couple of extra arguments passed to it. This makes it very flexible, as the backup peer can be different for each dialed number.

Calling extensions

The application lets you see if extensions are busy or "out of service" before calling them. This can be useful for handsets that support call waiting, but you don't want to fully disable it for all calls. Before calling the extension, you can check to see if the extension has call waiting enabled and then, depending on the result, check the device status as follows:

exten => 2XXX,1,Macro(dialext)
[Macro-dialext]
exten => s,1,NoOp(SIP/${MACRO_EXTEN} has state ${DEVSTATE(SIP/$ {MACRO_EXTEN})})
exten => s,n,Set(CW=${DB(CW/${MACRO_EXTEN})})
exten => s,n,GotoIf($["${CW}"="YES"]?dial)
exten => s,n,GotoIf($["${DEVSTATE(SIP/${MACRO_EXTEN})}"!="NOT_INUSE"]? s-BUSY,1)
exten => s,n(dial),Dial(SIP/${MACRO_EXTEN},35)
exten => s,n,Goto(s-BUSY,1)
exten => s-BUSY,1,Voicemail(${MACRO_EXTEN},b)
exten => s-BUSY,n,Hangup()

In the previous example, we have used the internal database to set the flag to say if call waiting is enabled or not. If call waiting is anything other than YES, the status of the extension will be checked, otherwise the DEVSTATE isn't checked and the extension is just called. As we will see next, we can expand this to light a BLF (Busy Lamp Field) key as well, to give a visual indication to users of the device status.

Setting lights

We can also use the DEVSTATE() function to set BLF lights on and off, a very simple but highly effective feature. This is particularly helpful if you are using the dialplan for setting call forwards or DND. It can also show if a call center agent is logged in or not, on their phone.

To illustrate this functionality, we have a very simple example showing how to turn the light on and off. It uses one number to toggle the light status and is not specific for the particular phone all phones dial the same number and it is the CHANNEL variable, which is used to set it for a specific phone. In this example, we have two hints 4078 and 4071, and these are linked to extensions 5078 and 5071.

Using this code and adding additional code to set the database key for call waiting (as we have already covered) would give the phone user a visual indication as to whether call waiting is set or not.

exten => 4071,hint,Custom:light5071
exten => 4078,hint,Custom:light5078
exten => 1236,1,Goto(1236-${DEVSTATE(Custom:light${CHANNEL:4:4})}|1)
exten => 1236-UNKNOWN,1,Goto(1236-NOT_INUSE|1)
exten => 1236-NOT_INUSE,1,Noop(Turn ${CHANNEL:4:4} light on)
exten => 1236-NOT_INUSE,n,Set(DEVSTATE(Custom:light${CHANNEL:4:4})= INUSE)
exten => 1236-INUSE,1,Noop(Turn ${CHANNEL:4:4} light off)
exten => 1236-INUSE,n,Set(DEVSTATE(Custom:light{CHANNEL:4:4})= NOT_INUSE)

By using userevent, you can also send out manager events to update the Flash Operator Panel. The following would set the CW flag for the Flash Operator Panel for our extension and change the icon to reflect the status.

exten => 1236-NOT_INUSE,2,UserEvent(ASTDB|Channel: ${CHANNEL}^Family: CW^Value: SET ^)

Note

There is also a version of DEVSTATE() called EXTSTATE(). It is a modified version of the DEVSTATE() function that returns the state of an extension, rather than the state of a device. This means you can write dialplan logic based on the state of an extension (in use, ringing, on hold, and so on). The extension just needs to have a hint so we can determine which devices to check.

Boosting outgoing call capacity

We're going to have a look at how DEVSTATE() has been used to address an unusual situation. A call-centre customer wished to temporarily increase their outgoing call capacity, in this case by 20 concurrent calls, to cater for a particular project. However, in their location, with their budget and given the temporary need for extra capacity, the only effective means of boosting bandwidth is to utilize multiple ADSL circuits. In other words, SDSL and leased line circuits were too costly for consideration. Therefore, there was a need to bond multiple ADSL circuits together within Asterisk, in order to provide a single high-bandwidth circuit for outbound calls.

It may seem obvious, but when calculating call capacity with ADSL circuits, the figure we're interested in is the lower of the upload/download speeds. It doesn't matter if you have a superfast 20 MB DSL circuit, chances are that you only have an uplink speed of 800 kbps or less. It must also be remembered that once traffic exceeds 50% of the link speed, collisions and latency are likely to become an issue and must be addressed. This gives you a theoretical limit of up to 10 uncompressed calls per circuit if you're really lucky. Of course, with the GSM codec you can get a lot more, but at the cost of audio quality, which your customer is unlikely to accept. They expect PSTN quality and nothing less. If bandwidth utilization is on the borderline with an uncompressed codec, it can be advantageous to use a commercial (non-free) codec such as G729, which is obtainable from Digium.

Using multiple broadband lines

Using the criteria already discussed and assuming a 500 kbps uplink speed, it was determined that four broadband circuits were needed. This may sound expensive, but in reality it's not, when you consider that the alternative was 20 channels of a PRI (ISDN 30), which worked out at twice the cost of four PSTN lines with broadband. As we'll see later in the sales appendix, a major benefit of VoIP is that the customer is paying much less for line rental. This solution only reinforces that benefit.

We are going to describe a solution that used four broadband circuits, but another advantage of this approach is that it is very scalable. To illustrate, a system has been set up for a charity in the UK that had 75 agents placing thousands of calls a day on just eight broadband circuits.

Note

This example was tested and proven using Asterisk 1.4.19.2, and should work with releases up to 1.4.19.2 - it’s operation cannot be guaranteed in other versions.

Configuration overview

Once the circuits are delivered, you will end up with four routers connected to the broadband service of your choice. Each router will have a unique IP address. In our case, we shall assume they are as follows:

192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4

We will also assume that the VoIP ITSP has multiple IP addresses that you can connect to, though if not, you can probably do some clever address translation in the routers.

Setting up the routing in Linux

Let's assume our VoIP ISP has provided us with the following external IP addresses:

88.88.88.81
88.88.88.82
88.88.88.83
88.88.88.84

Within Linux, you can easily set up different gateway addresses for a given destination. The file that manages the gateways is normally called ifup-routes in the /etc/sysconfig/network-scripts directory.

To configure the gateways, we append the following to the ifip-routes file:

/sbin/route add 88.88.88.81 gw 192.168.1.1

/sbin/route add 88.88.88.82 gw 192.168.1.2

/sbin/route add 88.88.88.83 gw 192.168.1.3

/sbin/route add 88.88.88.84 gw 192.168.1.4

Taking the last entry, what we're saying is that for all traffic to 88.88.88.84, route it via the router at 192.168.1.4.

If you reboot and run the command route n in a terminal session, you'll see these routes in place.

Configuring Asterisk

We now turn our attention to the Asterisk configuration. When we make a call, we're going to keep count of how many calls we have on a broadband line, so that when the circuit is "full", we can move on to the next available one.

Firstly, in the extensions.conf file, we need to declare a variable that sets the maximum concurrent calls we will allow through any one router.

MAXVOIPCALLS=5 ; Maximum Calls we allow over IP (outbound)

We've previously set up four entries in the iax.conf file called iaxline1 through to iaxline4. They have identical entries, with the exception on the host= line. Here we assign the appropriate external IP address, that is, 88.88.88.81 for iaxline1 and so on.

Now, we need to declare the IAX channels as follows:

AIXVOIPOUT = IAX2/iaxline1
AIXVOIPOUT1 = IAX2/iaxline2

As expected, we use a macro to manage the call routing. The example below only shows two lines for the sake of brevity.

[macro-voiptrunk]
exten => s,1,Noop(Number of Broadband calls)
;Use devstate to test the availability of the trunks. You could put some code in, to use an alternative if they are off line
exten => s,2,Noop(Trunk 1 ${DEVSTATE(${AIXVOIPOUT})} -> ${GROUP_COUNT([email protected])})
exten => s,3,Noop(Trunk 2 ${DEVSTATE(${AIXVOIPOUT1})} -> ${GROUP_COUNT([email protected])})
exten => s,4,GoToIf($[${GROUP_COUNT([email protected])} < ${MAXVOIPCALLS}]?10:20)
;Have we exceeded the max calls per trunk? If so, jump to Extension 20 and use second trunk
exten => s,10,gotoif($[${DEVSTATE(${AIXVOIPOUT})} = UNAVAILABLE]?20:11)
;Here we test to see if the trunk is available, if it's gone off-line, we use the second trunk
exten => s,11,Set(GROUP(list1)=VOIPTRUNKS) ;increment the usage count.
exten => s,12,noop(${DEVSTATE(${AIXVOIPOUT})})
exten => s,13,Noop(Trunk 1-> ${GROUP_COUNT([email protected])})
exten => s,14,Dial(${AIXVOIPOUT}/${MACRO_EXTEN})
exten => s,15,Goto(s-${DIALSTATUS},1)
exten => s,20,GoToIf($[${GROUP_COUNT([email protected])} < ${MAXVOIPCALLS}]?21:40)
exten => s,21,Set(GROUP(list2)=VOIPTRUNKS)
exten => s,22,Noop(Number of Broadband calls)
exten => s,23,Noop(Trunk 2-> ${GROUP_COUNT([email protected])})
exten => s,24,Dial(${AIXVOIPOUT1}/${MACRO_EXTEN})
exten => s,25,Goto(s-${DIALSTATUS},1)
exten => s,40,Congestion(15) ; No more lines left
Explanation of the macro

Priorities "2" and "3" use the DEVSTATE() function to test the availability of the broadband lines. If a line is down, UNAVAILABLE will be returned. At "4", we look to see if we've exceeded max calls on this line. If we haven't, we'll place a call on the first router, otherwise go to the next. "11" records the in-use count and increments it for the given router (list1).

What happens, overall, is that the first 5 calls (set by the global MAXVOIPCALLS) will go via router 1, the sixth will go via router 2. If in the meantime a call is dropped from router 1, the next call placed will go back to router 1, even if other calls are ongoing on router 2.

Finally, we need to add a call to the macro in our dialplan:

[outbound-national]
exten => _0Z.,1,NoOp(national call)
exten => _0Z.,2,Macro(voiptrunk)

The above technique is scalable. You can add as many broadband lines as you need. The end result is that you can say to your customer, "want more outgoing capacity? Just add another DSL line". However, it must not be forgotten that there may be more stable solutions such as SDSL and leased lines, depending on location.

Downsides

The above example works really well for outbound calling, but not so well for inbound. If you own the server the customer is connecting to, then you can reverse the logic at your end. If you don't, then all you can do is allocate one router for inbound (register via an inbound router) and the rest for outbound.

System() application

The System() application allows Asterisk to run Linux commands and shell scripts. What we will look at here is a simple hotdesking deployment script for asterisk. This type of deployment method is used by all commercial PBXs and is needed for any enterprise deployment of Asterisk. Hand editing filenames or even configuring phones via their web GUI will not be accepted by a customer or end user.

The dialplan is very simple. The user dials a code from his/her handset and is asked to enter a four-digit number (their hotdesk ID). The dialplan then stores this as a variable. It also sets the caller ID and the IP address for the set and passes these to the script.

[hotdesk_in]
exten => s,1,Answer
exten => s,n,Playback(privacy-thankyou)
exten => s,n,Read(MY_EXTEN,access-code,4)
exten => h,1,Set(MY_IP=${SIPPEER(${CALLERID(num)}:ip)})
exten => h,2,system(/usr/local/sbin/exten_in ${MY_EXTEN} ${CALLERID(num)} ${MY_IP})

With these three variables, the script then knows the handset's existing number, IP address, and the number it wants to be.

In addition, the script then performs an Address Resolution Protocol (ARP) lookup on the IP address to find the phone's MAC address. It needs this because, as in the example, we are using the phones config file in the format of<MAC-ADDRESS>.cfg, and we configure the sets via TFTP (Trivial File Transfer Protocol). Hence, as we know the MAC address we can copy the config files to the correct name.

Firstly, we will copy the old<MAC-ADDRESS>.cfg to a different file name. Then we copy the config file for the extension number we wish the phone to be using the MY_EXTEN variable we have passed to the script to define it to our new<MAC-ADDRESS>.cfg. Now when the set reboots, it will pick up the new file. However, we want this to be automatic and with as many handsets as it can have. The sip notify command does so when configured in the sip_notify.conf file, in the case of Aastra handsets, as follows:

[aastra-check-cfg]
Event=>check-sync
Content-Length=>0

The following command will cause the phone to check the config file for changes and reboot if any change is found:

/usr/sbin/asterisk -rx "sip notify aastra-check-cfg ${CALLERID}"

When it reboots, it will pick up its new configuration. By using scripts such as the previous one, you can speed up "moves, adds, and changes" and cut out the need for engineers to put out or replace handsets. It can also be used to provide a form of hotdesking with the user dialing a code to set the handset as theirs, and then log out when they leave (copying back the previous config ), thus returning the phone to its previous state.

 

Summary


In summary, we have looked at how to break down your dialplan into small, manageable contexts or objects. These can then be included into the dialplan to create a system with the flexibility to match any commercial PBX. We also looked at improving the security of the dialplan such that it is easy to manage who can dial where in an understandable way.

We looked at the many different ways that variables can be stored in the system and called upon when required, as well as seeing how they can interact with macros to make the dialplan more streamlined. Here we used one macro for many extensions.

We looked at the DEVSTATE() function and the uses that it can be put to. These are not just (as it initially seems) for checking the status, but also a way to set the status and light a BLF key to show a feature is set.

We looked at time and day call routing, and how it can be used to route calls based on time and day. We also looked at the clever use of date ranges, so that we can future-proof our dialplans for holidays for many years to come.

And finally we looked at the System() application and how this can be used for easing the deployment of handsets in an enterprise solution. In the next chapter, we shall focus on exploring network considerations.

About the Authors

  • Colman Carpenter

    Colman Carpenter is the MD of Voicespan, a Kent-based company that offers Asterisk-based systems to the SME market across the UK. He is an IT professional of over 20 years standing, with experience in diverse areas such as IBM mid-range software development, Lotus Notes and Domino consultancy, Data Management, E-marketing consultancy, IT Management, Project Management, Wordpress Website Design, and lately, Asterisk consultancy. He is a qualified PRINCE2 practitioner.

    Voicespan (http://www.voicespan.co.uk) offers Asterisk-based systems as the cornerstone of a holistic VoIP-telephony service for SMEs. They offer companies a one-stop shop for implementing a VoIP-capable system, encompassing Asterisk-based systems, endpoints, trunks, telephony interfaces and network equipment, and the consultancy necessary to bring it all together into a coherent whole. This is his first book.

    Browse publications by this author
  • David Duffett

    David Duffett delivers Asterisk training and consultancy around the world through his own company (TeleSpeak Limited, www.telespeak.co.uk), in addition to designing and delivering training for a number of companies, including Digium, Inc.

    A keen Asterisk enthusiast, David also enjoys podcasting, radio presenting, and teaching public-speaking skills. He is a Chartered Engineer with experience in fields including Air Traffic Control communications, Wireless Local Loop, Mobile Networks, VoIP, and Asterisk. David has been in the telecoms sector for nearly 20 years and has had a number of computer telephony, VoIP, and Asterisk articles published through various industry publications and web sites.

    Browse publications by this author
  • Ian Plain

    Ian Plain has worked in the telecoms industry since 1981 and has designed some of the largest PBX networks in the UK. Since the late 1990s, he has been involved with VoIP initially for links between systems, and with IP PBX systems since 1999. Since 2003, he has been running a telecoms consultancy based near Bath in the UK, working primarily on high-availability Asterisk-based solutions for corporate customers.

    Browse publications by this author
  • Nik Middleton

    Nik Middleton has been in wide-area communications since the mid-eighties. He spent most of the nineties working in the US, where he developed a shareware Microsoft mail to SMTP/POP3 connector that sold some 287,000 copies. He spent six years working for DuPont in VA, developing remote monitoring systems for their global Lycra business. In late 2000, he returned to the UK where he held various senior positions in British Telecom, LogicaCMG, and Computer Science Corp.

    In 2005, tired of working in London, he set up his own company (Noble Solutions) providing VoIP solutions in rural Devon, where he now lives with his wife Georgina and three children, Mathew, Vicky, and Isabel. A keen amateur pilot, his favorite place when not in the office is flying over the beautiful Devon countryside.

    Browse publications by this author
Book Title
Access this book, plus 7,500 other titles for FREE
Access now