Asterisk AEL

  • 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.


context default {
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.


context default {
includes {
local;
longdistance;
international;
};
};



Dialplan Switches

Switches are listed in their own block within a context.


context default {
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'.


context outgoing {
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.


globals {
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.


context foo {
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.


context blah {
s => {
CALLERID(name)=ChickenMan;
NoOp(My name is ${CALLERID(name)} !);
};
};



Loops

AEL has implementations of 'for' and 'while' loops.


context 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.


context conditional {
_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.


context example {
_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/guest@misery.digium.com);
Playback(demo-nogo);
goto s|instructions;
};
600 => {
Playback(demo-echotest);
Echo();
Playback(demo-echodone);
goto s|instructions;
};
  1. => {
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: 237887 ------------------------ Page Created: Wed 07 of Sep, 2005 (18:56 UTC)
Created by: X1Z, Last modification: Tue 11 of Sep, 2012 (00:41 UTC) by admin


Please update this page with new information, just login and click on the "Edit" or "Discussion" tab. Get a free login here: Register Thanks! - Find us on Google+

Page Changes | Comments

 

Featured -

Search: