Macros[84] are a very useful construct designed to avoid repetition in the dialplan. They also help in making changes to the dialplan. To illustrate this point, let’s look at our sample dialplan again. If you remember the changes we made for voicemail, we ended up with the following for John’s extension:
exten => 101,1,Dial(${JOHN},10) exten => 101,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy:unavail) exten => 101,n(unavail),Voicemail(101@default,u) exten => 101,n,Hangup() exten => 101,n(busy),VoiceMail(101@default,b) exten => 101,n,Hangup()
Now imagine you have a hundred users on your Asterisk system—setting up the extensions would involve a lot of copying and pasting. Then imagine that you need to make a change to the way your extensions work. That would involve a lot of editing, and you’d be almost certain to have errors.
Instead, you can define a macro that contains a list of steps to take, and then have all of the phone extensions refer to that macro. All you need to change is the macro, and everything in the dialplan that references that macro will change as well.
Tip
If you’re familiar with computer programming, you’ll recognize that macros are similar to subroutines in many modern programming languages. If you’re not familiar with computer programming, don’t worry—we’ll walk you through creating a macro.
The best way to appreciate macros is to see one in action, so let’s move right along.
Let’s take the dialplan logic we used above to set up voicemail for John and turn it into a macro. Then we’ll use the macro to give John and Jane (and the rest of their coworkers) the same functionality.
Macro definitions look a lot like contexts. (In fact, you could
argue that they really are small, limited contexts.) You define a
macro by placing macro-
and the
name of your macro in square brackets, like this:
[macro-voicemail]
Macro names must start with macro-
. This distinguishes them from regular
contexts. The commands within the macro are built almost identically
to anything else in the dialplan; the only limiting factor is that
macros use only the s
extension.
Let’s add our voicemail logic to the macro, changing the extension to
s
as we go:
[macro-voicemail] exten => s,1,Dial(${JOHN},10) exten => s,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy:unavail) exten => s,n(unavail),Voicemail(101@default,u) exten => s,n,Hangup() exten => s,n(busy),VoiceMail(101@default,b) exten => s,n,Hangup()
That’s a start, but it’s not perfect, as it’s still specific to John and his mailbox number. To make the macro generic so that it will work not only for John but also for all of his coworkers, we’ll take advantage of another property of macros: arguments. But first, let’s see how we call macros in our dialplan.
To use a macro in our dialplan, we use the Macro()
application. This application calls
the specified macro and passes it any arguments. For example, to call
our voicemail macro from our dialplan, we can do the following:
exten => 101,1,Macro(voicemail)
The Macro()
application also defines several special variables for our use.
They include:
As we explained earlier, the way we initially defined our macro
was hardcoded for John, instead of being generic. Let’s change our
macro to use ${MACRO_EXTEN}
instead
of 101
for the mailbox number. That
way, if we call the macro from extension 101 the voicemail messages
will go to mailbox 101, and if we call the macro from extension 102
messages will go to mailbox 102, and so on:
[macro-voicemail] exten => s,1,Dial(${JOHN},10) exten => s,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy:unavail) exten => s,n(unavail),Voicemail(${MACRO_EXTEN}@default,u) exten => s,n,Hangup() exten => s,n(busy),VoiceMail(${MACRO_EXTEN}@default,b) exten => s,n,Hangup()
Okay, now we’re getting closer to having the macro the way we want it,
but we still have one thing left to change; we need to pass in the
channel to dial, as it’s currently still hardcoded for ${JOHN}
(remember that we defined the
variable JOHN
as the channel to
call when we want to reach John). Let’s pass in the channel as an
argument, and then our first macro will be complete:
[macro-voicemail] exten => s,1,Dial(${ARG1},10) exten => s,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy:unavail) exten => s,n(unavail),Voicemail(${MACRO_EXTEN}@default,u) exten => s,n,Hangup() exten => s,n(busy),VoiceMail(${MACRO_EXTEN}@default,b) exten => s,n,Hangup()
Now that our macro is done, we can use it in our dialplan. Here’s how we can call our macro to provide voicemail to John, Jane, and Jack:
exten => 101,1,Macro(voicemail,${JOHN}) exten => 102,1,Macro(voicemail,${JANE}) exten => 103,1,Macro(voicemail,${JACK})
With 50 or more users, this dialplan will still look neat and
organized; we’ll simply have one line per user, referencing a macro
that can be as complicated as required. We could even have a few
different macros for various user types, such as executives
, courtesy_phones
, call_center_agents
, analog_sets
, sales_department
, and so on.
A more advanced version of the macro might look something like this:
[macro-voicemail] exten => s,1,Dial(${ARG1},20) exten => s,n,Goto(s-${DIALSTATUS},1) exten => s-NOANSWER,1,Voicemail(${MACRO_EXTEN},u) exten => s-NOANSWER,n,Goto(incoming,s,1) exten => s-BUSY,1,Voicemail(${MACRO_EXTEN},b) exten => s-BUSY,n,Goto(incoming,s,1) exten => _s-.,1,Goto(s-NOANSWER,1)
This macro depends on a nice side effect of the Dial()
application: when you use the
Dial()
application, it sets the
DIALSTATUS
variable to indicate
whether the call was successful or not. In this case, we’re handling
the NOANSWER
and BUSY
cases, and treating all other result
codes as a NOANSWER
.
[84] Although Macro
seems like a
general-purpose dialplan subroutine, it has a stack overflow problem
that means you should not try to nest Macro
calls more than five levels deep. As
of this writing, we do not know whether the Macro
application will be patched for 1.4,
or if it will be rewritten for future versions. If you plan to do a
lot of macros within macros (and call complex functions within
them), you may run into stability problems. You will know you have a
problem with just one test call, so if your dialplan tests out,
you’re good to go. We also recommend that you take a look at the
Gosub
and Return
applications, as a lot of macro
functionality can be implemented without actually using Macro()
. Also, please note that we are not
suggesting that you don’t use Macro()
. It is fantastic and works very
well; it just doesn’t nest efficiently.
Get Asterisk: The Future of Telephony, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.