- Please note that AEL is still considered EXPERIMENTAL at this time.
The Asterisk Extension Language
Over time, people have been pushing to add features to extensions.conf to make it more like a programming language. AEL is intended to provide an actual programming language that can be used to write an Asterisk dialplan.
Getting Started
The AEL parser (pbx_ael.so) is completely separate from the module that parses extensions.conf (pbx_config.so). To use AEL, the only thing that has to be done is the module pbx_ael.so must be loaded by Asterisk. This will be done automatically if using ‘autoload=yes’ in /etc/asterisk/modules.conf. When the module is loaded, it will look for ‘extensions.ael’ in /etc/asterisk/. Both extensions.conf and extensions.ael can be used in conjunction with each other if that is what is desired. Some users may want to keep extensions.conf for the features that are configured in the ‘general’ section of extensions.conf.
Reloading extensions.ael
To reload extensions.ael, the following command can be issued at the CLI.
- CLI> ael reload
Debugging
Enable AEL contexts debug
- CLI> ael debug contexts
Enable AEL macros debug
- CLI> ael debug macros
Enable AEL read debug
- CLI> ael debug read
Enable AEL tokens debug
- CLI> ael debug tokens
Disable AEL debug messages
- CLI> ael no debug
Comments
Lines starting with double slash (//) are comments
// This is a comment
Context
Contexts in AEL represent a set of extensions in the same way that they do in extensions.conf.
context default {
};
NOTE: The opening curly-brace must appear as above. Moving it to the following line may have disastrous consequences!
Extensions
To specify an extension in a context, the following syntax is used. If more than one application is be called in an extension, they can be listed in order inside of a block.
1234 => Playback(tt-monkeys);
8000 => {
NoOp(one);
NoOp(two);
NoOp(three);
};
_5XXX => NoOp(it’s a pattern!);
};
Includes
Contexts can be included in other contexts. All included contexts are listed within a single block.
includes {
local;
longdistance;
international;
};
};
Dialplan Switches
Switches are listed in their own block within a context.
switches {
DUNDi/e164;
IAX2/box5;
};
eswitches {
IAX2/context@${CURSERVER};
};
};
Ignorepat
ignorepat can be used to instruct channel drivers to not cancel dialtone upon receipt of a particular pattern. The most commonly used example is ‘9’.
ignorepat => 9;
};
NOTE: The opening curly-brace must appear as above. Moving it to the following line may have disastrous consequences!
Variables
Variables in Asterisk do not have a type, so to define a variable, it just has to be specified with a value.
Global variables are set in their own block.
CONSOLE=Console/dsp;
TRUNK=Zap/g2;
};
NOTE: The opening curly-brace must appear as above. Moving it to the following line may have disastrous consequences!
Variables can be set within extensions as well.
555 => {
x=5;
y=blah;
divexample=10/2
NoOp(x is ${x} and y is ${y} !);
};
};
NOTE: Asterisk beta1 parses assignments using a $[] wrapper as opposed to the more logical way of doing it like Set and SetVar work. In this example, I had ${ARG1} set to “SIP/x7065558529” sans-quotes and it flunked out.
NOTE: Another opinion: The $[ ] allow expressions to be used, and add extra power to the language. Read the README.variables about the requirements of $[ ] expressions. In the following example, the SIP/x7065558529
should not be “sans quotes”. So, the statement might have been entered: requesting_channel=”${ARG1}”; ( where the “”‘s prevent the evaluation. )
NOTE: These things are wrapped up in a $[ ] expression: The while() test; the if() test; the middle expression in the for( x; y; z) statement (the y expression); Assignments — the right hand side, so a = b -> Set(a=$[b])
requesting_channel=${ARG1}
ERROR: Oct 10 12:48:59 WARNING[19726]: ast_expr2.y:811 op_div: non-numeric argument
-- Executing Set("SIP/x7065558529-2afd", "requesting_channel=0") in new stack
FROM show dialplan:
's' => 1. Set(requesting_channel=$[ ${ARG1} ]) [pbx_ael]
But you can use Set and it works the old way.
Set(requesting_channel=${ARG1})
Writing to a dialplan function is treated the same as writing to a variable.
s => {
CALLERID(name)=ChickenMan;
NoOp(My name is ${CALLERID(name)} !);
};
};
Loops
AEL has implementations of ‘for’ and ‘while’ loops.
1 => {
for (x=0; ${x} < 3; x=${x} + 1) {
Verbose(x is ${x} !);
};
};
2 => {
y=10;
while (${y} >= 0) {
Verbose(y is ${y} !);
y=${y}-1;
};
};
};
NOTE: The conditional expression (the “${y} >= 0” above) is wrapped in $[ ] so it can be evaluated.
NOTE: The for loop test expression (the “${x} < 3” above) is wrapped in $[ ] so it can be evaluated.
Conditionals
AEL supports if and switch statements. Note that if you have an else clause, you MUST place braces around the non-else portion of the if statement.
_8XXX => {
Dial(SIP/${EXTEN});
if (“${DIALSTATUS}” = “BUSY”) {
Voicemail(${EXTEN}|b);
} else {
Voicemail(${EXTEN}|u);
}
};
_777X => {
switch (${EXTEN}) {
pattern N11:
NoOp(You called a N11 number– ${EXTEN});
break;
case 7771:
NoOp(You called 7771!);
break;
case 7772:
NoOp(You called 7772!);
break;
case 7773:
NoOp(You called 7773!);
// fall thru-
default:
NoOp(In the default clause!);
};
};
};
NOTE: The conditional expression in if() statements (the “${DIALSTATUS}” = “BUSY” above) is wrapped in $[ ] for evaluation.
NOTE: Neither the switch nor case values are wrapped in $[ ]; they can be constants, or ${var} type references only.
NOTE: Using ‘pattern’ instead of ‘case’ permits usage of Asterisk Dialplan Patterns.
NOTE: AEL generates each case as a separate extension. case clauses with no terminating ‘break’, or ‘goto’, have a goto inserted, to the next clause, which creates a ‘fall thru’ effect.
NOTE: Since switches are implemented as extensions, ${EXTEN} will not work as desired.
goto and labels
This is an example of how to do a goto in AEL.
context gotoexample {
s => {
begin:
NoOp(Infinite Loop! yay!);
Wait(1);
goto begin; // go to label in same extension
};
3 => {
goto s|begin; // go to label in different extension
};
4 => {
goto gotoexample|s|begin; // overkill go to label in same context
};
};
context gotoexample2 {
s => {
end:
goto gotoexample|s|begin; // go to label in different context
};
};
NOTE: goto labels follow the same requirements as the Goto() application, except the last value has to be a label. If the label does not exist, you will have run-time errors. If the label exists, but in a different extension, you have to specify both the extension name and label in the goto, as in: goto s|z; if the label is in a different context, you specify context|extension|label. There is a note about using goto’s in a switch statement below…
Jumps
To call another extension (or, in a switch statement, another case), use the ‘jump’ statement:
context incoming {
s => {
NoOp(Handle the call here);
};
18665551212 => jump s; // we accept POTS calls at this number
};
context home {
399 => jump s@incoming; // 399 is my ‘simulate incoming call’ speeddial 😉
};
Macros
A macro is defined in its own block like this. The arguments to the macro are specified with the name of the macro. They are then referred to by that same name. A catch block can be specified to catch special extensions.
macro std-exten( ext , dev ) {
Dial(${dev}/${ext},20);
switch(${DIALSTATUS) {
case BUSY:
Voicemail(b${ext});
break;
default:
Voicemail(u${ext});
};
catch a {
VoiceMailMain(${ext});
return;
};
};
A macro is then called by preceeding the macro name with an ampersand.
_5XXX => &std-exten(${EXTEN}, “IAX2”);
};
Examples
context demo {
s => {
Wait(1);
Answer();
TIMEOUT(digit)=5;
TIMEOUT(response)=10;
restart:
Background(demo-congrats);
instructions:
for (x=0; ${x} < 3; x=${x} + 1) {
Background(demo-instruct);
WaitExten();
};
};
2 => {
Background(demo-moreinfo);
goto s|instructions;
};
3 => {
LANGUAGE()=fr;
goto s|restart;
};
500 => {
Playback(demo-abouttotry);
Dial(IAX2/[email protected]);
Playback(demo-nogo);
goto s|instructions;
};
600 => {
Playback(demo-echotest);
Echo();
Playback(demo-echodone);
goto s|instructions;
};
- => {
hangup:
Playback(demo-thanks);
Hangup();
};
t => goto #|hangup;
i => Playback(invalid);
};
Hints and Bugs
Note: These hints/bugs are as of asterisk 1.2.1, it’s possible some of these limitations will be removed in the future
- Every closing bracket ‘}’, must be terminated with a semi-colon ‘;’, including if-statments, contexts, macros, switch statements, extensions, etc. If you do not, any statements between the closing bracket and the next found semi-colon will be ignored, and not generate a warning, can be hard to debug.
- You may _not_ have an empty case statement. Even if falling-thru to another, put a NoOp() in-between, or a very-hard-to-debug error will occur.
- Never use a colon ‘:’ unless constructing a label, parser does not check to see if colon is contained within quotes, or within a function call (even one such as NoOp, if you’re just trying to print debugging). To use a colon as required for MACRO-EXIT and dial options, you can set a global variable to a colon ‘:’ in extensions.conf, and call the variable as needed in your AEL scripts. This is a rough hack, but it does allow these features to function properly.
- When using labels, never put one at the very end of a macro or extension in a context, always make sure at least one command (such as a NoOp) still exists after a label, otherwise the label will not be added to the dialplan. (It is often convenient to have a label at the end in order to issue a goto command to handle error conditions).
- When in a switch statement, you cannot simply issue a goto for a label within the same context/extension/macro because the switch statment creates a new extension in the current context for each case statement, you must specify the full location of the label in the current context, like “goto s|mylabel” instead of “goto mylabel” , or avoid goto statements within switch statements.
- When calling a ‘FastAGI’ you must escape the backslashes, for example: AGI(agi:\/\/127.0.0.1\/…).
Examples
Here you can find some examples on how to use AEL to create powerful dialplans: AEL Example Snippets.
AEL2 | AEL Example Snippets | Configuration | The Dialplan – extensions.conf | Dialplan Commands
Page Hits: 9554 ———————— Page Created: Wed 07 of Sep, 2005 (18:56 UTC)