It's free software day again at Nerd Vittles, and today we're building a telephone reminder system which lets you schedule reminders for future events. And when the appointed date and time arrives, Asterisk® will swing into action and place a call to the number you designate to deliver your customized reminder message. Asterisk@Home is bundled with a wakeup call system which lets you schedule one wakeup call per extension for any time during the next 24 hours. It's a great product. However, after using a terrific, web-based email reminder system in the Linux and Mac worlds, we wanted something a bit more flexible and robust for Asterisk. For those using Asterisk@Home 2.5 or later, click here for the updated version.
Some of the smarts for this system already have been incorporated into our TeleYapper Voice Messaging System. But that was a real-time system meaning it began calling immediately after you chose a group of people to call. This system is different in that you can schedule the calls for the near or distant future, you can specify different numbers for the calls, and you can customize the recorded message for each call. In short, it's perfect for appointment reminders, birthday reminders, anniversary reminders, and anything else you want to remember. You can even schedule a call to your cellphone to remind you to pick up little Suzie at school on Friday after Band Camp. And all it takes is a phone call to set up each reminder. There's no web page to fill in and no database required to manage the reminders. You can schedule as many reminders per phone number as your little fingers care to dial! Did we mention? It's also FREE! And to all you freeloaders, yes, some of your fellow readers chipped in to pay for the Allison IVR prompts so those are included at no cost as well.
Prerequisites. As with all of our newer projects, we've tested this with ISO-installed versions of Asterisk@Home 1.5 and Asterisk@Home 2.2. If you wish to use it with some other version of Asterisk@Home or with a "pure Asterisk" system, then you're on your own. We're also assuming you have a working wakeup call system on your Asterisk box. Dial *62 from any extension on your system, and enter a wakeup call for a few minutes in the future, and be sure you get a return call on schedule. If not, get that installed and working first. Here's a link to our Asterisk@Home 2.2 installation tutorial if you need assistance. Once all the prerequisites are satisfied, this project ought to take you about 30 minutes ... if you're a fast reader.
How It Works. The reminder system is actually quite simple to use. You dial extension 1-2-3 on your Asterisk system, enter your password, and then you'll be prompted to record a reminder message. Next you enter the phone number, date, and time for delivery of the reminder message. When the appointed date and time arrives, Asterisk will place the call to the number you specified using your default dialing rules and will play the customized reminder when the call is answered. If the call is not answered, the call will be repeated n number of times with a delay between calls of x minutes before giving up on the call. You'll get an email with the call reminder setup if desired. You also get to configure the number of retries and the delay between calls.
Finally, a word about failed calls. Reminders are important to most folks, or you wouldn't be scheduling them. So failed calls are problematic. On the one hand, you don't want to overburden your phone system placing thousands of reminder calls just because the calls continue to fail. You may have entered an incorrect phone number, for example. So our middle-of-the-road solution to failed calls is this. You can tell the system how many times to repeat the call and how much time to eat up between attempts. If the call still fails, the reminder will be deleted from the system. But the reminder message is preserved. If you look in /var/lib/asterisk/sounds/custom on your system, you will see some custom sound files (such as the reminder prompts which all begin with reminder). In addition, you will see the actual reminder messages for each of your reminders. The naming convention is HourMinute.Date.PhoneNumber.gsm. If you see entries in this directory with dates before today's date, those are failed call reminders. You can play the sound files on most computers by simply double-clicking on the files. You can delete old reminders while logged in as root on your Asterisk system with a command like this:
rm -f /var/lib/asterisk/sounds/custom/*.20060123.*.gsm
Just be sure you don't delete today's reminder messages or messages with a future date, or your entire reminder system will be up in smoke!
Here are the components that make up the complete system:
Limitations. There are a few limitations you need to be aware of. First, you can't schedule delivery of a reminder within the first 5 minutes after midnight each night. That's when the reminder system does its housekeeping. Second, you must schedule reminders at least 5 minutes after you place your call to set up the reminder. This gives the system ample time to generate the configuration files it needs and to put them in the right places. Third, the current reminder system does not fully support simultaneous scheduling of multiple reminders. The current system uses unique names to identify sound files generated by multiple simultaneous callers. However, it still is theoretically possible for two different callers to schedule two reminders for the same phone number and the same reminder time and to do so at the exact same time. This would cause one of the reminders to be discarded. We just wanted to alert you to this remote possibility. But we hasten to add that the chance of this happening is pretty small even in a very large Asterisk system with hundreds of users. There's also no present ability to link to a database for scheduling reminders. Nor can you schedule recurring reminders for events such as birthdays and anniversaries. But we'll get to some of these features one day soon if there's sufficient interest in the product.
License. This software is licensed for your use under a Creative Commons Attribution-ShareAlike 2.5 license. Before using this software, please read the terms of the license. A Plain English version of the license is available here. You may not charge a fee for something we are giving you for free. Finally, we ask that you preserve our copyright notice in any copies of the software you make. The same applies to derivative works. If you do not accept the terms of the license, do not use the software. Even if you accept the terms of the license, keep in mind that BY USING THIS SOFTWARE, YOU ASSUME ALL RISKS OF USE AND NO WARRANTIES EXPRESS OR IMPLIED ARE PROVIDED WITH THIS FREE SOFTWARE INCLUDING FITNESS FOR A PARTICULAR USE AND MERCHANTABILITY. In short, it's up to you to determine, at your risk, whether this software meets your needs. Don't assume that it will, and don't assume that the code is error-free. It's probably not.
The Game Plan. To get the Reminder System set up, we're first going to move all of the code into the proper places. This includes the modifications to the dialplan contexts, installation of the new Allison voice prompts, and installation of the PHP/AGI scripts. Then we'll walk you through configuring the system. And finally we'll schedule a reminder to make sure everything went according to plan.
Modifying Your Dialplan. Step #1 is to cut-and-paste some code into your dialplan. For those using Asterisk@Home or the Asterisk Management Portal (AMP), this code should go at the bottom of the extensions_custom.conf file in the /etc/asterisk directory on your system. If you're using Asterisk@Home 1.5, use this code. If you're using Asterisk@Home 2.x, use this code. For Step #2, you'll need to cut-and-paste the following code snippet into the top section of extensions_custom.conf in the [from-internal-custom] context:
exten => 123,1,Answer
exten => 123,2,Wait(1)
exten => 123,3,Authenticate(12345678)
exten => 123,4,Goto(reminder,s,1)
If, for some reason, you already are using extension 1-2-3 on your Asterisk system for some other purpose, then simply adjust the 123 extension in the four lines above to another number that works on your system. This is the number you will dial to schedule reminders. Line 3 is important as well. This is where you set a password for scheduling reminders on your system. Anyone that knows the password can schedule reminders. Simply replace 12345678 with a password that's secure enough for you to sleep well.
Finally, a head's up. When you do the cut-and-paste, double-check the long lines of code such as h,1 in [reminder9] and be sure all of the text ends up on a single line. Otherwise, things won't work correctly. Once you've added the two sections of code, save extensions_custom.conf and reload Asterisk.
Installing Reminder Voice Prompts. As some of you know, we mounted a campaign to raise a little dough to pay for Allison, the Voice of Asterisk, to record custom voice prompts for the new reminder system. To all that contributed, thank you! And, we have some money remaining to cover some things we'll be telling you about in the coming weeks. Incidentally, it's not too late to donate $5 if you are so inclined. All of the donations will be used to defray future Asterisk development project costs. It also guarantees you free voice prompts in future projects. At least for this project, the voice prompts are free for the taking subject to the terms of the license agreement whether you contributed or not. Just log into your Asterisk server as root and enter the following commands:
chmod 664 reminder*.gsm
chown asterisk:asterisk reminder*.gsm
Installing the Reminder PHP/AGI Scripts. While you're still logged in as root, let's install the final pieces of code that you'll need to get things working. Just execute the commands below which match the version of Asterisk@Home you're running.
For Asterisk@Home 1.x users:
chmod 775 reminder.php
chown asterisk:asterisk reminder.php
chmod 775 check*.php
chown asterisk:asterisk check*.php
chmod 777 run_reminders
chown asterisk:asterisk run_reminders
For Asterisk@Home 2.x users:
chmod 775 reminder.php
chown asterisk:asterisk reminder.php
chmod 775 check*.php
chown asterisk:asterisk check*.php
chmod 777 run_reminders
chown asterisk:asterisk run_reminders
Creating Reminders Directory. While you're still logged in as root, create the directory to store your reminders until the day arrives to execute them. Just issue the following commands:
Setting Up the Reminder Crontab Entry. Now we need to set up the cron job to actually move reminders into the wakeup calls directory on the day they are scheduled to run. While logged in as root, edit the crontab file: nano -w /etc/crontab. Be sure you typed the exit command in the last step, or you'll be logged in as asterisk instead of root. And you won't be able to edit the file! Now cut-and-paste the following command to the bottom of the crontab file. It should go on its own line immediately below the run_wakeups line which already is in the file and is used to schedule your wakeup calls.
0 0 * * * root /var/lib/asterisk/agi-bin/run_reminders >/dev/null 2>&1
Once you've added the line, save your changes: Ctrl-X, Y, then press Enter. Whew! That's it for the Reminder code. Now let's configure the system, and you'll be all set.
Configuring the Reminder System. To configure the reminder system, you'll need to edit the reminder.php script while logged in as root: nano -w /var/lib/asterisk/agi-bin/reminder.php. You'll notice a section of variables at the top of the file that looks like this:
$debug = 1;
$newlogeachdebug = 1;
$emaildebuglog = 0;
$email = "yourname@yourdomain" ;
$trunk = "local" ;
$callerid = "6781234567" ;
$acctcode= "Reminder" ;
This is the only section of code you ought to mess with. When we update the code in coming months to add features, we'll assume everything else has been left intact. Be very careful when editing this file. Don't remove any semicolons or quotation marks, or nothing will work! Here's a quick run-down on what each of the above variables does:
Once you've configured the Reminder System to meet your needs, save your changes: Ctrl-X, Y, then press Enter. HINT: You may want to start with the defaults which will work well for most folks.
Scheduling A Reminder. We're ready to take the Reminder System for a trial run at this juncture. Make sure you've reloaded your Asterisk system, and then dial 123 from an extension on your system. Enter the password you set up for your system and then press the pound key.
Entering a Reminder Message. You'll first be prompted to record a reminder message. This is the message that will be played when someone answers the reminder call. If you're not scheduling this reminder for yourself, then the message ought to explain who's calling and what the purpose of the call is. Once you've recorded your message, press the pound key to end the recording. You can replay or rerecord the reminder if desired while you're in this step of the reminder creation process.
Entering the Callback Number. When prompted for the reminder callback number, there are a couple of things to keep in mind. First, if you've specified "local" as the trunk to use for reminders in the reminder.php script, then the phone numbers can be entered in any format supported by your dialplan. Press the pound key after entering the appropriate number. The calls will be placed using the trunks specified in your dialplan rules. The one exception is extensions on your local Asterisk system since these can't be routed by Asterisk@Home using your outbound calls dialplan rules. The way we handle extensions is by examining the length of the phone number. At the top of reminder.php, you can specify the maximum number of digits for local extensions by setting $extensionmaxdigits. So long as the callback number is less than or equal to this number of digits, the system has the smarts to correctly route the call to a local extension.
If you have designated a particular trunk for placement of reminder calls, then you'll need to make certain that the format of the phone numbers entered for reminders on your system matches a dial string supported for this outbound trunk in your dialplan. For example, if this trunk requires that calls be entered with a 1 and then an area code and 7-digit number, then that is the only format that should be used for entering callback numbers in your reminder system. Again, the one exception is calls to local extensions. So long as the number of a local extension is entered with less than or equal the number of digits set for the $extensionmaxdigits variable in reminder.php, the call will be routed properly to the local extension regardless of the trunk setting.
Finally, here's a shortcut that can be used if the phone you're using to schedule the reminder is the same one on which you want to get the reminder callback. In this case, just press the pound key when prompted for the number to which to deliver the reminder message. This will set the callback number as the caller ID of the phone you used to schedule the call. If it's a local extension, then the caller ID will be set to the local extension number of the phone from which you placed the reminder scheduling call. Just be sure your $extensionmaxdigits is set correctly or calls to local extensions will fail.
Entering the Date of the Reminder. Once you accept the reminder message, you'll be prompted to enter the date on which this reminder will be delivered. Dates are entered using a four-digit year, then a two-digit month, and then a two-digit day. There is some error correction but not much. You obviously can't schedule reminders in the past! And you don't need to press the pound key after entering the eight digits. Once you've entered a date, the system will tell you what date you entered including the day of the week. If the entry is correct, just press 1 to move on.
Entering the Time of the Reminder. Now you'll be prompted to enter the delivery time for your reminder. Times are entered as a two-digit hour and two-digit minute. For times less than 1200, you will be prompted whether you meant AM or PM. For those that understand military time, you can avoid this step by entering times using the format: 1345 which means 1:45 p.m. You don't need to press the pound key after entering the four-digit time for delivery of your reminder. Keep in mind that you cannot schedule a reminder for delivery in the first five minutes after midnight. Other times "in the midnight hour" should be entered in the format: 0045 which means 12:45 a.m. Keep in mind that reminder times always must be at least 5 minutes in the future. Finally, you cannot schedule two reminders for the exact same date and time for delivery to the same phone number. Once you enter a delivery time, the system will play back both the date and time for the reminder as a precaution. Press 1 to accept your entries and store the reminder to disk.
Where Reminders Are Stored. There are actually two files that make up each reminder: the .call file which places the actual call and the .gsm file which is the reminder message itself. The file naming convention is HourMinute.Date.PhoneNumber with either a .call or .gsm extension. The sound files are all stored in /var/lib/asterisk/sounds/custom. Prior to the delivery date of the reminder message, the .call file is stored in /var/spool/asterisk/reminders. Then, at midnight on the date the reminder is scheduled for delivery, the run_reminders script in /var/lib/asterisk/agi-bin moves the affected .call files to /var/spool/asterisk/wakeups. The .call files in the wakeups directory are reviewed every minute of the day by the run_wakeups script which is stored in /var/lib/asterisk/agi-bin. By examining the first four digits of the filename, the scripts look for a match with the current hour and minute of the day. Once the time for the call arrives, the .call file is moved to /var/spool/asterisk/outgoing where the Asterisk system itself processes the .call script and places the call. All dialing retries are handled internally by Asterisk with no user or program control so it's important to set your default values correctly in the reminder.php script as explained above. Once the .call file is processed, Asterisk discards the file whether the call was successful or not. As noted above, the reminder message file is only discarded if the call is completed successfully. So, from time to time, you do need to review the contents of /var/lib/asterisk/sounds/custom and discard reminder messages, if any, with dates in the past.
Just another word of caution about the reminder message files: be very careful in deleting these files. The message files and .call files are linked by filename only, and there is no error detection or correction if the message file gets discarded before the time for the reminder call arrives. What would happen in such a situation is the call would be placed, someone would answer, Allison would say "please hold for an important reminder," and then there would be a brief silence followed by Allison saying "to repeat this reminder, press 1; otherwise, press 2" which is not entirely helpful.
Reminder and Wakeup Call Processing. It has been documented that flooding Asterisk with a bunch of .call scripts simultaneously can cause some of the scripts to be discarded without being executed. To avoid this problem, the run_wakeups script (which handles both wakeup calls and reminder message delivery) inserts a 5-second pause after each .call file is moved to the Asterisk queue. Should you experience dropped calls, one solution would be to increase the 5-second delay to a larger number, but we would caution against doing so unless you experience delivery problems with either your reminder messages or wakeup calls.
When you're first getting started with the reminder system, it's probably a good idea to fire up Asterisk's Command Line Interface (CLI): asterisk -r. Then you can watch as the reminders are scheduled and reminder calls are placed. Just schedule a reminder for five minutes from the time you begin the reminder call, and you'll be all set.
For Programmers Only. If you're just getting into PHP and AGI programming with Asterisk, then have a second look at reminder.php. In particular, take a look at the section of code that begins with parse agi headers into array. As best we can tell, this is the first version of this subroutine written in PHP that actually works. If you review the log file (reminder.txt), you will see a listing of all the AGI headers which are passed by Asterisk to PHP. But this is the first code we've seen that correctly reads the headers into variables where you can actually retrieve the content. Call it a feature! For example, the commented out line ($tmp = $agi['dnid']) shows the syntax to retrieve the DNID value from Asterisk. Just make a mental note that the parse AGI headers code in reminder.php actually works. Some of our previous code inherited the mistakes of others, but we've now taken the time to get this functioning properly because we needed it for something else. Here's the complete list of AGI headers that can be saved to variables in your PHP code should the need ever arise:
read: agi_request: reminder.php
read: agi_channel: SIP/204-6a1a
read: agi_language: en
read: agi_type: SIP
read: agi_uniqueid: 1138010325.1367
read: agi_callerid: "Line2" <204>
read: agi_dnid: 123
read: agi_rdnis: unknown
read: agi_context: reminder9
read: agi_extension: h
read: agi_priority: 2
read: agi_enhanced: 0.0
You'll also want to take note of a little quirk in Asterisk (compared to some PBXs). To decipher the extension which actually placed a call, you must parse the agi_channel variable for the data between the slash and hyphen characters since the DNID (dialed number identifier) returns the extension being called (as opposed to the originating extension) when an internal call is placed. Here's one PHP approach to get the answer which in this case happens to be extension 204. Regex wizards could probably save a line of code, but who cares.
$CallingID = substr(stristr($agi['channel'],"/"),1);
$CallingID = substr($CallingID,0,strrpos($CallingID,"-"));
Wish List? Well, that about covers version 1 of our shiny new Reminder System for Asterisk. If you have enhancement suggestions or other recommendations for version 2, we'd love to hear from you. Just post a comment to this topic, and we'll have a look. In the meantime, enjoy!
Other Asterisk Projects? For a complete catalog of all our previous Asterisk projects, click here. For the most recent articles including those you missed over the Christmas and New Year's holidays, click here and scroll down the page.