mirror of https://github.com/asterisk/asterisk
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
446 lines
19 KiB
446 lines
19 KiB
==================
|
|
| Local Channels |
|
|
==================
|
|
|
|
In Asterisk, Local channels are a method used to treat an extension in the
|
|
dialplan as if it were an external device. In essense, Asterisk will send the
|
|
call back into the dialplan as the destination of the call, versus sending the
|
|
call to a device.
|
|
|
|
Two of the most common areas where Local channels are used include members
|
|
configured for queues, and in use with callfiles. There are also other uses
|
|
where you want to ring two destinations, but with different information, such as
|
|
different callerID for each outgoing request.
|
|
|
|
|
|
Examples
|
|
========
|
|
|
|
Local channels are best demonstrated through the use of an example. Our first
|
|
example isn't terribly useful, but will demonstrate how Local channels can
|
|
execute dialplan logic by dialing from the Dial() application.
|
|
|
|
|
|
-----------------------------
|
|
Trivial Local channel example
|
|
-----------------------------
|
|
|
|
In our dialplan (extensions.conf), we can Dial() another part of the dialplan
|
|
through the use Local channels. To do this, we can use the following dialplan:
|
|
|
|
[devices]
|
|
exten => 201,1,Verbose(2,Dial another part of the dialplan via the Local chan)
|
|
exten => 201,n,Verbose(2,Outside channel: ${CHANNEL})
|
|
exten => 201,n,Dial(Local/201@extensions)
|
|
exten => 201,n,Hangup()
|
|
|
|
[extensions]
|
|
exten => 201,1,Verbose(2,Made it to the Local channel)
|
|
exten => 201,n,Verbose(2,Inside channel: ${CHANNEL})
|
|
exten => 201,n,Dial(SIP/some-named-extension,30)
|
|
exten => 201,n,Hangup()
|
|
|
|
The output of the dialplan would look something like the following. The output
|
|
has been broken up with some commentary to explain what we're looking at.
|
|
|
|
-- Executing [201@devices:1] Verbose("SIP/my_desk_phone-00000014", "2,Dial another part of the dialplan via the Local chan") in new stack
|
|
== Dial another part of the dialplan via the Local chan
|
|
|
|
We dial extension 201 from SIP/my_desk_phone which has entered the [devices]
|
|
context. The first line simply outputs some information via the Verbose()
|
|
application.
|
|
|
|
|
|
-- Executing [201@devices:2] Verbose("SIP/my_desk_phone-00000014", "2,Outside channel: SIP/my_desk_phone-00000014") in new stack
|
|
== Outside channel: SIP/my_desk_phone-00000014
|
|
|
|
The next line is another Verbose() application statement that tells us our
|
|
current channel name. We can see that the channel executing the current dialplan
|
|
is a desk phone (aptly named 'my_desk_phone').
|
|
|
|
|
|
-- Executing [201@devices:3] Dial("SIP/my_desk_phone-00000014", "Local/201@extensions") in new stack
|
|
-- Called 201@extensions
|
|
|
|
Now the third step in our dialplan executes the Dial() application which calls
|
|
extension 201 in the [extensions] context of our dialplan. There is no
|
|
requirement that we use the same extension number -- we could have just as
|
|
easily used a named extension, or some other number. Remember that we're dialing
|
|
another channel, but instead of dialing a device, we're "dialing" another part
|
|
of the dialplan.
|
|
|
|
|
|
-- Executing [201@extensions:1] Verbose("Local/201@extensions-7cf4;2", "2,Made it to the Local channel") in new stack
|
|
== Made it to the Local channel
|
|
|
|
Now we've verified we've dialed another part of the dialplan. We can see the
|
|
channel executing the dialplan has changed to Local/201@extensions-7cf4;2. The
|
|
part '-7cf4;2' is just the unique identifier, and will be different for you.
|
|
|
|
|
|
-- Executing [201@extensions:2] Verbose("Local/201@extensions-7cf4;2", "2,Inside channel: Local/201@extensions-7cf4;2") in new stack
|
|
== Inside channel: Local/201@extensions-7cf4;2
|
|
|
|
Here we use the Verbose() application to see what our current channel name is.
|
|
As you can see the current channel is a Local channel which we created from our
|
|
SIP channel.
|
|
|
|
|
|
-- Executing [201@extensions:3] Dial("Local/201@extensions-7cf4;2", "SIP/some-named-extension,30") in new stack
|
|
|
|
And from here, we're using another Dial() application to call a SIP device
|
|
configured in sip.conf as [some-named-extension].
|
|
|
|
Now that we understand a simple example of calling the Local channel, let's
|
|
expand upon this example by using Local channels to call two devices at the same
|
|
time, but delay calling one of the devices.
|
|
|
|
---------------------
|
|
Delay dialing devices
|
|
---------------------
|
|
|
|
Lets say when someone calls extension 201, we want to ring both the desk phone
|
|
and their cellphone at the same time, but we want to wait about 6 seconds to
|
|
start dialing the cellphone. This is useful in a situation when someone might be
|
|
sitting at their desk, but don't want both devices ringing at the same time, but
|
|
also doesn't want to wait for the full ring cycle to execute on their desk phone
|
|
before rolling over to their cellphone.
|
|
|
|
The dialplan for this would look something like the following:
|
|
|
|
[devices]
|
|
exten => 201,1,Verbose(2,Call desk phone and cellphone but with delay)
|
|
exten => 201,n,Dial(Local/deskphone-201@extensions&Local/cellphone-201@extensions,30)
|
|
exten => 201,n,Voicemail(201@default,${IF($[${DIALSTATUS} = BUSY]?b:u)})
|
|
exten => 201,n,Hangup()
|
|
|
|
[extensions]
|
|
; Dial the desk phone
|
|
exten => deskphone-201,1,Verbose(2,Dialing desk phone of extension 201)
|
|
exten => deskphone-201,n,Dial(SIP/0004f2040001) ; SIP device with MAC address
|
|
; of 0004f2040001
|
|
|
|
; Dial the cellphone
|
|
exten => cellphone-201,1,Verbose(2,Dialing cellphone of extension 201)
|
|
exten => cellphone-201,n,Verbose(2,-- Waiting 6 seconds before dialing)
|
|
exten => cellphone-201,n,Wait(6)
|
|
exten => cellphone-201,n,Dial(DAHDI/g0/14165551212)
|
|
|
|
|
|
When someone dials extension 201 in the [devices] context, it will execute the
|
|
Dial() application, and call two Local channels at the same time:
|
|
|
|
* Local/deskphone-201@extensions
|
|
* Local/cellphone-201@extensions
|
|
|
|
It will then ring both of those extensions for 30 seconds before rolling over to
|
|
the Voicemail() application and playing the appropriate voicemail recording
|
|
depending on whether the ${DIALSTATUS} variable returned BUSY or not.
|
|
|
|
When reaching the deskphone-201 extension, we execute the Dial() application
|
|
which calls the SIP device configured as '0004f204001' (the MAC address of the
|
|
device). When reaching the cellphone-201 extension, we dial the cellphone via
|
|
the DAHDI channel using group zero (g0) and dialing phone number 1-416-555-1212.
|
|
|
|
|
|
-----------------------------------------------
|
|
Dialing destinations with different information
|
|
-----------------------------------------------
|
|
|
|
With Asterisk, we can place a call to multiple destinations by separating the
|
|
technology/destination pair with an ampersand (&). For example, the following
|
|
Dial() line would ring two separate destinations for 30 seconds:
|
|
|
|
exten => 201,1,Dial(SIP/0004f2040001&DAHDI/g0/14165551212,30)
|
|
|
|
That line would dial both the SIP/0004f2040001 device (likely a SIP device on
|
|
the network) and dial the phone number 1-416-555-1212 via a DAHDI interface. In
|
|
our example though, we would be sending the same callerID information to both
|
|
end points, but perhaps we want to send a different callerID to one of the
|
|
destinations?
|
|
|
|
We can send different callerIDs to each of the destinations if we want by using
|
|
the Local channel. The following example shows how this is possible because we
|
|
would Dial() two different Local channels from our top level Dial(), and that
|
|
would then execute some dialplan before sending the call off to the final
|
|
destinations.
|
|
|
|
[devices]
|
|
exten => 201,1,NoOp()
|
|
exten => 201,n,Dial(Local/201@internal&Local/201@external,30)
|
|
exten => 201,n,Voicemail(201@default,${IF($[${DEVICE_STATE} = BUSY]?b:u)})
|
|
exten => 201,n,Hangup()
|
|
|
|
[internal]
|
|
exten => 201,1,Verbose(2,Placing internal call for extension 201)
|
|
exten => 201,n,Set(CALLERID(name)=From Sales)
|
|
exten => 201,n,Dial(SIP/0004f2040001,30)
|
|
|
|
[external]
|
|
exten => 201,1,Verbose(2,Placing external call for extension 201)
|
|
exten => 201,n,Set(CALLERID(name)=Acme Cleaning)
|
|
exten => 201,n,Dial(DAHDI/g0/14165551212)
|
|
|
|
|
|
With the dialplan above, we've sent two different callerIDs to the destinations:
|
|
|
|
* "From Sales" was sent to the local device SIP/0004f2040001
|
|
* "Acme Cleaning" was sent to the remote number 1-416-555-1212 via DAHDI
|
|
|
|
Because each of the channels is independent from the other, you could perform
|
|
any other call manipulation you need. Perhaps the 1-416-555-1212 number is a
|
|
cell phone and you know you can only ring that device for 18 seconds before the
|
|
voicemail would pick up. You could then limit the length of time the external
|
|
number is dialed, but still allow the internal device to be dialed for a longer
|
|
period of time.
|
|
|
|
----------------------------------
|
|
Using callfiles and Local channels
|
|
----------------------------------
|
|
|
|
Another example is to use callfiles and Local channels so that you can execute
|
|
some dialplan prior to performing a Dial(). We'll construct a callfile which
|
|
will then utilize a Local channel to lookup a bit of information in the AstDB
|
|
and then place a call via the channel configured in the AstDB.
|
|
|
|
First, lets construct our callfile that will use the Local channel to do some
|
|
lookups prior to placing our call. More information on constructing callfiles is
|
|
located in the doc/callfiles.txt file of your Asterisk source.
|
|
|
|
Our callfile will simply look like the following:
|
|
|
|
Channel: Local/201@devices
|
|
Application: Playback
|
|
Data: silence/1&tt-weasels
|
|
|
|
Add the callfile information to a file such as 'callfile.new' or some other
|
|
appropriately named file.
|
|
|
|
Our dialplan will perform a lookup in the AstDB to determine which device to
|
|
call, and will then call the device, and upon answer, Playback() the silence/1
|
|
(1 second of silence) and the tt-weasels sound files.
|
|
|
|
Before looking at our dialplan, lets put some data into AstDB that we can then
|
|
lookup from the dialplan. From the Asterisk CLI, run the following command:
|
|
|
|
*CLI> database put phones 201/device SIP/0004f2040001
|
|
|
|
We've now put the device destination (SIP/0004f2040001) into the 201/device key
|
|
within the phones family. This will allow us to lookup the device location for
|
|
extension 201 from the database.
|
|
|
|
We can then verify our entry in the database using the 'database show' CLI
|
|
command:
|
|
|
|
*CLI> database show
|
|
/phones/201/device : SIP/0004f2040001
|
|
|
|
Now lets create the dialplan that will allow us to call SIP/0004f2040001 when we
|
|
request extension 201 from the [extensions] context via our Local channel.
|
|
|
|
[devices]
|
|
exten => 201,1,NoOp()
|
|
exten => 201,n,Set(DEVICE=${DB(phones/${EXTEN}/device)})
|
|
exten => 201,n,GotoIf($[${ISNULL(${DEVICE})}]?hangup) ; if nothing returned,
|
|
; then hangup
|
|
exten => 201,n,Dial(${DEVICE},30)
|
|
exten => 201,n(hangup(),Hangup()
|
|
|
|
|
|
Then, we can perform a call to our device using the callfile by moving it into
|
|
the /var/spool/asterisk/outgoing/ directory.
|
|
|
|
# mv callfile.new /var/spool/asterisks/outgoing
|
|
|
|
Then after a moment, you should see output on your console similar to the
|
|
following, and your device ringing. Information about what is going on during
|
|
the output has also been added throughout.
|
|
|
|
-- Attempting call on Local/201@devices for application Playback(silence/1&tt-weasels) (Retry 1)
|
|
|
|
You'll see the line above as soon as Asterisk gets the request from the
|
|
callfile.
|
|
|
|
-- Executing [201@devices:1] NoOp("Local/201@devices-ecf0;2", "") in new stack
|
|
-- Executing [201@devices:2] Set("Local/201@devices-ecf0;2", "DEVICE=SIP/0004f2040001") in new stack
|
|
|
|
This is where we performed our lookup in the AstDB. The value of
|
|
SIP/0004f2040001 was then returned and saved to the DEVICE channel variable.
|
|
|
|
-- Executing [201@devices:3] GotoIf("Local/201@devices-ecf0;2", "0?hangup") in new stack
|
|
|
|
We perform a check to make sure ${DEVICE} isn't NULL. If it is, we'll just
|
|
hangup here.
|
|
|
|
-- Executing [201@devices:4] Dial("Local/201@devices-ecf0;2", "SIP/0004f2040001,30") in new stack
|
|
-- Called 000f2040001
|
|
-- SIP/0004f2040001-00000022 is ringing
|
|
|
|
Now we call our device SIP/0004f2040001 from the Local channel.
|
|
|
|
-- SIP/0004f2040001-00000022 answered Local/201@devices-ecf0;2
|
|
|
|
We answer the call.
|
|
|
|
> Channel Local/201@devices-ecf0;1 was answered.
|
|
> Launching Playback(silence/1&tt-weasels) on Local/201@devices-ecf0;1
|
|
|
|
We then start playing back the files.
|
|
|
|
-- <Local/201@devices-ecf0;1> Playing 'silence/1.slin' (language 'en')
|
|
== Spawn extension (devices, 201, 4) exited non-zero on 'Local/201@devices-ecf0;2'
|
|
|
|
At this point we now see the Local channel has been optimized out of the call
|
|
path. This is important as we'll see in examples later. By default, the Local
|
|
channel will try to optimize itself out of the call path as soon as it can. Now
|
|
that the call has been established and audio is flowing, it gets out of the way.
|
|
|
|
-- <SIP/0004f2040001-00000022> Playing 'tt-weasels.ulaw' (language 'en')
|
|
[Mar 1 13:35:23] NOTICE[16814]: pbx_spool.c:349 attempt_thread: Call completed to Local/201@devices
|
|
|
|
We can now see the tt-weasels file is played directly to the destination
|
|
(instead of through the Local channel which was optimized out of the call path)
|
|
and then a NOTICE stating the call was completed.
|
|
|
|
|
|
Understanding When To Use /n
|
|
============================
|
|
|
|
Lets take a look at an example that demonstrates when the use of the /n
|
|
directive is necessary. If we spawn a Local channel which does a Dial()
|
|
to a SIP channel, but we use the L() option (which is used to limit the
|
|
amount of time a call can be active, along with warning tones when the
|
|
time is nearly up), it will be associated with the Local channel,
|
|
which is then optimized out of the call path, and thus won't perform
|
|
as expected.
|
|
|
|
This following dialplan will not perform as expected.
|
|
|
|
[services]
|
|
exten => 2,1,Dial(SIP/PHONE_B,,L(60000:45000:15000))
|
|
|
|
[internal]
|
|
exten => 4,1,Dial(Local/2@services)
|
|
|
|
By default, the Local channel will try to optimize itself out of the call path.
|
|
This means that once the Local channel has established the call between the
|
|
destination and Asterisk, the Local channel will get out of the way and let
|
|
Asterisk and the end point talk directly, instead of flowing through the Local
|
|
channel.
|
|
|
|
This can have some adverse effects when you're expecting information to be
|
|
available during the call that gets associated with the Local channel. When the
|
|
Local channel is optimized out of the call path, any Dial() flags, or channel
|
|
variables associated with the Local channel are also destroyed and are no longer
|
|
available to Asterisk.
|
|
|
|
We can force the Local channel to remain in the call path by utilizing the /n
|
|
directive. By adding /n to the end of the channel definition, we can keep the
|
|
Local channel in the call path, along with any channel variables, or other
|
|
channel specific information.
|
|
|
|
In order to make this behave as we expect (limiting the call), we would change:
|
|
|
|
[internal]
|
|
exten => 4,1,Dial(Local/2@services)
|
|
|
|
...into the following:
|
|
|
|
[internal]
|
|
exten => 4,1,Dial(Local/2@services/n)
|
|
|
|
|
|
By adding /n to the end, our Local channel will now stay in the call path and
|
|
not go away.
|
|
|
|
Why does adding the /n option all of a suddon make the 'L' option work? First
|
|
we need to show an overview of the call flow that doesn't work properly, and
|
|
discuss the information associated with the channels:
|
|
|
|
1) SIP device PHONE_A calls Asterisk via a SIP INVITE
|
|
|
|
2) Asterisk accepts the INVITE and then starts processing dialplan logic in
|
|
the [internal] context.
|
|
|
|
3) Our dialplan calls Dial(Local/2@services) <-- notice no /n
|
|
|
|
4) The Local channel then executes dialplan at extension 2 within the [services]
|
|
context.
|
|
|
|
5) Extension 2 within [services] then performs Dial() to PHONE_B
|
|
with the line: Dial(SIP/PHONE_B,,L(60000:45000:15000))
|
|
|
|
6) SIP/PHONE_B then answers the call.
|
|
|
|
7) Even though the L option was given when dialing the SIP device, the L
|
|
information is stored in the channel that is doing the Dial() which is the Local
|
|
channel, and not the endpoint SIP channel.
|
|
|
|
8) The Local channel in the middle, containing the information for tracking the
|
|
time allowance of the call, is then optimized out of the call path, losing all
|
|
information about when to terminate the call.
|
|
|
|
9) SIP/PHONE_A and SIP/PHONE_B then continue talking indefinitely.
|
|
|
|
Now, if we were to add /n to our dialplan at step three (3) then we would force the
|
|
Local channel to stay in the call path, and the L() option associated with the
|
|
Dial() from the Local channel would remain, and our warning sounds and timing
|
|
would work as expected.
|
|
|
|
There are two workarounds for the above described scenario:
|
|
|
|
1) Use what we just described, Dial(Local/2@services/n) to cause the Local
|
|
channel to remain in the call path so that the L() option used inside the
|
|
Local channel is not discarded when optimization is performed.
|
|
|
|
2) Place the L() option at the outermost part of the path so that when the middle
|
|
is optimized out of the call path, the information required to make L() work
|
|
is associated with the outside channel. The L information will then be stored
|
|
on the calling channel, which is PHONE_A. For example:
|
|
|
|
[services]
|
|
exten => 2,1,Dial(SIP/PHONE_B)
|
|
|
|
[internal]
|
|
exten => 4,1,Dial(Local/2@services,,L(60000:45000:15000))
|
|
|
|
Local channel modifiers
|
|
=======================
|
|
|
|
There are additional modifiers for the Local channel as well. They include:
|
|
|
|
* 'n' -- Adding "/n" at the end of the string will make the Local channel not
|
|
do a native transfer (the "n" stands for "n"o release) upon the remote
|
|
end answering the line. This is an esoteric, but important feature if
|
|
you expect the Local channel to handle calls exactly like a normal
|
|
channel. If you do not have the "no release" feature set, then as soon
|
|
as the destination (inside of the Local channel) answers the line and
|
|
one audio frame passes, the variables and dial plan will revert back
|
|
to that of the original call, and the Local channel will become a
|
|
zombie and be removed from the active channels list. This is desirable
|
|
in some circumstances, but can result in unexpected dialplan behavior
|
|
if you are doing fancy things with variables in your call handling.
|
|
|
|
* 'j' -- Adding "/j" at the end of the string allows you to use the generic
|
|
jitterbuffer on incoming calls going to Asterisk applications. For
|
|
example, this would allow you to use a jitterbuffer for an incoming
|
|
SIP call to Voicemail by putting a Local channel in the middle. The
|
|
'j' option must be used in conjunction with the 'n' option to make
|
|
sure that the Local channel does not get optimized out of the call.
|
|
|
|
This option is available starting in the Asterisk 1.6.0 branch.
|
|
|
|
* 'm' -- Using the "/m" option will cause the Local channel to forward music on
|
|
hold (MoH) start and stop requests. Normally the Local channel acts on
|
|
them and it is started or stopped on the Local channel itself. This
|
|
options allows those requests to be forwarded through the Local
|
|
channel.
|
|
|
|
This option is available starting in the Asterisk 1.4 branch.
|
|
|
|
* 'b' -- The "/b" option causes the Local channel to return the actual channel
|
|
that is behind it when queried. This is useful for transfer scenarios
|
|
as the actual channel will be transferred, not the Local channel.
|
|
|
|
This option is available starting in the Asterisk 1.6.0 branch.
|