Asterisk Calendaring (For the Technically Minded)

In this post, I hope to share information that I could not find elsewhere when I began trying to set up Asterisk calendaring.  If you are using PBX in a Flash / Incredible PBX, be sure and check out http://nerdvittles.com/?p=745 as a lot of the work has been done for you.  If you aren’t sure what all of this is about, perhaps you need to read the post Asterisk Calendaring (For the Non-Technical)

I’m using PBX In a Flash Purple version 1.7.9

I’m using Google Apps Calendar.  I created new calendars for holiday, closed, specialopen, and dnd.  I made them public calendars with all details available.  I ran into problems when I tried to use the free/busy only option as well as trying to use the authenticated option.  The *only* ical option that worked for me was the public ical location (found under calendar settings and the main settings for each calendar).

I created my own /etc/asterisk/calendar.conf file (urls/extensions/filenames have been munged – please use your own stuff and make sure to correct from line-wrap)

[mpwita-holidays]

type = ical

url = http://www.google.com/calendar/ical/mpwita.com_rk41o27s37kikgnkf0%40group.calendar.google.com/public/basic.ics

refresh = 5

timeframe = 3600

autoreminder = 1

channel =  Local/7999@from-internal/n

context = mpwita-holiday-greeting-notify

extension = s

[mpwita-open]

type = ical

url = http://www.google.com/calendar/ical/mpwita.com_ebjf2bftht3tkd378%40group.calendar.google.com/public/basic.ics

refresh = 5

timeframe = 3600

autoreminder=1

channel = Local/7999@from-internal/n

context = mpwita-closed-greeting-notify

extension = s

[mpwita-busy]

type = ical

url = http://www.google.com/calendar/ical/mpwita.com_e065dmp9r485%40group.calendar.google.com/public/basic.ics

refresh = 5

timeframe = 3600

autoreminder=1

channel = Local/7999@from-internal/n

context = mpwita-busy-greeting-notify

extension = s

[mpwita-specialopen]

type = ical

url = http://www.google.com/calendar/ical/mpwita.com_suufghjl0%40group.calendar.google.com/public/basic.ics

refresh = 5

timeframe = 3600

autoreminder=1

channel = Local/7999@from-internal/n

context = mpwita-specialopen-greeting-notify

extension = s

 

After setting this file up, going into the CLI, doing a ‘reload’ the original documentation shows that typing the command ‘calendar show calendars’ should give you output similar to below.

*CLI> calendar show calendars

Calendar             Type       Status

——–             —-       ——

mpwita-holidays      ical       free

mpwita-specialopen   ical       free

mpwita-open          ical       free

mpwita-busy          ical       free

Mine did not.  My calendars did not show up. I did much research and found several articles that explained that calendaring support was not compiled into Asterisk 1.8 on my PBX In a Flash machine.

http://highsecurity.blogspot.com/2010/12/asterisk-18-calendaring-with-text-to.html

http://www.voiptoday.org/index.php?option=com_content&view=article&id=543:asterisk-18-using-the-new-calendar-functions

https://wiki.asterisk.org/wiki/display/AST/Configuring+Asterisk+Calendaring

These articles are useful references and resources when deciding what to do with Asterisk Calendaring, but just could not get anything to work with what information they provided in reference to setup.  If I had hair left, I would have pulled it all out trying to figure out what to do from the above references.  I finally discovered a post on the PBX in a Flash forums.  THANK YOU THANK YOU THANK YOU!!!!!!!

http://pbxinaflash.com/forum/showthread.php?t=8908

I ran the following steps, exactly as suggested, and upon doing a reload the command ‘calendar show calendars’ had information as listed above.  Here are the steps to compile that I used:

amportal stop

cd /usr/src

wget http://downloads.sourceforge.net/freeassociation/libical-0.44.tar.gz

tar -xf libical-0.44.tar.gz

cd libical-0.44

./configure –prefix=/usr

make

make install

cd /usr/src

wget http://www.webdav.org/neon/neon-0.29.5.tar.gz

tar -xf neon-0.29.5.tar.gz

cd neon-0.29.5

./configure –prefix=/usr –with-ssl

make

make install

cd ../asterisk

make clean

./configure

make

make install

amportal start

Next on the list was to make sure that items that are on your calendar.  doing a ‘calendar show calendar calendarname’ will show all of the items that have been retrieved. Keep in mind after initial reload that it takes the initial refresh time before the calendar function will download your calendar items, and only those items that start within the “timeframe” seconds.

*CLI> calendar show calendar mpwita-open

Name              : mpwita-open

Notify channel    : Local/7999@from-inte

Notify context    : mpwita-closed-greeti

Notify extension  : s

Notify applicatio :

Notify appdata    :

Refresh time      : 5

Timeframe         : 3600

Autoreminder      : 1

Events

——

Summary     : Closed

Description :

Organizer   :

Location    :

Cartegories :

Priority    : 0

UID         : vu1p08sc1f20lfmhc8mks@google.com

Start       : 2011-02-28 05:05:00 PM -0600

End         : 2011-03-01 07:55:00 AM -0600

Alarm       :

Summary     : Closed

Description :

Organizer   :

Location    :

Cartegories :

Priority    : 0

UID         : vu1p08sc1f20lfmhc8mks@google.com

Start       : 2011-03-01 05:05:00 PM -0600

End         : 2011-03-02 07:55:00 AM -0600

Alarm       :

Summary     : Closed

Description :

Organizer   :

Location    :

Cartegories :

Priority    : 0

UID         : vu1p08sc1f20lfmhc8mks@google.com

Start       : 2011-03-02 05:05:00 PM -0600

End         : 2011-03-03 07:55:00 AM -0600

Alarm       :

Once you get this far, you can start to rely on the other reference materials to see what you can do with it.  I won’t go into details here, but I have purchased a license for one of the voices from www.cepstral.com and installed it.  (http://nerdvittles.com/?m=20080128 describes the basic process of installing on a previous version of Asterisk) The application ‘swift’ works very similarly to ‘flite’ for doing text to speech, but has a MUCH higher quality voice.  to make use of this, I added some entries to /etc/asterisk/extensions_custom.conf.  I realize that there may be some security concerns for this macro, but those security concerns involve having multiple users on the same system/etc.  If you have suggestions on how to improve what I’ve done below, PLEASE DO comment.

Added to /etc/asterisk/extensions_custom.conf

[custom-businessopen]

exten => s,1,Set(MPWITAHOLIDAY=${CALENDAR_BUSY(mpwita-holidays)})

exten => s,n,Set(MPWITACLOSED=${CALENDAR_BUSY(mpwita-open)})

exten => s,n,Set(MPWITASOPEN=${CALENDAR_BUSY(mpwita-specialopen)})

exten => s,n,GotoIf($[“${MPWITASOPEN}” = “1”]?Sopen,1)

exten => s,n,GotoIf($[“${MPWITAHOLIDAY}” = “1”]?Holiday,1)

exten => s,n,GotoIf($[“${MPWITACLOSED}” = “1”]?Closed,1)

exten => s,n,Goto(custom-mpwitadnd,Main,1)

exten => Sopen,1,MacroExclusive(mpwita-specialopen)

exten => Sopen,n,Playback(custom/mpwita-specialopen)

exten => Sopen,n,Goto(custom-mpwitadnd,Main,1)

exten => Holiday,1,MacroExclusive(mpwita-holiday)

exten => Holiday,n,Goto(app-announcement-22,s,1)

exten => Closed,1,MacroExclusive(mpwita-closed)

exten => Closed,n,Goto(app-announcement-21,s,1)

[macro-mpwitadnd]

exten => s,1,Set(MPWITADND=${CALENDAR_BUSY(mpwita-busy)})

exten => s,2,Noop(If Free, goto “${MPWITAIFFREE}”, if Busy goto “${MPWITAIFBUSY}”.  Currently busy state is “${MPWITADND}”)

exten => s,3,GotoIf($[“${MPWITADND}” = “1”]?4:8)

exten => s,4,GotoIf($[“${skipdndannounce}” = “1”]?7)

exten => s,5,MacroExclusive(mpwita-busy)

exten => s,6,Playback(custom/mpwita-busy)

exten => s,7,Goto(${MPWITAIFBUSY})

exten => s,8,Goto(${MPWITAIFFREE})

[custom-mpwitadnd]

exten => EmergN,1,Set(MPWITAIFBUSY=ext-local,vmu7904,1)

exten => EmergN,n,Set(MPWITAIFFREE=app-announcement-23,s,1)

exten => EmergN,n,Set(skipdndannounce=0)

exten => EmergN,n,Goto(MPWITAalldone,1)

exten => EmergD,1,Set(MPWITAIFBUSY=ext-queues,7981,1)

exten => EmergD,n,Set(MPWITAIFFREE=ext-queues,7980,1)

exten => EmergD,n,Set(skipdndannounce=1)

exten => EmergD,n,Goto(MPWITAalldone,1)

exten => GenSpt,1,Set(MPWITAIFBUSY=ext-local,vmu7903,1)

exten => GenSpt,n,Set(MPWITAIFFREE=ext-queues,7978,1)

exten => GenSpt,n,Set(skipdndannounce=0)

exten => GenSpt,n,Goto(MPWITAalldone,1)

exten => Admin,1,Set(MPWITAIFBUSY=ext-local,vmu7901,1)

exten => Admin,n,Set(MPWITAIFFREE=ext-queues,7976,1)

exten => Admin,n,Set(skipdndannounce=0)

exten => Admin,n,Goto(MPWITAalldone,1)

exten => Oper,1,Set(MPWITAIFBUSY=ext-local,vmu7901,1)

exten => Oper,n,Set(MPWITAIFFREE=ext-queues,7975,1)

exten => Oper,n,Set(skipdndannounce=0)

exten => Oper,n,Goto(MPWITAalldone,1)

exten => Main,1,Set(MPWITAIFBUSY=ivr-21,s,1)

exten => Main,n,Set(MPWITAIFFREE=ext-group,7956,1)

exten => Main,n,Set(skipdndannounce=1)

exten => Main,n,Goto(MPWITAalldone,1)

exten => MPWITAalldone,1,Macro(mpwitadnd)

[mpwita-holiday-greeting-notify]

exten => s,1,Wait(60)

exten => s,n,Answer

exten => s,n,MacroExclusive(mpwita-holiday)

exten => s,n,Playback(custom/mpwita-holiday)

[macro-mpwita-holiday]

exten => s,1,Set(mpwita-calendar=mpwita-holidays)

exten => s,n,Macro(calendarinfo,0)

exten => s,n,GotoIf($[${AlreadyDone} = 1]?10)

exten => s,n,System(swift “We are closed until ${cal-end-date} for the ${cal-summary} holiday.  Emergency support is still available.” -o /var/lib/asterisk/sounds/custom/mpwita-holiday.wav -p audio/sampling-rate=8000)

exten => s,10,MacroExit

[mpwita-closed-greeting-notify]

exten => s,1,Wait(60)

exten => s,n,Answer

exten => s,n,MacroExclusive(mpwita-closed)

exten => s,n,Playback(custom/mpwita-afterhours)

[macro-mpwita-closed]

exten => s,1,Set(mpwita-calendar=mpwita-open)

exten => s,n,Macro(calendarinfo,300)

exten => s,n,GotoIf($[${AlreadyDone} = 1]?10)

exten => s,n,System(swift “It is presently outside normal business hours.  Emergency support is still available.  We will re-open at ${cal-end-time} on ${cal-end-date}.” -o  /var/lib/asterisk/sounds/custom/mpwita-afterhours.wav -p audio/sampling-rate=8000)

exten => s,10,MacroExit

[mpwita-busy-greeting-notify]

exten => s,1,Wait(60)

exten => s,n,Answer

exten => s,n,MacroExclusive(mpwita-busy)

exten => s,n,Playback(custom/mpwita-busy)

[macro-mpwita-busy]

exten => s,1,Set(mpwita-calendar=mpwita-busy)

exten => s,n,Macro(calendarinfo,0)

exten => s,n,GotoIf($[${AlreadyDone} = 1]?10)

exten => s,n,System(swift “We currently have limited access to telephone calls. We expect to be more readily available after ${cal-end-time}.  we will attempt to respond to only emergencies during this brief period.  Thank you for your understanding.” -o  /var/lib/asterisk/sounds/custom/mpwita-busy.wav -p audio/sampling-rate=8000)

exten => s,10,MacroExit

[mpwita-specialopen-greeting-notify]

exten => s,1,Wait(60)

exten => s,n,Answer

exten => s,n,MacroExclusive(mpwita-specialopen)

exten => s,n,Playback(custom/mpwita-specialopen)

[macro-mpwita-specialopen]

exten => s,1,Set(mpwita-calendar=mpwita-specialopen)

exten => s,n,Macro(calendarinfo,0)

exten => s,n,GotoIf($[${AlreadyDone} = 1]?10)

exten => s,n,System(swift “We are presently open special hours for ${cal-summary} through ${cal-end-time} ${cal-end-date}” -o /var/lib/asterisk/sounds/custom/mpwita-specialopen.wav -p audio/sampling-rate=8000)

exten => s,10,MacroExit

[macro-calendarinfo]

;set mpwita-calendar to calendar to check ${ARG1} is number of seconds to add to end time

;returns populates cal-summary, cal-end-time, cal-end-date if AlreadyDone=0

;if marker file exists for current event, return AlreadyDone=1

;needs to be run with MacroExclusive() to prevent possible race condition

exten => s,1,Set(i=${MATH(${EPOCH}+3600,int)}) ; one hour

exten => s,n,Set(id=${CALENDAR_QUERY(${mpwita-calendar},${EPOCH},${i})})

exten => s,n,Set(cal-start-epoch=${CALENDAR_QUERY_RESULT(${id},start)})

exten => s,n,GotoIf($[${STAT(M,/tmp/${mpwita-calendar}-recording)} > ${cal-start-epoch}]?30)

exten => s,n,Set(cal-summary=${CALENDAR_QUERY_RESULT(${id},summary)})

exten => s,n,Set(cal-end=${MATH(${CALENDAR_QUERY_RESULT(${id},end)}+${ARG1},int)})

exten => s,n,Set(cal-end-date=${STRFTIME(${cal-end},,%A%t %B%t %e%t %G%t)})

exten => s,n,NoOp(${STRFTIME(${cal-end},,%M)})

exten => s,n,GotoIf($[“${STRFTIME(${cal-end},,%M)}” = “00”]?20:18)

exten => s,18,Set(cal-end-time=${STRFTIME(${cal-end},,%l%t %M%t %p%t %Z%t)})

exten => s,n,Goto(s,21)

exten => s,20,Set(cal-end-time=${STRFTIME(${cal-end},,%l%t o clock %p%t %Z%t)})

exten => s,21,NoOp(end of ${cal-summary} is at ${cal-end-time} on ${cal-end-date}

exten => s,n,System(echo “${cal-summary} at ${cal-end-time} on ${cal-end-date}” > /tmp/${mpwita-calendar}-recording)

exten => s,n,Set(AlreadyDone=0)

exten => s,n,Goto(s,31)

exten => s,30,Set(AlreadyDone=1)

exten => s,31,MacroExit

To make use of the dialplan above,  you will need to set up a “Custom Destination”.  Once you configure a Custom Destination, you can use them like any other target for incoming call flow.  Instead of having an opening announcement go to an IVR, you can have the opening announcement go to a Custom Destination.  The custom destination can determine where to go next and end up at the IVR when appropriate.

My opening announcement on incoming calls custom-businessopen,s,1.  from the day and night ivrs, the various options go to custom-mpwitadnd,(quename),1 except for the night/holiday options for general support and administrative which always just go directly to voicemail.  I have created custom destinations for each, and the IVR points to the Custom Destination now instead of the original queue or other destination.

Example of custom destination used for item 1

I did find another issue/bug/quirk:

One other thing I have been noticing and as of yet have no explanation for.  The calendar autoreminder seems to only work occasionally and could not depend on it to run in order to update my messages.  This is why on the code below, I have the system to check a marker file for the calendar.  If the calendar marker file is as new or newer than the start of the calendar event, then it skips regenerating the .wav file.  If not, the wav file is generated with swift and then the wav file is played.  This process can introduce a 1-4 second delay for the first caller to hear the message.

I have also noticed that if you make a recurring reminder, then modify one of the events, the original and modified calendar event stay in asterisk while they are no longer on google calendar

this one  shows up which is correct

——

Summary     : Closed

Description :

Organizer   :

Location    :

Cartegories :

Priority    : 0

UID         : u1p08sc1f20lfmhc8mks@google.com

Start       : 2011-03-01 10:15:00 PM -0600

End         : 2011-03-02 07:55:00 AM -0600

Alarm       :

Summary     : Closed

Description :

Organizer   :

Location    :

Cartegories :

Priority    : 0

UID         : vu1p08sc1f20lfmhc8mks@google.com

Start       : 2011-03-01 05:05:00 PM -0600

End         : 2011-03-02 07:55:00 AM -0600

Alarm       :

the second one is not in the calendar, but is the original non-edited recurring appointment.  CALENDAR_BUSY stays busy for this calendar even though there is nothing on the Google calendar.  Be aware that you may run into this and other oddities during the course of your project.

Before starting on this, I wouldn’t have dreamed I would be someone providing the documentation.  I got tips/tricks/pointers from the references mentioned above, but didn’t find anything out there that did what I wanted, so I started writing this from scratch. I hope that you may find this useful and can help contribute back to those who have given contributions to this project.  If it is a bit overwhelming, I understand, and feel that I have developed a pretty good understanding of how all this works.  If this is something that I can help you with setting up for you/yourself/your organization, please contact me.


Share