login | register
Sat 19 of Jul, 2008 [23:27 UTC]

voip-info.org

Discuss [15] History

Asterisk AEL

Created by: X1Z,Last modification on Wed 05 of Dec, 2007 [02:33 UTC] by GeoMinimoto9851
  • 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



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 reffered 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;
    };
    # => {
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: 98564 ------------------------ Page Created: Wed 07 of Sep, 2005 [18:56 UTC]

Comments

Comments Filter
222

333Using Dial command option M in AEL

by Mundo1979, Tuesday 20 of May, 2008 [06:58:38 UTC]
Does anybody know how to use the Dial command option M from within extensions.ael? I can't get the following to work:

Dial(${sz_Extensions},${int_Voicemail_Timeout},gM(&call_screen(${SCREEN_FILE})));

macro call_screen(sz_Caller_Name_Audio_File)
{
Wait(0.2);
Playback(screen-from);
.
.
.
}

It seems to be trying to call an extensions.conf macro.

Any help would be greatly appreciated.

Mundo1979
222

333Re: How to 'include' realtime extensions?

by KLBlack, Friday 16 of May, 2008 [14:04:04 UTC]
In response to Jan Van Nieuwenhove, you add realtime extensions into an ael file by using the syntax

switches {
               Realtime/context@family/options 
       }
222

333Re: include other files?

by vshasm, Wednesday 03 of October, 2007 [13:23:25 UTC]
Please read this article:
http://www.voip-info.org/wiki/view/Asterisk+AEL2#Includes

Maybe in the ael first version this impossible
222

333include other files?

by j_vb, Monday 16 of July, 2007 [07:15:46 UTC]
How can I include other files?
The "#include" directive seems not to work like in other config files (pbx_ael.c:1120 handle_root_token: Unknown root token '#include').

I am using Asterisk version 1.2.9.1.


EDIT:
2007/07/16:
Ok, I figured out, it works with Asterisk 1.4.5.


222

333AEL Syntax Highlighting for VIM

by cmdrwalrus, Thursday 17 of May, 2007 [17:14:21 UTC]
I've created a syntax file for VIM to highlight AEL. It can be found here:

http://www.vim.org/scripts/script.php?script_id=1900

Any comments or contributions are more than welcome!
222

333goto

by pip, Sunday 22 of October, 2006 [01:03:38 UTC]
goto not only takes labels as it's last argument, but also priorities, like: 'goto othercontext|${EXTEN}|1;', of course, in AEL, using priority numbers other than 1 is potentially dangerous.
222

333Aw: Dialplan Errors

by cibi, Monday 07 of August, 2006 [22:41:04 UTC]
>Does anyone know how to deal with "errors" (ie n+101 priorities) in AEL?
Just look at the above samples. You use the DIALSTATUS variable for that.
You can even use it in the extension.conf (Asterisk 1.2.x) - priority jumping can be disabled in 1.2.x for the extensions.conf

I recommend everyone to take a look at AEL2.

222

333Dialplan Errors

by drmouse, Tuesday 06 of June, 2006 [13:06:05 UTC]
Does anyone know how to deal with "errors" (ie n+101 priorities) in AEL?

For example, choosing whether to play the busy or unavailable voicemail message?
222

333Switch requires default+break?

by cibi, Monday 02 of January, 2006 [14:06:12 UTC]
I'm using Asterisk 1.2.1-BRIstuffed-0.3.0-PRE-1d.
I use a code similar to this (Edit: the code is used in a macro)

switch(${ton}) {
       case 1:
           clipno=1;
           break;
       case 2:
           clipno=2;
           break;
   };
   NoOp(After the switch statement);

With ton=1 or ton=2, the NoOP (or any other code after that) is executed, but if ton is 0, it isn't. If add a default case with 1 NoOp, same result.
But if i add a default case (include some code, or just NoOP) + a closing break it works.
222

333Comments in AEL

by middletn, Saturday 10 of December, 2005 [23:35:45 UTC]
I like to coment my dial plans, it would appear that the generic // seems to work