Chapter 14. Device States

Out of clutter, find simplicity.

Albert Einstein

It is often useful to be able to determine the state of the devices that are attached to a telephone system. For example, a receptionist might require the ability to see the statuses of all the people in the office in order to determine whether somebody can take a phone call. Asterisk itself needs this same information. As another example, if you were building a call queue, as discussed in Chapter 13, Asterisk needs to know when an agent is available so that another call can be delivered. This chapter discusses device state concepts in Asterisk, as well as how devices and applications use and access this information.

Device States

There are two types of devices that device states refer to: real devices and virtual devices. Real devices are telephony endpoints that can make or receive calls, such as SIP phones. Virtual devices include things that are inside of Asterisk, but provide useful state information. Table 14-1 lists the available virtual devices in Asterisk.

Table 14-1. Virtual devices in Asterisk

Virtual deviceDescription
MeetMe:<conference bridge>The state of a MeetMe conference bridge. The state will reflect whether or not the conference bridge currently has participants called in. More information on using MeetMe() for call conferencing can be found in Conferencing with MeetMe().
SLA:<shared line>Shared Line Appearance state information. This state is manipulated by the SLATrunk() and SLAStation() applications. More detail can be found in Shared Line Appearances.
Custom:<custom name>Custom device states. These states have custom names and are modified using the DEVICE_STATE() function. Example usage can be found in Using Custom Device States.
Park:<exten@context>The state of a spot in a call parking lot. The state information will reflect whether or not a caller is currently parked at that extension. More information about call parking in Asterisk can be found in Parking Lots.
Calendar:<calendar name>Calendar state. Asterisk will use the contents of the named calendar to set the state to available or busy. More information about calendar integration in Asterisk can be found in Chapter 18.

A device state is a simple one-to-one mapping to a device. Figure 14-1 shows this mapping.

Device state mappings

Figure 14-1. Device state mappings

Checking Device States

The DEVICE_STATE() dialplan function can be used to read the current state of a device. Here is a simple example of it being used in the dialplan:

exten => 7012,1,Answer()

; *** This line should not have any line breaks
    same => n,Verbose(3,The state of SIP/0004F2060EB4 is 
${DEVICE_STATE(SIP/0004F2060EB4)})
    same => n,Hangup()

If we call extension 7012 from the same device that we are checking the state of, the following verbose message comes up on the Asterisk console:

    -- The state of SIP/0004F2060EB4 is INUSE

Note

Chapter 20 discusses the Asterisk Manager Interface (AMI). The GetVar manager action can be used to retrieve device state values in an external program. You can use it to get the value of either a normal variable or a dialplan function, such as DEVICE_STATE().

The following list includes the possible values that will come back from the DEVICE_STATE() function:

  • UNKNOWN

  • NOT_INUSE

  • INUSE

  • BUSY

  • INVALID

  • UNAVAILABLE

  • RINGING

  • RINGINUSE

  • ONHOLD

Extension States

Extension states are another important concept in Asterisk. Extension states are what SIP devices subscribe to for presence information. (SIP presence is discussed in more detail in SIP Presence). The state of an extension is determined by checking the state of one or more devices. The list of devices that map to extension states is defined in the Asterisk dialplan, /etc/asterisk/extensions.conf, using a special hint directive. Figure 14-2 shows the mapping between devices, device states, and extension states.

Extension state mappings

Figure 14-2. Extension state mappings

Hints

To define an extension state hint in the dialplan, the keyword hint is used in place of a priority. Here is a simple example dialplan that relates to Figure 14-2:

[default]

exten => 1234,hint,SIP/phoneA&SIP/phoneB&SIP/phoneC

exten => 5555,hint,DAHDI/1

exten => 31337,hint,MeetMe:31337

Typically, hints are simply defined along with the rest of the extension. This next example adds simple extension entries for what would happen if each of these extensions were called:

[default]

exten => 1234,hint,SIP/phoneA&SIP/phoneB&SIP/phoneC
exten => 1234,1,Dial(SIP/phoneA&SIP/phoneB&SIP/phoneC)

exten => 5555,hint,DAHDI/1
exten => 5555,1,Dial(DAHDI/1)

exten => 31337,hint,MeetMe:31337
exten => 31337,1,MeetMe(31337,dM)

In our example we’ve made a direct correlation between the hint’s extension number and the extension number being dialed, although there is no requirement that that be the case.

Checking Extension States

The easiest way to check the current state of an extension is at the Asterisk CLI. The core show hints command will show you all currently configured hints. Consider the following hint definition:

[phones]

exten => 7001,hint,SIP/0004F2060EB4

When core show hints is executed at the Asterisk CLI, the following output is presented when the device is currently in use:

*CLI> core show hints

    -= Registered Asterisk Dial Plan Hints =-
           7001@phones   : SIP/0004F2060EB4    State:InUse    Watchers  0
----------------
- 1 hints registered

In addition to showing you the state of the extension, the output of core show hints also provides a count of watchers. A watcher is something in Asterisk that has subscribed to receive updates on the state of this extension. If a SIP phone subscribes to the state of an extension, the watcher count will be increased.

Extension state can also be retrieved with a dialplan function, EXTENSION_STATE(). This function operates similarly to the DEVICE_STATE() function described in the preceding section. The following example shows an extension that will print the current state of another extension to the Asterisk console:

exten => 7013,1,Answer()
    same => n,Verbose(3,The state of 7001@phones is ${EXTENSION_STATE(7001@phones)})
    same => n,Hangup()

When this extension is called, this is the verbose message that shows up on the Asterisk console:

    -- The state of 7001@phones is INUSE

The following list includes the possible values that may be returned back from the EXTENSION_STATE() function:

  • UNKNOWN

  • NOT_INUSE

  • INUSE

  • BUSY

  • UNAVAILABLE

  • RINGING

  • RINGINUSE

  • HOLDINUSE

  • ONHOLD

SIP Presence

Asterisk provides the ability for devices to subscribe to extension state using the SIP protocol. This functionality is often referred to as BLF (Busy Lamp Field).[131]

Asterisk Configuration

To get this working, hints must be defined in /etc/asterisk/extensions.conf (see Hints for more information on configuring hints in the dialplan). Additionally, there are some important options that must be set in the configuration file for the SIP channel driver, which is /etc/asterisk/sip.conf. The following list discusses these options:

callcounter

Enables/disables call counters. This must be enabled for Asterisk to be able to provide state information for SIP devices. This option may be set either in the [general] section or in peer-specific sections of sip.conf.

Tip

If you would like device states to work for SIP devices, you must at least set the callcounter option to yes. Otherwise, the SIP channel driver will not bother tracking calls to and from devices and will provide no state information about them.

busylevel

Sets the number of calls that must be in progress for Asterisk to report that a device is busy. This option may only be set in peer-specific sections of sip.conf. By default, this option is not set. This means that Asterisk will report that a device is in use, but never busy.

call-limit

This option has been deprecated in favor of using the GROUP() and GROUP_COUNT() functions in the Asterisk dialplan. You may find older documentation that suggests that this option is required for SIP presence to work. That used to be the case, but this option has been replaced by the callcounter option for that purpose.

allowsubscribe

Allows you to disable support for subscriptions. If this option has not been set, subscriptions will be enabled. To disable subscription support completely, set allowsubscribe to no in the [general] section of sip.conf.

subscribecontext

Allows you to set a specific context for subscriptions. Without this set, the context defined by the context option will be used. This option may be set either in the [general] section or in peer-specific sections of sip.conf.

notifyringing

Controls whether or not a notification will be sent when an extension goes into a ringing state. This option is set to yes by default. It only has an effect on subscriptions that use the dialog-info event package. This option can only be set globally in the [general] section of sip.conf.

notifyhold

Allows chan_sip to set SIP devices’ states to ONHOLD. This is set to yes by default. This option can only be set globally in the [general] section of sip.conf.

notifycid

Enables/disables sending of an inbound call’s caller ID information to an extension. This option applies to devices that subscribe to dialog-info+xml-based extension state notifications, such as Snom phones. Displaying caller ID information can be useful to help an agent decide whether to execute a pickup on an incoming call. This option is set to no by default.

Note

This magic pickup only works if the extension and context of the hint are the same as the extension and context of the incoming call. Notably, the usage of the subscribecontext option usually breaks this option. This option can also be set to the value ignore-context. This will bypass the context issue, but should only be used in an environment where there is only a single instance of the extension that has been subscribed to. Otherwise, you might accidentally pick up calls that you did not mean to pick up.

Using Custom Device States

Asterisk provides the ability to create custom device states. This lends itself to the development of some interesting custom applications. We’ll start by showing the basic syntax for controlling custom device states, and then we’ll build an example that uses them.

Custom device states all start with a prefix of Custom:. The text that comes after the prefix can be anything you want. To set or read the value of a custom device state, use the DEVICE_STATE() dialplan function. For example, to set a custom device state:

exten => example,1,Set(DEVICE_STATE(Custom:example)=BUSY)

Similarly, to read the current value of a custom device state:

exten => Verbose(1,The state of Custom:example is ${DEVICE_STATE(Custom:example)})

Custom device states can be used as a way to directly control the state shown on a device that has subscribed to the state of an extension. Just map an extension to a custom device state using a hint in the dialplan:

exten => example,hint,Custom:example

An Example

There are a number of interesting use cases for custom device states. In this section we will build an example that implements a custom “do not disturb” (DND) button on a SIP phone. This same approach could be applied to many other things that you might like to be able to toggle at the touch of a button. For example, this approach could be used to let members know if they are currently logged into a queue or not.

The first piece of the example is the hint in the dialplan. This is required so BLF can be configured on a SIP phone to subscribe to this extension. In this case, the phone must be configured to subscribe to the state of DND_7015:

exten => DND_7015,hint,Custom:DND_7015

Next, we will create an extension that will be called when the user presses the key associated with the custom DND feature. It is interesting to note that this extension does nothing with audio. In fact, the user of the phone most likely will not even know that a call is placed when he presses the button. As far as the user is concerned, pressing that key simply turns on or off the light next to the button that reflects whether or not DND is enabled. The extension should look like this:

exten => DND_7015,1,Answer()
    same => n,GotoIf($["${DEVICE_STATE(Custom:DND_7015)}"="BUSY"]?turn_off:turn_on)

    same => n(turn_off),Set(DEVICE_STATE(Custom:DND_7015)=NOT_INUSE)
    same => n,Hangup()

    same => n(turn_on),Set(DEVICE_STATE(Custom:DND_7015)=BUSY)
    same => n,Hangup()

The final part of this example shows how the DND state is used in the dialplan. If DND is enabled, a message is played to the caller saying that the agent is unavailable. If it is disabled, a call will be made to a SIP device:

exten => 7015,1,GotoIf($["${DEVICE_STATE(Custom:DND_7015)}"="BUSY"]?busy:available)
    same => n(available),Verbose(3,DND is currently off for 7015.)
    same => n,Dial(SIP/exampledevice)
    same => n,Hangup()

    same => n(busy),Verbose(3,DND is on for 7015.)
    same => n,Playback(vm-theperson)
    same => n,Playback(digits/7&digits/0&digits/1&digits/5)
    same => n,Playback(vm-isunavail)
    same => n,Playback(vm-goodbye)
    same => n,Hangup()

Example 14-1 shows the full example as it would appear in /etc/asterisk/extensions.conf.

Example 14-1. Custom “do not disturb” functionality using custom device states

;
; A hint so a phone can use BLF to signal the DND state.
;
exten => DND_7015,hint,Custom:DND_7015

;
; An extension to dial when the user presses the custom DND
; key on his phone.  This will toggle the state and will result
; in the light on the phone turning on or off.
;
exten => DND_7015,1,Answer()
    same => n,GotoIf($["${DEVICE_STATE(Custom:DND_7015)}"="BUSY"]?turn_off:turn_on)

    same => n(turn_off),Set(DEVICE_STATE(Custom:DND_7015)=NOT_INUSE)
    same => n,Hangup()

    same => n(turn_on),Set(DEVICE_STATE(Custom:DND_7015)=BUSY)
    same => n,Hangup()


;
; Example usage of the DND state.
;
exten => 7015,1,GotoIf($["${DEVICE_STATE(Custom:DND_7015)}"="BUSY"]?busy:available)
    same => n(available),Verbose(3,DND is currently off for 7015.)
    same => n,Dial(SIP/exampledevice)
    same => n,Hangup()

    same => n(busy),Verbose(3,DND is on for 7015.)
    same => n,Playback(vm-theperson)
    same => n,Playback(digits/7&digits/0&digits/1&digits/5)
    same => n,Playback(vm-isunavail)
    same => n,Playback(vm-goodbye)
    same => n,Hangup()

Distributed Device States

Asterisk is primarily designed to run on a single system. However, as requirements for scalability increase, it is common for deployments to require multiple Asterisk servers. Since that has become increasingly common, some features have been added to make it easier to coordinate multiple Asterisk servers. One of those features is distributed device state support.

What this means is that if a device is on a call on one Asterisk server, the state of that device on all servers reflects that. To be more specific, the way this works is that every server knows the state of each device from the perspective of each server. Using this collection of states, each server will calculate what the overall device state value is to report to the rest of Asterisk.

To accomplish distributed device state, some sort of messaging mechanism must be used for the servers to communicate with each other. Two such mechanisms are supported as of Asterisk 1.8: AIS and XMPP.

Using OpenAIS

The Application Interface Specification (AIS) is a standardized set of messaging middleware APIs. The definition for the APIs is provided by the Service Availability Forum. The open source implementation of AIS that was used for the development and testing of this functionality is OpenAIS, which is built on Corosync.

Corosync, and thus OpenAIS, is built in such a way that nodes must be located on the same high-speed, low-latency LAN. If your deployment is geographically distributed, you should use the XMPP-based distributed device state support, which is discussed in Using XMPP.

Installation

The first step to getting the necessary components installed is to install Corosync and OpenAIS. Corosync depends on the NSS library. Install the libnss3-dev package on Ubuntu or the nss-devel package on CentOS.

Next, install Corosync and OpenAIS. There may be packages available, but they are also fairly straightforward to install from source. Download the latest releases from the Corosync and OpenAIS home pages. Then, execute the following commands to compile and install each package:

$ tar xvzf corosync-1.2.8.tar.gz
$ cd corosync-1.2.8
$ ./configure
$ make
$ sudo make install

$ tar xvzf openais-1.1.4.tar.gz
$ cd openais-1.1.4
$ ./configure
$ make
$ sudo make install

If you installed Asterisk prior to installing Corosync and OpenAIS, you will need to re-compile and reinstall Asterisk to get AIS support. Start by running the Asterisk configure script. The configure script is responsible for inspecting the system to find out which optional dependencies can be found so that the build system knows which modules can be built:

$ cd /path/to/asterisk
$ ./configure

After running the configure script, run the menuselect tool to ensure that Asterisk has been told to build the res_ais module (this module can be found in the Resource Modules section of menuselect):

$ make menuselect

Finally, compile and install Asterisk:

$ make
$ sudo make install

Note

This is a pretty quick and crude set of instructions for compiling and installing Asterisk. For a much more complete set of instructions, please see Chapter 3.

OpenAIS configuration

Now that OpenAIS has been installed, it needs to be configured. There is a configuration file for both OpenAIS and Corosync that must be put in place. Check to see if /etc/ais/openais.conf and /etc/corosync/corosync.conf exist. If they do not exist, copy in the sample configuration files:

$ sudo mkdir -p /etc/ais
$ cd openais-1.1.4
$ sudo cp conf/openais.conf.sample /etc/ais/openais.conf

$ sudo mkdir -p /etc/corosync
$ cd corosync-1.2.8
$ sudo cp conf/corosync.conf.sample /etc/corosync/corosync.conf

Next, you will need to edit both the openais.conf and corosync.conf files. There are a number of options here, but the most important one that must be changed is the bindnetaddr option in the totem-interface section. This must be set to the IP address of the network interface that this node will use to communicate with the rest of the cluster:

totem {
    ...
    interface {
        ringnumber: 0
        bindnetaddr: 10.24.22.144
        mcastaddr: 226.94.1.1
        mcastport: 5405
    }
}

For detailed documentation on the rest of the options in these configuration files, see the associated manpages:

$ man openais.conf
$ man corosync.conf

To get started with testing out basic OpenAIS connectivity, try starting the aisexec application in the foreground and watching the output:

$ sudo aisexec -f

For example, if you watch the output of aisexec on the first node while you bring up the second node, you should see output that reflects that the cluster now has two connected nodes:

Nov 13 06:55:30 corosync [CLM   ] CLM CONFIGURATION CHANGE
Nov 13 06:55:30 corosync [CLM   ] New Configuration:
Nov 13 06:55:30 corosync [CLM   ]       r(0) ip(10.24.22.144) 
Nov 13 06:55:30 corosync [CLM   ]       r(0) ip(10.24.22.242) 
Nov 13 06:55:30 corosync [CLM   ] Members Left:
Nov 13 06:55:30 corosync [CLM   ] Members Joined:
Nov 13 06:55:30 corosync [CLM   ]       r(0) ip(10.24.22.242) 
Nov 13 06:55:30 corosync [TOTEM ] A processor joined or left the membership and a new 
membership was formed.
Nov 13 06:55:30 corosync [MAIN  ] Completed service synchronization, ready to provide
service.

Tip

If you have any trouble getting the nodes to sync up with each other, one thing to check is that there are no firewall rules on the nodes that are blocking the multicast traffic that is used for the nodes to communicate with each other.

Asterisk configuration

The res_ais module for Asterisk has a single configuration file, /etc/asterisk/ais.conf. One short section is required in this file to enable distributed device state in an AIS cluster. Place the following contents in the /etc/asterisk/ais.conf file:

[device_state]

type = event_channel
publish_event = device_state
subscribe_event = device_state

There is an Asterisk CLI command that can be used to ensure that this configuration has been loaded properly:

*CLI> ais evt show event channels 

=============================================================
=== Event Channels ==========================================
=============================================================
===
=== ---------------------------------------------------------
=== Event Channel Name: device_state
=== ==> Publishing Event Type: device_state
=== ==> Subscribing to Event Type: device_state
=== ---------------------------------------------------------
===
=============================================================

Another useful Asterisk CLI command provided by the res_ais module is used to list the members of the AIS cluster:

*CLI> ais clm show members 

=============================================================
=== Cluster Members =========================================
=============================================================
===
=== ---------------------------------------------------------
=== Node Name: 10.24.22.144
=== ==> ID: 0x9016180a
=== ==> Address: 10.24.22.144
=== ==> Member: Yes
=== ---------------------------------------------------------
===
=== ---------------------------------------------------------
=== Node Name: 10.24.22.242
=== ==> ID: 0xf216180a
=== ==> Address: 10.24.22.242
=== ==> Member: Yes
=== ---------------------------------------------------------
===
=============================================================

Testing device state changes

Now that you’ve set up and configured distributed device state using OpenAIS, there are some simple tests that can be done using custom device states to ensure that device states are being communicated between the servers. Start by creating a test hint in the Asterisk dialplan, /etc/asterisk/extensions.conf:

[devstate_test]

exten => foo,hint,Custom:abc

Now, you can adjust the custom device state from the Asterisk CLI using the dialplan set global CLI command and then check the state on each server using the core show hints command. For example, we can use this command to set the state on one server:

pbx1*CLI> dialplan set global DEVICE_STATE(Custom:abc) INUSE

    -- Global variable 'DEVICE_STATE(Custom:abc)' set to 'INUSE'

and then, check the state on another server using this command:

*CLI> core show hints

-= Registered Asterisk Dial Plan Hints =-
   foo@devstatetest        : Custom:abc        State:InUse       Watchers  0

If you would like to dive deeper into the processing of distributed device state changes, there are some useful debug messages that can be enabled. First, enable debug on the Asterisk console in /etc/asterisk/logger.conf. Then, enable debugging at the Asterisk CLI:

*CLI> core set debug 1

With the debug output enabled, you will see some messages that show how Asterisk is processing each state change. When the state of a device changes on one server, Asterisk checks the state information it has for that device on all servers and determines the overall device state. The following examples illustrate:

*CLI> dialplan set global DEVICE_STATE(Custom:abc) NOT_INUSE

    -- Global variable 'DEVICE_STATE(Custom:abc)' set to 'NOT_INUSE'

[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:652 
handle_devstate_change: Processing device state change for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:602 
process_collection: Adding per-server state of 'Not in use' for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:602 
process_collection: Adding per-server state of 'Not in use' for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:609 
process_collection: Aggregate devstate result is 'Not in use' for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:631 
process_collection: Aggregate state for device 'Custom:abc' has changed to 
'Not in use'

*CLI> dialplan set global DEVICE_STATE(Custom:abc) INUSE

    -- Global variable 'DEVICE_STATE(Custom:abc)' set to 'INUSE'

[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:652 handle_devstate_change: 
Processing device state change for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:602 process_collection: 
Adding per-server state of 'Not in use' for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:602 process_collection: 
Adding per-server state of 'In use' for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:609 process_collection: 
Aggregate devstate result is 'In use' for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:631 process_collection: 
Aggregate state for device 'Custom:abc' has changed to 'In use'

Using XMPP

The eXtensible Messaging and Presence Protocol (XMPP), formerly (and still commonly) known as Jabber, is an IETF standardized communications protocol. It is most commonly known as an IM protocol, but it can be used for a number of other interesting applications as well. The XMPP Standards Foundation (XSF) works to standardize extensions to the XMPP protocol. One such extension, referred to as PubSub, provides a publish/subscribe mechanism.

Asterisk has the ability to use XMPP PubSub to distribute device state information. One of the nice things about using XMPP to accomplish this is that it works very well for geographically distributed Asterisk servers.

Installation

To distribute device states using XMPP, you will need an XMPP server that supports PubSub. One such server that has been successfully tested against Asterisk is Tigase.

The Tigase website has instructions for installing and configuring the Tigase server. We suggest that you follow those instructions (or the instructions provided for whatever other server you may choose to use) and come back to this book when you’re ready to work on the Asterisk-specific parts.

On the Asterisk side of things, you will need to ensure that you have installed the res_jabber module. You can check to see if it is already loaded at the Asterisk CLI:

*CLI> module show like jabber

Module                         Description                              Use Count 
res_jabber.so                  AJI - Asterisk Jabber Interface          0         
1 modules loaded

If you are using a custom /etc/asterisk/modules.conf file that lists only specific modules to be loaded, you can also check the filesystem to see if the module was compiled and installed:

$ ls -l /usr/lib/asterisk/modules/res_jabber.so

-rwxr-xr-x 1 root root 837436 2010-11-12 15:33 /usr/lib/asterisk/modules/res_jabber.so

If you do not yet have res_jabber installed, you will need to install the iksemel and OpenSSL libraries. Then, you will need to recompile and reinstall Asterisk. Start by running the Asterisk configure script, which is responsible for inspecting the system and locating optional dependencies, so that the build system knows which modules can be built:

$ cd /path/to/asterisk
$ ./configure

After running the configure script, run the menuselect tool to ensure that Asterisk has been told to build the res_jabber module. This module can be found in the Resource Modules section of menuselect:

$ make menuselect

Finally, compile and install Asterisk:

$ make
$ sudo make install

Note

This is a pretty quick and crude set of instructions for compiling and installing Asterisk. For a much more complete set of instructions, please see Chapter 3.

Creating XMPP accounts

Unfortunately, Asterisk is currently not able to register new accounts on an XMPP server. You will have to create an account for each server via some other mechanism. The method we used while testing was to use an XMPP client such as Pidgin to complete the account registration process. After account registration is complete, the XMPP client is no longer needed. For the rest of the examples, we will use the following two buddies, both of which are on the server jabber.shifteight.org:

Asterisk configuration

The /etc/asterisk/jabber.conf file will need to be configured on each server. We will show the configuration for a two-server setup here, but the configuration can easily be expanded to more servers as needed. Example 14-2 shows the contents of the configuration file for server 1 and Example 14-3 shows the contents of the configuration file for server 2. For additional information on the jabber.conf options associated with distributed device states, see the configs/jabber.conf.sample file that is included in the Asterisk source tree.

Example 14-2. jabber.conf for server1

[general]
autoregister = yes

[asterisk]
type = client
serverhost = jabber.shifteight.org
pubsub_node = pubsub.jabber.shifteight.org
username = [email protected]/astvoip1
secret = mypassword
distribute_events = yes
status = available
usetls = no
usesasl = yes
buddy = [email protected]/astvoip2

Example 14-3. jabber.conf for server2

[general]
autoregister = yes

[asterisk]
type = client
serverhost = jabber.shifteight.org
pubsub_node = pubsub.jabber.shifteight.org
username = [email protected]/astvoip2
secret = mypassword
distribute_events = yes
status = available
usetls = no
usesasl = yes
buddy = [email protected]/astvoip1

Testing

To ensure that everything is working properly, start by doing some verification of the jabber.conf settings on each server. There are a couple of relevant Asterisk CLI commands that can be used here. The first is the jabber show connected command, which will verify that Asterisk has successfully logged in with an account on the jabber server. The output of this command on the first server shows:

*CLI> jabber show connected

Jabber Users and their status:
       User: [email protected]/astvoip1     - Connected
----
   Number of users: 1

Meanwhile, if jabber show connected is executed on the second server, it shows:

*CLI> jabber show connected

Jabber Users and their status:
       User: [email protected]/astvoip2     - Connected
----
   Number of users: 1

The next useful command for verifying the setup is jabber show buddies. This command allows you to verify that the other server is correctly listed on your buddy list. It also lets you see if the other server is seen as currently connected. If you were to run this command on the first server without Asterisk currently running on the second server, the output would look like this:

*CLI> jabber show buddies

Jabber buddy lists
Client: [email protected]/astvoip1
    Buddy: [email protected]
           Resource: None
    Buddy: [email protected]/astvoip2
           Resource: None

Next, start Asterisk on the second server and run jabber show buddies on that server. The output will contain more information, since the second server will see the first server online:

*CLI> jabber show buddies

Jabber buddy lists
Client: [email protected]/astvoip2
    Buddy: [email protected]
           Resource: astvoip1
               node: http://www.asterisk.org/xmpp/client/caps
               version: asterisk-xmpp
               Jingle capable: yes
           Status: 1
           Priority: 0
    Buddy: [email protected]/astvoip1
           Resource: None

At this point, you should be ready to test out the distribution of device states. The procedure is the same as that for testing device states over AIS, which can be found in Testing device state changes.

Shared Line Appearances

In Asterisk, Shared Line Appearances (SLA)—sometimes also referred to in the industry as Bridged Line Appearances (BLA)—can be used. This functionality can be used to satisfy two primary use cases, which include emulating a simple key system and creating shared extensions on a PBX.

Building key system emulation is the use case for which these applications were primarily designed. In this environment, you have some small number of trunks coming into the PBX, such as analog phone lines, and each phone has a dedicated button for calls on that trunk. You may refer to these trunks as line 1, line 2, and line 3, for example.

The second primary use case is for creating shared extensions on your PBX. This use case seems to be the most common these days. There are many reasons you might want to do this. One example is that you may want an extension to appear on both the phones of an executive and her administrative assistant. Another example would be if you want the same extension to appear on all of the phones in the same lab.

While these use cases are supported to an extent, there are limitations. There is still more work to be done in Asterisk to make these features work really well for what people want to do with them. These limitations are discussed in Limitations.

Installing the SLA Applications

The SLA applications are built on two key technologies in Asterisk. The first is device state processing, and the second is conferencing. Specifically, the conferencing used by these applications is the MeetMe() application. The SLA applications come with the same module as the MeetMe() application, so you must install the app_meetme module.

You can check at the Asterisk CLI to see if you already have the module:

pbx*CLI> module show like app_meetme.so

Module                         Description                              Use Count 
0 modules loaded

In this case, the module is not present. The most common reason that an Asterisk system does not have the app_meetme module is because DAHDI has not been installed. The MeetMe() application uses DAHDI to perform conference mixing. Once DAHDI is installed (refer to Chapter 3 for installation information), rerun the Asterisk configure script, recompile, and reinstall. Once the module has been properly installed, you should be able to see it at the CLI:

*CLI> module show like app_meetme.so

Module                         Description                              Use Count 
app_meetme.so                  MeetMe conference bridge                 0         
1 modules loaded

Once the app_meetme module is loaded, you should have both the SLAStation() and SLATrunk() applications available:

*CLI> core show applications like SLA

    -= Matching Asterisk Applications =-
            SLAStation: Shared Line Appearance Station. 
              SLATrunk: Shared Line Appearance Trunk. 
    -= 2 Applications Matching =-

Configuration Overview

The two main configuration files that must be edited to set up SLA are /etc/asterisk/extensions.conf and /etc/asterisk/sla.conf. The sla.conf file is used for defining trunks and stations. A station is any SIP phone that will be using SLA. Trunks are the literal trunks or shared extensions that will be appearing on two or more stations. The Asterisk dialplan, extensions.conf, provides some important glue that pulls an SLA configuration together. The dialplan includes some extension state hints and extensions that define how calls get into and out of an SLA setup. The next few sections provide detailed examples of the configuration for a few different use cases.

Key System Example with Analog Trunks

This usage of SLA comes with the simplest configuration.[132] This scenario would typically be used for a fairly small installation, where you have a few analog lines and SIP phones that all have line keys directly associated with the analog lines. For the purposes of this example, we will say we have two analog lines and four SIP phones. Each SIP phone will have a button for line1 and a button for line2. This section will assume that you have done some configuration up front, including:

  • Configuring the four SIP phones. For more information on setting up SIP phones, see Chapter 5.

  • Configuring the two analog lines. Fore more information on setting up analog lines with Asterisk, see Chapter 7.

For this example, we will use the following device names for the SIP phones and analog lines. Be sure to adapt the examples to match your own configuration:

  • SIP/station1

  • SIP/station2

  • SIP/station3

  • SIP/station4

  • DAHDI/1

  • DAHDI/2

sla.conf

As mentioned previously, sla.conf contains configuration that maps devices to trunks and stations. For this example, we will start by defining the two trunks:

[line1]
type = trunk
device = DAHDI/1

[line2]
type = trunk
device = DAHDI/2

Next, we will set up the station definitions. We have four SIP phones, which will each use both trunks. Note that the section names in sla.conf for stations do not need to match the SIP device names, but it is done that way here for convenience:

[station1]
type = station
device = SIP/station1
trunk = line1
trunk = line2

[station2]
type = station
device = SIP/station2
trunk = line1
trunk = line2

[station3]
type = station
device = SIP/station3
trunk = line1
trunk = line2

[station4]
type = station
device = SIP/station4
trunk = line1
trunk = line2

The station configuration is a bit repetitive. Asterisk configuration file template sections come in handy here to collapse the configuration down a bit. Here is the station configuration again, but this time using a template:

[station](!)
type = trunk
trunk = line1
trunk = line2

[station1](station)
device = SIP/station1

[station2](station)
device = SIP/station2

[station3](station)
device = SIP/station3

[station4](station)
device = SIP/station4

extensions.conf

The next configuration file required for this example is /etc/asterisk/extensions.conf. There are three contexts. First, we have the line1 and line2 contexts. When a call comes in on one of the analog lines, it will come in to one of these contexts in the dialplan and execute the SLATrunk() application. This application will take care of ringing all of the appropriate stations:

[line1]

exten => s,1,SLATrunk(line1)

[line2]

exten => s,1,SLATrunk(line2)

The next section of the dialplan is the sla_stations context. All calls from the SIP phones should be sent to this context. Further, the SIP phones should be configured such that as soon as they go off-hook, they immediately make a call to the station1 extension (or station2, station3, etc., as appropriate). If the line1 key on the phone is pressed, a call should be sent to the station1_line1 extension (or station2_line1, etc.).

Any time that a phone goes off-hook or a line key is pressed, the call that is made will immediately connect it to one of the analog lines. For a line that is not already in use, the analog line will be providing a dialtone, and the user will be able to send digits to make a call. If a user presses a line key for a line that is already in use, that user will be bridged into the existing call on that line. The sla_stations context looks like this:

[sla_stations]

exten => station1,1,SLAStation(station1)
exten => station1_line1,hint,SLA:station1_line1
exten => station1_line1,1,SLAStation(station1_line1)
exten => station1_line2,hint,SLA:station1_line2
exten => station1_line2,1,SLAStation(station1_line2)

exten => station2,1,SLAStation(station2)
exten => station2_line1,hint,SLA:station2_line1
exten => station2_line1,1,SLAStation(station2_line1)
exten => station2_line2,hint,SLA:station2_line2
exten => station2_line2,1,SLAStation(station2_line2)

exten => station3,1,SLAStation(station3)
exten => station3_line1,hint,SLA:station3_line1
exten => station3_line1,1,SLAStation(station3_line1)
exten => station3_line2,hint,SLA:station3_line2
exten => station3_line2,1,SLAStation(station3_line2)

exten => station4,1,SLAStation(station4)
exten => station4_line1,hint,SLA:station4_line1
exten => station4_line1,1,SLAStation(station4_line1)
exten => station4_line2,hint,SLA:station4_line2
exten => station4_line2,1,SLAStation(station4_line2)

Additional phone configuration tasks

The previous section covered the dialplan for trunks and stations. There are some specific things to keep in mind when setting up phones for use with this setup. First, each phone should be configured to send a call as soon as it is taken off-hook.

The other important item is the configuration of the line keys. Asterisk uses extension state subscriptions to control the LEDs next to the line buttons. Beyond that, each line key should be configured as a speed-dial. Use the following checklist for your line key configuration (how you accomplish these tasks will depend on the specific phones you are using):

  • Set the label of the key to be Line 1 (etc.), or whatever you deem appropriate.

  • Set up the keys such that the Line 1 key on station1 subscribes to the state of station1_line1, and so on. This is required so Asterisk can make the LEDs reflect the state of the lines.

  • Ensure that if the Line 1 key on station1 is pressed a call is sent to the station1_line1 extension, and so on.

Key System Example with SIP Trunks

This example is intended to be identical in functionality to the previous example. The difference is that instead of using analog lines as trunks, we will use a connection to a SIP provider that will terminate the calls to the PSTN. For more information on setting up Asterisk to connect to a SIP provider, see Chapter 7.

sla.conf

The sla.conf file for this scenario is a bit tricky.[133] You might expect to see the device line in the trunk configuration have a SIP channel listed, but instead we’re going to use a Local channel. This will allow us to use some additional dialplan logic for call processing. The purpose of the Local channel will become clearer in the next section, when the dialplan example is discussed. Here are the trunk configurations:

[line1]
type = trunk
device = Local/disa@line1_outbound

[line2]
type = trunk
device = Local/disa@line2_outbound

The station configuration is identical to the last example, so let’s get right to it:

[station](!)
type = trunk
trunk = line1
trunk = line2

[station1](station)
device = SIP/station1

[station2](station)
device = SIP/station2

[station3](station)
device = SIP/station3

[station4](station)
device = SIP/station4

extensions.conf

As in the last example, you will need line1 and line2 contexts to process incoming calls on these trunks:

[line1]

exten => s,1,SLATrunk(line1)

;
; If the provider specifies your phone number when sending you
; a call, you will need another rule in the dialplan to match that.
;
exten => _X.,1,Goto(s,1)

[line2]

exten => s,1,SLATrunk(line2)

exten => _X.,1,Goto(s,1)

This example requires an sla_stations context, as well. This is for all calls coming from the phones. It’s the same as it was in the last example:

[sla_stations]

exten => station1,1,SLAStation(station1)
exten => station1_line1,hint,SLA:station1_line1
exten => station1_line1,1,SLAStation(station1_line1)
exten => station1_line2,hint,SLA:station1_line2
exten => station1_line2,1,SLAStation(station1_line2)

exten => station2,1,SLAStation(station2)
exten => station2_line1,hint,SLA:station2_line1
exten => station2_line1,1,SLAStation(station2_line1)
exten => station2_line2,hint,SLA:station2_line2
exten => station2_line2,1,SLAStation(station2_line2)

exten => station3,1,SLAStation(station3)
exten => station3_line1,hint,SLA:station3_line1
exten => station3_line1,1,SLAStation(station3_line1)
exten => station3_line2,hint,SLA:station3_line2
exten => station3_line2,1,SLAStation(station3_line2)

exten => station4,1,SLAStation(station4)
exten => station4_line1,hint,SLA:station4_line1
exten => station4_line1,1,SLAStation(station4_line1)
exten => station4_line2,hint,SLA:station4_line2
exten => station4_line2,1,SLAStation(station4_line2)

The last piece of the dialplan that is required is the implementation of the line1_outbound and line2_outbound contexts. This is what the SLA applications use when they want to send calls out to a SIP provider. The key to this setup is the usage of the DISA() application. In the last example, phones were directly connected to an analog line. This allowed the upstream switch to provide a dialtone, collect digits, and then complete the call. In this example, we use the DISA() application locally to provide a dialtone and collect digits. Once a complete number has been dialed, the call will proceed to go out to a SIP provider:

[line1_outbound]

exten => disa,1,DISA(no-password,line1_outbound)

;
; Add extensions for whatever numbers you would like to
; allow to be dialed.
;
exten => _1NXXNXXXXXX,1,Dial(SIP/${EXTEN}@myprovider)

[line2_outbound]

exten => disa,1,DISA(no-password,line2_outbound)

exten => _1NXXNXXXXXX,1,Dial(SIP/${EXTEN}@myprovider)

Shared Extension Example

The previous two examples were for small key system emulation. For this example, we’ll try something quite different. Many PBX vendors offer the ability to have the same extension shared across multiple phones. This is not simply a matter of having multiple phones ring when an extension is called: it is deeper integration than that. The behavior of the line key for a shared extension is similar to that of a line key on a key system. For example, you can simply put a call on hold from one phone and pick it up from another. Also, if multiple phones press the key for the shared extension, they will all be bridged into the same call. That is why this functionality is often also referred to as Bridged Line Appearances (BLA).

In the previous two examples, we had two trunks and four stations. For this example, we’re going to set up a single shared extension on two phones. The shared extension will be referred to as extension 5001.

sla.conf

Every usage of the SLA applications requires trunk and station definitions. This example, like the previous ones, will be making use of the DISA() application and the sla.conf file will look very similar:

[5001]
type = trunk
device = Local/disa@5001_outbound

[5001_phone1]
device = SIP/5001_phone1
trunk = 5001

[5001_phone2]
device = SIP/5001_phone2
trunk = 5001

extensions.conf

The first part of the dialplan that is required is what will be executed when extension 5001 is dialed on the PBX. Normally, to call a phone you would use the Dial() application. In this case, we’re going to use the SLATrunk() application. This will take care of ringing both phones and keeping them bridged together:

exten => 5001,1,SLATrunk(5001)

Next, we will need a context that will be used for making outbound calls from this shared extension. This assumes that 5001_phone1 and 5001_phone2 have been configured with their context options set to 5001 in sip.conf:

[5001]

;
; This extension is needed if you want the shared extension to
; be used by default. In that case, have this extension dialed
; when the phone goes off-hook.
;
exten => 5001_phone1,1,SLAStation(5001_phone1)
;
; This is the extension that should be dialed when the 5001 key is
; pressed on 5001_phone1.
;
exten => 5001_phone1_5001,hint,SLA:5001_phone1_5001
exten => 5001_phone1_5001,1,SLAStation(5001_phone1_5001)

exten => 5001_phone2,1,SLAStation(5001_phone2)
exten => 5001_phone2_5001,hint,SLA:5001_phone2_5001
exten => 5001_phone2_5001,1,SLAStation(5001_phone2_5001)

Finally, we need an implementation of the 5001_outbound context. This will be used to provide a dialtone and collect digits on the bridged line:

[5001_outbound]

exten => disa,1,DISA(no-password,5001_outbound)

;
; This context will also need to be able to see whatever
; extensions you would like to be reachable from this extension.
;
include => pbx_extensions

Additional Configuration

The /etc/asterisk/sla.conf file has some optional configuration parameters that were not used in any of the examples in this chapter. To give you an idea of what other behavior can be configured, the options are covered here. This file has a [general] section that is reserved for global configuration options. Currently, there is only a single option that can be specified in this section:

attemptcallerid = yes

This option specifies whether or not the SLA applications should attempt to pass caller ID information. It is set to no by default. If this is enabled, the display of the phones may not be what you would expect in some situations.

The trunk definitions in the previous examples only specified the type and device. Here are some additional options that can be specified for a trunk:

autocontext = line1

If this option is set, Asterisk will automatically create a dialplan context using this name. The context will contain an s extension that executes the SLATrunk() application with the appropriate argument for this trunk. By default, all dialplan entries must be created manually.

ringtimeout = 20

This option allows you to specify the number of seconds to allow an inbound call on this trunk to ring before the SLATrunk() application will exit and consider it an unanswered call. By default, this option is not set.

barge = no

The barge option specifies whether or not other stations are allowed to join a call that is in progress on this trunk by pressing the same line button. Barging into a call on a trunk is allowed by default.

hold = private

The hold option specifies hold permissions for this trunk. If this option is set to open, any station can place this trunk on hold and any other station is allowed to take it back off of hold. If this option is set to private, only the station that placed the trunk on hold is allowed to take it back off of hold. This option is set to open by default.

When we defined the stations in the previous examples, we only supplied the type, device, and a list of trunks. However, station definitions accept some additional configuration options, as well. They are listed here:

autocontext = sla_stations

If this option is specified, Asterisk will automatically create the extensions required for calls coming from this station in the context specified. This is off by default, which means that all extensions must be specified manually.

ringtimeout = 20

A timeout may be specified in seconds for how long this station will ring before the call is considered unanswered. There is no timeout set by default.

ringdelay = 5

A ring delay in seconds can be specified for a station. If a delay is specified, this station will not start ringing until this number of seconds after the call first came in on this shared line. There is no delay set by default.

hold = private

Hold permissions can be specified for a specific station as well. If this option is set to private, any trunks put on hold by this station can only be picked back up by this station. By default, this is set to open.

trunk = line1,ringtimeout=20

A ringtimeout can be applied to calls coming from only a specific trunk.

trunk = line1,ringdelay=5

A ringdelay can also be applied to calls from a specific trunk.

Limitations

While Asterisk makes many things easy, SLA is not one of them. Despite this functionality being intended to emulate simple features, the configuration required to make it work is fairly complex. Someone who is new to Asterisk and only wants a simple key system setup will have to learn a lot of complex Asterisk and SIP phone concepts to get it working.

Another feature that still needs some development work before it will work seamlessly with SLA is caller ID. At the time that this functionality was written, Asterisk did not have the appropriate infrastructure in place to be able to update caller ID information throughout the duration of the call. Based on how this functionality is implemented, this infrastructure is required to make the display on the phones useful. It does exist as of Asterisk 1.8 but the SLA applications have not yet been updated to use it. The end result is that you can either have no caller ID information at all, or you can enable it and understand that the phone displays are not always going to display correctly as changes happen throughout the duration of a call.

Another limitation, most relevant to usage of shared extensions, is that transfers do not work. The main reason is that transfers generally involve putting a call on hold for a short time. Call hold is processed in a special way with SLA, in that the held call is not controlled by the phone that initiated the hold. This breaks transfer processing.

In summary, SLA is not necessarily simple to set up, and it comes with some significant limitations. With that said, if what does exist suits your needs, by all means go for it.

Conclusion

This chapter discussed many aspects of device state handling in Asterisk. We started by discussing the core concepts of device states and extension states, and built up from there. We covered how SIP phones can subscribe to states, tools for creating custom states, and two mechanisms that can be used for distributing states among many servers. Finally, we covered one of the features in Asterisk, Shared Line Appearances, that relies heavily on the device state infrastructure in Asterisk to operate.



[131] Some also like to call these “blinky lamps” or “blinky lights” for their phones. Geeks and their LEDs…

[132] Admittedly, none of the configuration for SLA is simple.

[133] Read: a hack.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset