Asterisk can be integrated with several different kinds of calendar formats, such as iCal, CalDAV, MS Exchange (Exchange 2003), and MS Exchange Web Services (Exchange 2007 and later). Integrating Asterisk with your calendar gives you the ability to manipulate call routing based on your current calendar information. For example, if you’re not going to be in your office for the afternoon it may make sense for people ringing your desk phone to be routed directly to your voicemail.
Another advantage to calendar integration is the ability to originate calls based on calendar information. For example, if you set up a meeting on your conference server, you can arrange to have a reminder call five minutes before the meeting starts, which then places you into the conference room. We think this type of flexibility and integration is pretty nifty and quite useful.
As there are several modules for calendaring support (allowing us to provide support for different backends, such as MS Exchange, CalDAV, iCal, etc.), you’ll need to install the dependencies for the backends you want to support. This modularized setup has the advantage that you only need to install dependencies for the modules you need; also other backends can easily be integrated with the primary calendaring backend in the future.
Because of the different
dependencies of each module, we need to check menuselect for what needs to be installed for
each of the calendaring modules we wish to support. All modules require
the neon
development library,
available from http://www.webdav.org/neon/.
res_calendar_ews
(Exchange Web
Services) requires version 0.29 or later, which means some distributions
will require you to compile the neon
library from source instead of using the precompiled package available
from the distribution.
While the configuration for all the calendaring modules is similar, we’ll be discussing CalDAV integration specifically since it is widely supported by a range of calendar software and servers.[155]
Since all the modules require the neon
library, we’ll install that first. Be
sure to append .x86_64 to the end of the package
name if installing on a 64-bit machine:
$
sudo yum install neon-devel
If you are planning on
compiling the res_calendar_ews
module, you will need to have a version of neon
greater than or equal to 0.29.
Currently CentOS is shipping with 0.25, so you will have to
compile the neon
library and
link to it from the configure
script. This can be done via ./configure
--with-neon29=<path to
neon>
.
The next step is to install the libical-devel
dependency. Unfortunately,
this module is not shipped with CentOS and requires a third-party
repository (see Third-Party Repositories). In this case,
we need to install libical-devel
from the EPEL (Extra Packages for Enterprise Linux) repository:
$
sudo yum --enablerepo=epel install libical-devel
After
installing our dependencies, we can run the configure script in our Asterisk source
directory and enable both the res_calendar
and res_calendar_caldav
modules from within the
Resource Modules section of menuselect.
Because all the modules require the neon
development library, we’re going to
install that first. On Ubuntu, you will typically be given several
different versions (e.g., on 10.04 we have the option of
libneon 2.5, 2.6, and 2.7). We’re going to
install the latest version available to us:
$
sudo apt-get install libneon27-dev
If you are planning on
compiling the res_calendar_ews
module, you will need to have neon
0.29 or greater. Currently Ubuntu
is shipping with 0.27, so you will have to compile the neon
library and link to it from the
configure script. This can be
done via ./configure --with-neon29=<path
to neon>
.
With libneon installed, we can now
install the libical-dev
package
and its dependencies with apt-get:
$
sudo apt-get install libical-dev
After
installing our dependencies, we can run the configure script in our Asterisk source
directory and enable both the res_calendar
and res_calendar_caldav
modules from within the
Resource Modules section of menuselect.
In this section we’re going to discuss how to connect your Asterisk system to a Google calendar. We’re using calendars from Google for the simple reason that they don’t require any other configuration (such as setting up a calendaring server), which gets us up and running far quicker. Of course, once you’re comfortable with configuring calendaring support in Asterisk, you can connect it to any calendaring server you desire.
The first step is to make sure you
have a Gmail (http://www.gmail.com) account with
Google, which will get you access to a calendaring server. Once you’ve
logged into your Gmail account, there should be a link to your calendar
in the upper-left corner. Click on the Calendar
link and insert a couple of items for the next hour or two. When we
configure our calendar.conf
file
we’ll be instructing Asterisk to check for new events every 15 minutes,
and pulling in 60 minutes’ worth of data.
Be sure to verify the time on your server. If the time is not in sync with the rest of the world—e.g., if is not updated via the Network Time Protocol (NTP)—your events may not show, or may show at the wrong times. This tip is the result of running into this very issue while testing and documenting. : )
The next step is to configure our
calendar.conf
file for polling our
calendar server.
The calendar.conf.sample
file has several
examples for calendaring servers, such as those supplied by Microsoft
Exchange–, iCal-, and CalDAV-based calendar servers.
The following configuration will connect to the Google calendaring server and poll for new events every 15 minutes, retrieving 60 minutes’ worth of data. Feel free to change these settings as necessary, but be aware that pulling more data (especially if you have multiple calendars for people in your company) will utilize more memory:
$
cat >> calendar.conf [myGoogleCal] type=caldav url=https://www.google.com/calendar/dav/
Ctrl+D<Gmail Email Address>
/events/ user=<Gmail Email Address>
secret=<Gmail Password>
refresh=15 timeframe=60
With your calendar.conf
file configured, let’s load the
calendaring modules into Asterisk.
First we’ll load the res_calendar.so
module into memory, then
we’ll follow it up by doing a module
reload, which will load the sister modules (such as res_calendar_caldav.so
) correctly.[156]
$
asterisk -r
*CLI>
module load res_calendar.so
*CLI>
module reload
After loading the modules we can check to make sure our calendar has connected to the server and been loaded into memory correctly, by executing calendar show calendars:
*CLI>
calendar show calendars
Calendar Type Status -------- ---- ------ myGoogleCal caldav busy
Our status is currently set to
busy
(which doesn’t have any bearing
on our dialplan at the moment, but simply means we have an event that
has marked us as busy in the calendar), and we can see the currently
loaded events for our time range by running calendar show calendar
<myGoogleCal> from the Asterisk console:
*CLI>
calendar show calendar myGoogleCal
Name : myGoogleCal Notify channel : Notify context : Notify extension : Notify applicatio : Notify appdata : Refresh time : 15 Timeframe : 60 Autoreminder : 0 Events ------ Summary : Awesome Call With Russell Description : Organizer : Location : Cartegories : Priority : 0 UID : hlfhcpi0j360j8fteop49cvk68@google.com Start : 2010-09-28 08:30:00 AM -0400 End : 2010-09-28 09:00:00 AM -0400 Alarm : 2010-09-28 04:20:00 AM -0400
The first field in the top section is the Name of our calendar. Following that are several Notify fields, which are used to dial a destination upon the start of a meeting, that we’ll discuss in more detail shortly. The Refresh time and Timeframe fields are the values we configured for how often to check for new events and how long of a range we should look at for data, respectively. The Autoreminder field controls how long prior to an event we should execute the Notify options.
If you have not configured any of
the Notify options but have an alarm set to
remind you in the calendar, you may get a WARNING
message such as:
WARNING[5196]: res_calendar.c:648 do_notify: Channel should be in form Tech/Dest (was '')
The reason for the warning is that an alarm was set for notification about the start of the meeting, but Asterisk was unable to generate a call due to values not being configured to place the call. This warning message can be safely ignored if you don’t plan on placing calls for event notifications.
The rest of the screen output is a listing of events available within our Timeframe, along with information about the events. The next steps are to look at some dialplan examples of what you can do now that you have your calendaring information in Asterisk, and to configure dialing notifications to remind you about upcoming meetings.
In this section we’ll discuss how to configure the
calendar.conf
file to execute some
simple dialplan that will call your phone prior to a calendar event.
While the dialplan we’ll provide might not be ready for production, it
certainly gives you a good taste of the possibilities that exist for
triggering calls based on calendar state.
In our first example, we’re going to call a device and
play back a reminder notice for a particular calendar event. It might
be useful to get this type of reminder if you’re likely to be napping
at your desk when your weekly Monday meeting rolls around. To set up a
wakeup call reminder, we simply need to add the following lines to our
calendar configuration in calendar.conf
:
channel=SIP/0000FFFF0001 app=Playback appdata=this-is-yr-wakeup-call
In your calendar, you need to make sure the event you’re adding has an alarm or reminder associated with it. Otherwise, Asterisk won’t try to generate a call.
After making this change, reload
the res_calendar.so
module from
the Asterisk console:
*CLI>
module reload res_calendar.so
When the event rolls around, Asterisk
will generate a call to you and play back the sound file this-is-yr-wakeup-call
. The output on the
console would look like this:
-- Dialing SIP/0000FFFF0001 for notification on calendar myGoogleCal == Using SIP RTP CoS mark 5 -- Called 0000FFFF0001 -- SIP/0000FFFF0001-00000001 is ringing -- SIP/0000FFFF0001-00000001 connected line has changed, passing it to Calendar/myGoogleCal-5fd3c52 -- SIP/0000FFFF0001-00000001 answered Calendar/myGoogleCal-5fd3c52 -- <SIP/0000FFFF0001-00000001> Playing 'this-is-yr-wakeup-call.ulaw' (language 'en')
If you modify the calendar
event so it’s just a couple of minutes in the future, you can
trigger the events quickly by unloading and then loading the
res_calendar_caldav.so
module
from the Asterisk console. By doing that, you’ll trigger Asterisk
to generate the call immediately.
Remember that our refresh rate is set to 15 minutes and we’re gathering 60 minutes’ worth of events. You might have to adjust these numbers if you wish to test this out on your development server.
In this example we’re going to show how you can use a
combination of some simple dialplan and the CALENDAR_EVENT()
dialplan function
to generate a call between two participants based on the
information in the location field. We’re going to fill in the location
field with 0000FFFF0002
, which is
the SIP device we wish to call after answering our reminder.
We haven’t specified SIP/0000FFFF0002
directly in the calendar
event because we want to be a bit more secure with what we accept.
Because we’re going to filter out anything but alphanumeric
characters, we won’t be able to accept a forward slash as the
separator between the technology and the location (e.g., SIP/0000FFFF0001
). We could certainly
allow this, but then we would run the risk of people making
expensive outbound calls, especially if a user opens his calendar
publicly or is compromised. With the method we’re going to employ,
we simply limit our risk.
Add the following dialplan to
your extensions.conf
file:
[AutomatedMeetingSetup] exten => start,1,Verbose(2,Triggering meeting setup for two participants) same => n,Set(DeviceToDial=${FILTER(0-9A-Za-z,${CALENDAR_EVENT(location)})}) same => n,Dial(SIP/${DeviceToDial},30) same => n,Hangup()
When the event time arrives, our device will receive a call, and when that call is answered another call will be placed to the endpoint with which we wish to have our meeting. The console output looks like the following:
This is where our calendar triggers a call to our device -- Dialing SIP/0000FFFF0001 for notification on calendar myGoogleCal == Using SIP RTP CoS mark 5 -- Called 0000FFFF0001 -- SIP/0000FFFF0001-00000004 is ringing And now we have answered the call from Asterisk triggered by an event -- SIP/0000FFFF0001-00000004 connected line has changed, passing it to Calendar/myGoogleCal-347ec99 -- SIP/0000FFFF0001-00000004 answered Calendar/myGoogleCal-347ec99 Upon answer, we trigger some dialplan that looks up the endpoint to call -- Executing [start@AutomatedMeetingSetup:1] Verbose("SIP/0000FFFF0001-00000004", "2,Triggering meeting setup for two participants") in new stack == Triggering meeting setup for two participants This is where we used CALENDAR_EVENT(location) to get the remote device -- Executing [start@AutomatedMeetingSetup:2] Set("SIP/0000FFFF0001-00000004", "DeviceToDial=0000FFFF0002") in new stack And now we're dialing that endpoint -- Executing [start@AutomatedMeetingSetup:3] Dial("SIP/0000FFFF0001-00000004", "SIP/0000FFFF0002,30") in new stack == Using SIP RTP CoS mark 5 -- Called 0000FFFF0002 -- SIP/0000FFFF0002-00000005 is ringing The other end answered the call, and Asterisk bridged us together -- SIP/0000FFFF0002-00000005 answered SIP/0000FFFF0001-00000004 -- Locally bridging SIP/0000FFFF0001-00000004 and SIP/0000FFFF0002-00000005
Of course, the dialplan could be expanded to prompt the initial
caller to acknowledge being ready for the meeting prior to calling the
other party. Likewise, we could add some dialplan that plays a prompt
to the other caller that lets her know that she has scheduled a
meeting and that if she presses 1
she will be connected with the other party immediately. We could even
have created a dialplan that would allow the original party to record
a message to be played back to the other caller.
Just for fun, we’ll show you an example of the functionality we just described. Feel free to modify it to your heart’s content:
[AutomatedMeetingSetup] exten => start,1,Verbose(2,Triggering meeting setup for two participants) ; *** This line should not have any line breaks same => n,Read(CheckMeetingAcceptance,to-confirm-wakeup&press-1&otherwise &press-2,,1) same => n,GotoIf($["${CheckMeetingAcceptance}" != "1"]?hangup,1) same => n,Playback(silence/1&pls-rcrd-name-at-tone&and-prs-pound-whn-finished) ; We set a random number and assign it to the end of the recording ; so that we have a unique filename in case this is used by multiple ; people at the same time. ; ; We also prefix it with a double underscore because the channel ; variable also needs to be available to the channel we're going to call ; same => n,Set(__RandomNumber=${RAND()}) same => n,Record(/tmp/meeting-invite-${RandomNumber}.ulaw) same => n,Set(DeviceToDial=${FILTER(0-9A-Za-z,${CALENDAR_EVENT(location)})}) same => n,Dial(SIP/${DeviceToDial},30,M(CheckConfirm)) same => n,Hangup() exten => hangup,1,Verbose(2,Call was rejected) same => n,Playback(vm-goodbye) same => n,Hangup() [macro-CheckConfirm] exten => s,1,Verbose(2,Allowing called party to accept or reject) same => n,Playback(/tmp/meeting-invite-${RandomNumber}) ; *** This line should not have any line breaks same => n,Read(CheckMeetingAcceptance,to-confirm-wakeup&press-1&otherwise &press-2,,1) same => n,GotoIf($["${CheckMeetingAcceptance}" != "1"]?hangup,1) exten => hangup,1,Verbose(2,Call was rejected by called party) same => n,Playback(vm-goodbye) same => n,Hangup()
We hope you’ll be able to use this simple dialplan example as a jumping-off point. With a little creativity and some dialplan skills, the possibilities are endless!
To expand upon the functionality in the previous section, we’re going to delve into the logic problem of how you might be able to place multiple participants into a meeting. Our goal is to use our calendar to call us when the meeting is scheduled to start, and then, when we answer, to place calls to all the other members of the conference. As the other participants answer their phones, they will be placed into a virtual conference room, where they will wait for the meeting organizer to join. After all participants have been dialed and answered (or perhaps not answered), the organizer will be placed into the call, at which point the meeting will start.
This type of functionality increases the likelihood that the meeting will start on time, and it means the meeting organizer doesn’t have to continually perform roll call as new participants continue to join after the call is supposed to start (which invariably happens, with people’s schedules typically being fairly busy).
The dialplan we’re going to show
you isn’t necessarily a polished, production-ready installation (for
example, the data returned from the calendar comes from the
description field, only deals with device names, and assumes the
technology is SIP). However, we’ve done the hard work for you by
developing the Local channel usage, along with the M()
flag (macro) usage with Dial()
. With some testing and tweaks this
code could certainly be developed more fully for your particular
installation, but we’ve kept it general to allow for it to be usable
for more people in more situations. The example dialplan looks like
this:
[AutomatedMeetingSetup] exten => start,1,Verbose(2,Calling multiple people and placing into a conference) ; Get information from calendar and save that information. Prefix ; CalLocation with an underscore so it is available to the Local ; channel (variable inheritance). ; same => n,Set(CalDescription=${CALENDAR_EVENT(description)}) same => n,Set(_CalLocation=${CALENDAR_EVENT(location)}) same => n,Set(X=1) ; Our separator is a caret (^), so the description should be in the ; format of: 0000FFFF0001^0000FFFF0002^etc... ; same => n,Set(EndPoint=${CUT(CalDescription,^,${X})}) ; This loop is used to build the ${ToDial} variable, which contains ; a list of Local channels to be dialed, thereby triggering the multiple ; Originate() actions simultaneously instead of linearly ; same => n,While($[${EXISTS(${EndPoint})}]) ; This statement must be on a single line same => n,Set(ToDial=${IF($[${ISNULL(${ToDial})}]? :${ToDial}&)}Local/${EndPoint}@MeetingOriginator) same => n,Set(X=$[${X} + 1]) same => n,Set(EndPoint=${CUT(CalDescription,^,${X})}) same => n,EndWhile() ; If no values are passed back, then don't bother dialing same => n,GotoIf($[${ISNULL(${ToDial})}]?hangup) same => n,Dial(${ToDial}) ; After our Dial() statement returns, we should be placed into ; the conference room. We are marked, so the conference can start ; (which is indicated by the 'A' flag to MeetMe). ; same => n,MeetMe(${CalLocation},dA) same => n(hangup),Hangup() [MeetingOriginator] exten => _[A-Za-z0-9].,1,NoOp() same => n,Set(Peer=${FILTER(A-Za-z0-9,${EXTEN})}) ; Originate calls to a peer as passed to us from the Local channel. Upon ; answer, the called party should execute the dialplan located at the ; _meetme-XXXX extension, where XXXX is the conference room number. ; same => n,Originate(SIP/${Peer},exten,MeetingOriginator,meetme-${CalLocation},1) same => n,Hangup() ; Join the meeting; using the 'w' flag, which means 'wait for marked ; user to join before starting' ; exten => _meetme-XXXX,1,Verbose(2,Joining a meeting) same => n,Answer() same => n,MeetMe(${EXTEN:7},dw) same => n,Hangup()
Sometimes it is useful to redirect calls automatically—for
example, when you’re in a meeting, or on vacation. In this section we’ll
be making use of the CALENDAR_BUSY()
dialplan function, which allows us to check the current status of our
calendar to determine if we’re busy or not. A simple example of this
would be to send all calls to voicemail using the busy message whenever
an event that marks us as busy has been scheduled.
The following dialplan shows a simple example where we check our calendar for busy status prior to sending a call to a device. Notice that a lot of the information in this example is static, and additional effort would be required to make it dynamic and suitable for production:
exten => 3000,1,Verbose(2,Simple calendar busy check example) same => n,Set(CurrentExten=${EXTEN}) same => n,Set(CalendarBusy=${CALENDAR_BUSY(myGoogleCal)}) same => n,GotoIf($["${CalendarBusy}" = "1"]?voicemail,1) same => n,Dial(SIP/0000FFFF0002,30) same => n,Goto(voicemail,1) exten => voicemail,1,Verbose(2,Caller sent to voicemail) ; *** This line should not have any line breaks same => n,GotoIf($["${DIALSTATUS}" = "BUSY" | "${CalendarBusy}" = "1"]?busy:unavail) same => n(busy),VoiceMail(${CurrentExten}@shifteight,b) same => n,Hangup() same => n(unavail),VoiceMail(${CurrentExten}@shifteight,u) same => n,Hangup()
And here is a slightly more elaborate
section of dialplan that utilizes a few of the tools we’ve learned
throughout the book, including DB_EXISTS()
, GotoIf()
, and the IF()
function:
exten => _3XXX,1,Verbose(2,Simple calendar busy check example) same => n,Set(CurrentExten=${EXTEN}) same => n,GotoIf($[${DB_EXISTS(extension/${CurrentExten}/device)}]?:no_device,1) same => n,Set(CurrentDevice=${DB_RESULT}) same => n,GotoIf($[${DB_EXISTS(extension/${CurrentExten}/calendar)}]?:no_calendar) same => n,Set(CalendarBusy=${CALENDAR_BUSY(${DB_RESULT})}) same => n,GotoIf($[${CalendarBusy}]?voicemail,1) same => n(no_calendar),Verbose(2,No calendar was found for this user) same => n,Dial(SIP/${CurrentDevice},30) same => n,Goto(voicemail,1) exten => voicemail,1,Verbose(2,Sending caller to voicemail) ; *** This line should not have any line breaks same => n,GotoIf($[${DB_EXISTS(extension/${CurrentExten}/voicemail_context)}] ?:no_voicemail) same => n,Set(VoiceMailContext=${DB_RESULT}) ; *** This line should not have any line breaks same => n,Set(VoiceMailStatus=${IF($["${DIALSTATUS}" = "BUSY" | 0${CalendarBusy}]?b:u)}) same => n,VoiceMail(${CurrentExten}@${VoiceMailContext},${VoiceMailStatus}) same => n,Hangup() same => n(no_voicemail),Playback(number-not-answering) same => n,Hangup() exten => no_device,1,Verbose(2,No device found in the DB) same => n,Playback(invalid) same => n,Hangup()
Using the CALENDAR_WRITE()
function opens some other
possibilities in terms of calendar integration. From the Asterisk
dialplan, we can insert information into a calendar, which can be
consumed by other devices and applications. Our next example is a
calendar that tracks call logs. For anyone who may be on the phone a
fair amount who needs to track time for clients, writing all calls to a
calendar for a visual reference can be useful when verifying things at
the end of the day.
We’re going to utilize the Google
web calendar again for this example, but we’re going to create a new,
separate calendar just for tracking calls. In order to write to the
calendar, we’ll need to set up our calendar.conf
file a little bit differently, by using the CalDAV
calendar format. First, though, we need to create our new
calendar.
On the left side of the Google calendar interface will be a link labeled Add. Clicking on this will open a new window where you can create the calendar. Go ahead and do that now. We’ve called ours “Phone Calls.”
Now we need to enable CalDAV calendar syncing for our calendar. Information about how to do this is located at http://www.google.com/support/mobile/bin/answer.py?answer=151674. This page notes that only your primary calendar will be synced to the device, but we want to make sure our calls are logged to a separate calendar so we can easily hide them (and so our smartphone doesn’t synchronize the phone’s calls either, which may cause confusion). There are two links near the bottom of the page: one for regular Google calendar users, and the other for Google Apps users. Select the appropriate link and open it. You will then be presented with a page that contains your calendars. Select the Phone Calls calendar and then select Save.
Next up is configuring our
calendar.conf
file for Asterisk.
One of the parameters we need is the link to the CalDAV calendar. There
is a Calendar ID value that we need that will identify our calendar
specifically. To find the calendar ID, click the down arrow beside the
calendar name on the lefthand side of the calendar page and select
Calendar Settings. Near the bottom of the calendar
settings will be two rows that contain the icons for sharing the
calendar (XML, ICAL, HTML). Beside the first set of icons inside the
Calendar Address box will be the calendar ID. It
will look like this:
(Calendar ID: 2hfb6p5974gds924j61cmg4gfd@group.calendar.google.com)
If you’re setting this up via
Google Apps, the calendar ID will be prefixed with your domain name and
an underscore (e.g., shifteight.org_
). Make a note of this string,
as we’re going to use it next.
Open up the calendar.conf
file and add a new calendar
section. In our case we’ve called it [phone_call_calendar]
. You’ll recognize the
formatting of the calendar from earlier, so we won’t go through all the
settings here. The key setting to note is the url
parameter. The format of this parameter
is:
https://www.google.com/calendar/dav/<calendar_id>
/events/
We
need to replace the <calendar_id>
with
the calendar ID we recently made a note of. The full configuration then
ends up looking like so:
[phone_call_calendar]
type=caldav
; The URL must be on a single line
url=https://www.google.com/calendar/dav/
shifteight.org_2hfb6p5974gds924j61cmg4gfd@group.calendar.google.com/events/
user = leif@shifteight.org
secret = my_secret_password
refresh=15
timeframe=120
Now that we have our calendar configured, we need to load it
into memory, which can be done by reloading the res_calendar.so
module:
*CLI>
module reload res_calendar.so
Verify that the calendar has been loaded into memory successfully with the calendar show command:
*CLI>
calendar show calendars
Calendar Type Status -------- ---- ------ phone_call_calendar caldav free
With our calendar successfully
loaded into memory, we can write some dialplan around our Dial()
command to save our call information to
the calendar with the CALENDAR_WRITE()
function:
[LocalSets] exten => _NXXNXXXXXX,1,Verbose(2,Outbound calls) same => n,Set(CalendarStart=${EPOCH}) ; Used by CALENDAR_WRITE() same => n,Set(X=${EXTEN}) ; Used by CALENDAR_WRITE() same => n,Dial(SIP/ITSP/${EXTEN},30) same => n,NoOp(Handle standard voicemail stuff here) same => n,Hangup() exten => h,1,Verbose(2,Call cleanup) ; Everything that follows must be on a single line same => n,Set(CALENDAR_WRITE(phone_call_calendar,summary,description,start,end)= OUTBOUND: ${X},Phone call to ${X} lasted for ${CDR(billsec)} seconds., ${CalendarStart},${EPOCH})
In our dialplan we’ve created
a simple scenario where we place an outbound call through our Internet
Telephony Service Provider (ITSP), but prior to placing the call, we
save the epoch[157] to a channel variable (so we can use it later when we
write our calendar entry at the end of the call). After our call, we
write our calendar entry to the phone_call_calendar
with the CALENDAR_WRITE()
dialplan function within the
built-in h
extension. There are
several options we can pass to the calendar, such as the summary,
description, and start and end times. All of this information is then
saved to the calendar.
We’ve also used the CDR()
dialplan function in our description to show the number of seconds the
answered portion of the call lasted for, so we can get a more accurate
assessment of whether a call was answered and, if so, how long the
answered portion lasted for. We could also be clever and only write to
the calendar if ${CDR(billsec)}
was
greater than 0 by wrapping the Set()
application in an ExecIf()
; e.g.,
same => n,ExecIf($[${CDR(billsec)} >
0]?Set(CALENDAR_WRITE...))
.
Many possibilities exist for the
CALENDAR_WRITE()
function; this is
just one that we’ve implemented and enjoy.
In this section we’ve learned how
to integrate Asterisk with an external calendar server such as that
provided by Google, but the concepts for attaching to other calendaring
servers remain the same. We explored how to set up a meeting between two
participants, and how to set up a multiparty conference using
information obtained from the description field in the calendar. We also
looked at how to control calls using the CALENDAR_BUSY()
function, to redirect calls to voicemail when our current event
describes us as busy. By implementing this type of functionality in
Asterisk, you can see the power we have to control call flow using
external services such as those supplied by a calendar server.
And we didn’t even get to dive
into every use of the calendar implementation—there exist other calendar
functions, such as CALENDAR_QUERY()
, which allows you to pull back a list of events within a
given time period for a particular calendar, and CALENDAR_QUERY_RESULT()
, which allows you to access the specifics of those
calendar events. Additionally, you could create functionality that
writes events into your calendar with the CALENDAR_WRITE()
function: for example, you
may wish to develop some dialplan that allows you to set aside blocks of
times in your calendar from your phone when you’re on the road without
access to your laptop. Many possibilities exist, and all it takes is a
little creativity.
[155] And because the authors of this book do not have access to Exchange servers for testing. : )
[156] As of this writing, there is a bug in the process of loading of the calendar modules after Asterisk has been started. It was filed as issue 18067 at https://issues.asterisk.org and hopefully will have been resolved by the time you read this. If not, be aware that you may need to restart Asterisk to get the modules loaded into memory correctly.
[157] In Unix, the epoch is the number of seconds that have elapsed since January 1, 1970, not counting leap seconds.