Do-it-yourselfers: Raspberry Pi with Asterisk installed to act as a door entry system

This is for those that are really into building their own hardware, in addition to being familiar with Asterisk. From Reddit’s Asterisk section comes this post:

I’m working on using a Raspberry Pi with Asterisk installed on it to act as a door entry system integrated with my home phone system (also running Asterisk). The operation I would like is this:

    Doorbell pushed.
    Raspberry Pi detects push and its asterisk originates a call from the local console channel to some predefined extension on the main asterisk server, which routes to the appropriate house extensions.
    Person at door hears either ringing or music, whatever.
    Call is eventually answered.
    Caller talks to owner and owner decides to let caller in.
    ????
    Asterisk in Raspberry Pi activates a local GPIO pin to open the door strike.

I believe I’ve got all of the above described parts working, or at least workable. The “????” line — I haven’t.

The post continues to explain the thinking that has bee put into solving this problem. Then others chime in, and finally a solution is found that involves some lines added to Asterisk’s features.conf file, and a small program in C. We’re not into hardware hacking but this solution seems like it would be a lot less expensive, and probably more fun to build than some, than an equivalent commercial unit.

How it used to be done: How to use Google Voice for free outgoing calls on an Asterisk/FreePBX system (the no-XMPP way)

 

Important
This article contains text excerpted from a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Certain edits, including those in the green boxes below, were made on or after December 1, 2013.

EDIT (May 23, 2018): This post is seriously outdated. For more recent information, see How to use Google Voice with FreePBX and Asterisk without using XMPP or buying new hardware. You may also wish to visit this thread at DSLReports for additional information.

It appears that some people are getting upset because Google is dropping the use of its XMPP protocol with Google Hangouts, and there is much speculation that they might drop XMPP support altogether, which could break the method used by PBX software (such as Asterisk, FreeSWITCH, or Yate) and even some hardware to connect to Google Voice.

We would just like to remind everyone that back in the ancient days of Google Voice, Asterisk users had another way to initiate Google Voice calls that did not involve the use of XMPP. While we do not suggest attempting to use this method today, unless absolutely nothing else works, we are reposting a portion of the original instructions here. Again, please note that this method is being reposted as a matter of historical interest only (see this article for more recent recommendations). It is just intended to show that it used to be possible to connect outgoing calls using Google Voice, but without using XMPP.

This method was used by those using Asterisk and FreePBX that wanted to enable free Google Voice outgoing calls. It was assumed that you already had a Google Voice number and has it coming into your system via an Inbound Route (that is, you had it forwarded to a DID that came into your system, that was then handled by an Inbound Route).

Before you began the process, you had to make sure that your Google Voice account was set up correctly.  In your Google Voice account, under the Settings link, Calls tab, you had to make sure the following four options on that page were set up as follows:

Call Screening: Off
Caller ID (in): Display caller’s number (very important!)
Do Not Disturb: Make sure this is unchecked!
Call Options: Make sure this is unchecked!

You also needed to install some Python additions and the pygooglevoice program, assuming you had not done so previously.  Under CentOS you’d do the following from a command prompt:

cd /root
yum -y install python-setuptools
easy_install simplejson
 

Important
If (and only if) you receive a series of error messages while attempting to install simplejson, it may be because your system is running an older version of Python, such as version 2.4 (if some of the error lines contain the string “python2.4”, this is almost certainly the problem). In that case, you can try this (note that the first line might be split into two lines on your display, but should be entered as a single line):

wget http://pypi.python.org/packages/source/s/simplejson/simplejson-2.0.9.tar.gz#md5=af5e67a39ca3408563411d357e6d5e47
tar xzvf simplejson-2.0.9.tar.gz
cd simplejson-2.0.9
sudo python setup.py install

You’d then download the latest version of pygooglevoice and install it, using a line similar to this (this is all one line):

wget http://code.google.com/p/pygooglevoice/downloads/detail?name=pygooglevoice-0.5.tar.gz

Then you’d extract and install pygooglevoice:

tar zxvf pygooglevoice-0.5
cd pygooglevoice-0.5
python setup.py install

There was also a patch that needed to be installed (again this is all one line):

sed -i 's|https://www.google.com/accounts/ServiceLoginAuth?service=grandcentral|https://accounts.google.com/ServiceLogin?service=grandcentral\&continue=https://www.google.com/voice|' /usr/lib/python2.4/site-packages/googlevoice/settings.py

The above was found in this thread in the PBX in a Flash forum, which explains why the patch was needed.

Then you’d open /etc/asterisk/extensions_custom.conf in a text editor and add two new contexts (assuming you were using Asterisk 1.6 or later):

 
[custom-gv-trunk-username
exten => _X.,1,System(gvoice -e username@gmail.com -p userpassword call ${EXTEN} gvregphonenum code &)
exten => _X.,n,Set(DB(gv_dialout_username/channel)=${CHANNEL})
exten => _X.,n,Wait(20)
exten => _X.,n,Noop(Never received callback from Google Voice on channel ${DB_DELETE(gv_dialout_username/channel)} – exiting)
exten => h,1,GotoIf($[“${CHANNEL(state)}” = “Ring”]?:bridged)
exten => h,n,Noop(Hangup on channel ${DB_DELETE(gv_dialout_username/channel)})
exten => h,n,System(gvoice -e username@gmail.com -p userpassword cancel &)
exten => h,n,Hangup()
exten => h,n(bridged),Noop(The channel has been bridged successfully)

Replacing username with the name of the user associated with the Google Voice account (the name before @gmail.com in the associated Gmail account), userpassword with the password of the Google Voice account, gvregphonenumber with the registered phone number in Google Voice that you want to forward calls to (this had to be a number that came into your Asterisk/FreePBX box, and that you had already registered it as a destination in Google Voice, NOT your Google Voice number.  And sometimes you had to use just ten digits, other times you had to add the “1” at the start of the number, and that could vary from account to account so you just had to try and see which worked), and code with a single digit that was one of the following:  1-Home, 2-Mobile, or 3-Work (you’d just use the single digit, not the word, and when you registered the destination phone number with Google Voice you had to tell them if the number was a Home, Work, or Mobile, so the code would correspond to what you’d put there. Some people found that they could omit the code and it would still work, but that wasn’t always true).

In the System() calls it was important not to omit the space and ampersand just before the final parenthesis — this allowed the dial plan to move ahead rather than imposing extra and unnecessary delays on the caller.

One strange thing about this context was that the Noop line was NOT optional – things just didn’t work as expected if it was left out or commented out.
 

Important
On one Asterisk 1.8 system, the following changes were necessary:

  1. The first instance of System(gvoice -e … had to be changed to System(sudo gvoice -e … (adding sudo before gvoice)
  2. In the file /etc/sudoers the following line had to be added: asterisk ALL = NOPASSWD: /usr/bin/gvoice
  3. The hangup portion of the context (everything below the first Noop statement) did not work as intended, and actually cancelled the Google Voice callback!. So it was changed to simply:

exten => h,1,Hangup

As you will see, the line that cancels an abandoned call was added to the second context. The first two changes were necessary because without them, Asterisk could not successfully execute the gvoice program, despite the fact that permissions were set to make it readable and executable by everyone.

The second context that had to be added in /etc/asterisk/extensions_custom.conf was this one:

 
[custom-gv-inbound-username
exten => s,1,NoCDR()
exten => s,n,Bridge(${DB_DELETE(gv_dialout_username/channel)})
 

Important
On one Asterisk 1.8 system it was found necessary to change the above context to read as follows:

 
[custom-gv-inbound-username
exten => s,1,NoCDR()
exten => s,n,GotoIf($[${ISNULL(${DB(gv_dialout_username/channel)})}]?ext-did-0002,gvregphonenum,1)
exten => s,n,Bridge(${DB_DELETE(gv_dialout_username/channel)})
exten => s,n,System(sudo gvoice -e username@gmail.com -p userpassword cancel &)
exten => s,n,Hangup()

The purpose of the line that begins with exten => s,n,GotoIf… is to correctly route the incoming call in case you initiated a callback from the Google Voice web page. The string gvregphonenum is replaced by the registered phone number in Google Voice that you are forwarding calls to, which should also be the same as the DID in your Inbound Route for Google Voice calls – this is NOT your Google Voice number! This line is optional, and note that it sends calls to the context ext-did-0002 in extensions_additional.conf, which could possibly be different in differently-configured versions or future versions of FreePBX. If it doesn’t work for you, you can leave the entire line out, but you won’t be able to initiate callbacks from your Google Voice page.

The purpose of the line that begins with exten => s,n,System… is to cancel the call in case the caller has hung up before the callback from Google Voice is received. If the call is not cancelled, weird stuff happens! You can omit the “sudo” if you did not find it necessary to add it above.

Again replacing all instances of username and userpassword with the name and password of the user associated with the Google Voice account. After saving those changes you would then go into the FreePBX GUI, go to the Tools menu, Custom Destinations module, and create a new Custom Destination. In the Custom Destination: text box you’d enter this string:

custom-gv-inbound-username,s,1

In the Description: field, you could use a meaningful description such as Google Voice Bridge username and then click on “Submit Changes.” After saving that you’d go into Trunk settings and create a new CUSTOM trunk with these settings:

Maximum Channels: set to 1
Dial Rules: Use what you like, it’s suggested that you at least add 1+NXXNXXXXXX but if your area allows seven digit dialing, you may also want to add one like 1areacode+NXXXXXX where areacode is replaced with your local three digit area code.
Custom Dial String: Set to Local/$OUTNUM$@custom-gv-trunk-username — once again, replace username with the name of the user associated with the Google Voice account.

Once you had created and saved this trunk, it could be used as a trunk selection for any USA/Canada Outbound Route.

One assumption was that you had already set up Google Voice to send incoming calls to your Asterisk system via a DID that did not change the incoming Caller ID number. Normally this would be accomplished by getting a DID from some provider (either a free one or one you paid for — it didn’t matter as long as they would pass incoming Caller ID number correctly). Once that was working, you’d create a second inbound route for that same DID, without changing the original in any way — this would be a new Incoming Route, which would be set it up as follows:

Description: Anything meaningful, such as Username Google Voice”.

DID Number: Same as on your other Inbound Route that handles your Google Voice traffic.

Caller ID number: This had to be be your Google Voice number (the one associated with your Google Voice account), but depending on how your DID provider sent Caller ID, you might have to specify it as either a ten or eleven digit Caller ID, or even +1 followed by ten digits.  Whatever format your DID provider used, you had to match their format, which sometimes meant watching the Asterisk CLI during a test call to see what format they used. If it didn’t match what they sent, it wouldn’t work.

Destination: the Custom Destination created above (e.g. Custom Destinations: Google Voice Bridge username).

No other settings of the Inbound Route were changed and in particular, you were advised not to set a CID Lookup Source because it would cause an unwanted delay in connection time and was totally useless in this route, and there’s no reason to do so since this route is only triggered when a single Caller ID comes in).

Once you had submitted these changes, you should have been able to add your Google Voice custom trunk as a trunk selection for one or more of your USA/Canada Outbound Routes.

If it didn’t work it was generally one of three things. Either the Custom Trunk hadn’t been specified as a destination in the appropriate Outbound Route, or the incoming Caller ID of the Google Voice call didn’t exactly match what you set up in the Inbound Route you created, or you got something wrong in extensions_custom.conf such as the username or password (or you missed a place where you were supposed to change one of those items).

For test purposes, you could go to a Linux command prompt (not a CLI prompt in Asterisk!) and try entering a gvoice command directly, using this format (note this is all one line):

gvoice -e username@gmail.com -p userpassword call numbertocall gvregphonenum code

All of those values are as explained earlier, except for numbertocall, which had to be a USA or Canada phone number in 11 digit format.  Yes, the number to call had to be in 11 digit format (with the leading “1”) whereas the gvregphonenum might (or might not) need to be TEN digits (no leading “1”) – go figure.  If it worked from the command line then you needed to double check your values in extensions_custom.conf.  If it did not work from the command line, then either there was something wrong with your pygooglevoice installation, or with Python on your system (you had to have at least Python version 2.4 or higher), or you could have been using incorrect values (EDIT: Or, on some systems, it might be necessary to execute the gvoice command using sudo from within Asterisk, as described above).  And, some people got confused over the fact that that the “gvregphonenum” value was NOT supposed to be their Google Voice number, but instead the number to which Google Voice forwarded their calls, and it had to be a number that had been previously registered as a destination with Google Voice.

That’s the basics of how it was done. Note this is not intended to be a guide as to how to set this up now, since many things have changed since then. Just because this method worked back then does not mean it will still work today, and it’s definitely slower in the setup of calls than more recent methods. However, it is worth noting that at no point was the XMPP protocol used. It’s also worth mentioning that there are other programs that can interact with Google Voice besides PyGoogleVoice (see for example Google-Voice-PHP-API, google-voice-java, SharpGoogleVoice, voice.js, etc.) that may or may not be more suitable for use in this type of application.

Link: Asterisk – Voicemail with Speech Recognition using Google API

This is probably a somewhat advanced level project, but for those who might enjoy a challenge, all the steps are laid out for you.

In a previous article I published a solution to convert Asterisk voicemail attachments from WAV to MP3 on the fly. This is done by catching the mails sent by Asterisk just before they are passed to sendmail.

I recently got the idea from Daniel Dainty to add Voice Recognition feature at the same time as mp3 encoding.

After testing different voice recognition engines, I realized that the Google Speech Recognition API is by far superior to any other solution available under Linux (Sphinx, …).

This article will explain an approach to add voice recognition to Asterisk voicemail using the services of Google Speech Recognition API.

Full article here:
Asterisk – Voicemail with Speech Recognition using Google API (Bernaerts family site)

Link: The SIPaholic’s Dream Come True: Introducing Anveo Direct SIP Trunking

We’re incredibly happy with the current list of providers that we recommend to PBX in a Flash™ users for VoIP trunking. At the top of our list is Vitelity, a leading VoIP provider that has been a major contributor to the Nerd Vittles and PBX in a Flash projects for many years. But, as often happens, one of our gurus on the PIAF Forum comes up with a terrific discovery that we just can’t wait to pass along. This week it was @w1ve who stumbled upon a new Anveo® Direct service for end-users with a special DID and SIP trunk offer. While the sub-account flexibility of Vitelity and some of the other providers is sorely missing, Anveo Direct does provide end-users with many of the same routing tools and SIP feature set that previously were reserved for use by major carriers. If you don’t believe some of the competition is less than thrilled, read this message thread on dslreports.com. And, until June 1, you can order DIDs in almost any U.S. region for 50¢ a month per DID with no setup fee. With Anveo Direct Value, that 50¢ buys you two trunks with 400 free incoming minutes a day. Outbound calls are pay-as-you-go and vary depending upon where you’re calling. Typical U.S. rates are $.001 to $.0055 per minute with least cost routing and automatic failover when a particular carrier’s route is having problems. …..

Full article here:
The SIPaholic’s Dream Come True: Introducing Anveo Direct SIP Trunking (Nerd Vittles)

Not receiving some incoming Google Voice calls? Try increasing the priority

A page on the Asterisk Wiki entitled Calling using Google contains this bit of information about priorities:

More about Priorities

As many different connections to Google are possible simultaneously via different client mechanisms, it is important to understand the role of priorities in the routing of inbound calls. Proper usage of the priority setting can allow use of a Google account that is not otherwise entirely dedicated to voice services.

With priorities, the higher the setting value, the more any client using that value is preferred as a destination for inbound calls, in deference to any other client with a lower priority value. Known values of commonly used clients include the Gmail chat client, which maintains a priority of 20, and the Windows GTalk client, which uses a priority of 24. The maximum allowable value is 127. Thus, setting one’s priority option for the XMPP peer in res_xmpp.conf to a value higher than 24 will cause inbound calls to flow to Asterisk, even while one is logged into either Gmail or the Windows GTalk client.

Outbound calls are unaffected by the priority setting.

Therefore, if you are not receiving some incoming Google Voice calls, check to make sure that your priority= statement in the configuration is set to at least 25.

Read more: Calling using Google

Link: How to Install Asterisk 11 in RHEL/CentOS/Fedora and Ubuntu/Debian/Linux Mint

Asterisk is an Open Source software PBX (Private Branch Exchange), developed by Mark Specer of Digium. It allows you to make calls to one another which may have connected to other PSTN (Public Switched Telephone Network) and Voice over Internet Protocol (VoIP). Asterisk runs on Linux, BSD, MacOSX and others. Having built-in features like voicemail, conferencing, IVR, queuing etc.

In this article, we are going to see a basic installation and working of Asterisk 11 in RHEL 6.4/6.3/6.2/6.1/6.0, CentOS 6.4/6.3/6.2/6.1/6.0, Fedora 18,17,16, Ubuntu 12.10/12.04/11.10, Linux Mint 14/13 and Debian Linux Operating Systems.

How to Install Asterisk 11 in RHEL/CentOS/Fedora and Ubuntu/Debian/Linux Mint (TecMint.com)

Asterisk/FreePBX: How to get the DID of a SIP trunk when the provider doesn’t send it (and why some incoming SIP calls fail)

The symptom: On a SIP trunk, you can’t get an inbound route to work – it just doesn’t seem to recognize the number. You might be able to get the call to come in on your any DID/any CID route, or maybe the call doesn’t get answered at all. When you type sip debug from the CLI, you can see (when you scroll back to the point where the call came in) that a sip INVITE packet arrived, and perhaps it contained the DID number in the sip To: header (in the form To: <sip:NUMBER@IP ADDRESS>), but you also see that the FROM_DID was set to s. In other words, you see a line that looks like this:

  -- Executing Set("SIP/9995552368-09876543", "FROM_DID=s") in new stack

EDIT: The most common reasons this happens are one or both of two things. Either you are not specifying the number of your DID in the format your provider is sending it (for example, they are sending 11 digits including a leading “1”, but you are only using ten digits in your inbound route), or they are sending the number in the SIP “To:” header. In the latter case, FreePBX has a much easier way to deal with this now. Before you do anything else, try changing the trunk context from from-trunk or from-pstn to from-pstn-toheader. This works for both SIP and PJSIP trunks, but only if the provider really is sending the number in the SIP “To:” header.

Only continue with this article if you have tried the above and it doesn’t work, as much of what is below simply shows an older and less intuitive way of doing the same thing. If the above did not work, don’t forget to change your trunk context back to whatever it was previously. (End of edit.)

Before you attempt anything else, you may want to try this suggestion by Dan Swartz: Check the registration string (in the trunk settings for the provider), and if it’s not already there, try putting the DID at the end of the registration string, prefixed by a ‘/’. It may (or may not) require a leading ‘1’ too. e.g. ‘/18005551212’ (or your country code if you are not in the U.S./Canada, etc.). So, your registration string would take this format:

accountid:password@your.provider/yourDIDnumber

Remember to try your DID both with and without the country code prefix. If this doesn’t work, it’s time to try a workaround (however, you may want to read the addendum at the bottom of this article first!). Perhaps you can see the DID number in the sip INVITE packet’s To: header, but the CLI reveals that Asterisk isn’t picking it up, and therefore it goes to your default inbound route.

(Oh, and for anyone who’s still trying to figure out how to turn off sip debugging, the CLI command is sip no debug)

Fortunately this isn’t a hard thing to work around, as long as the DID number really is in the sip To: header.

NOTE: In the following examples, we now use the s extension rather than _. – this is considered better practice (and safer) but the disadvantage is that the context will fail if the provider is sending any type of DID, even if it’s incorrect or incomplete. If the code doesn’t seem to work, try replacing the s extension with _X! (the extension is to the right of the => and space characters).

First, create a context in extensions_custom.conf that looks like this:

EDIT: Really, don’t do this next part, unless you are running an old version of FreePBX! Just change your trunk context to from-pstn-toheader and be done with it! (End of edit.)

[custom-get-did-from-sip]
exten => s,1,Noop(Fixing DID using information from SIP TO header)
exten => s,n,Set(pseudodid=${SIP_HEADER(To)})
exten => s,n,Set(pseudodid=${CUT(pseudodid,@,1)})
exten => s,n,Set(pseudodid=${CUT(pseudodid,:,2)})
exten => s,n,Goto(from-trunk,${pseudodid},1)

Or, thanks to naftali5, you can cut the above down to one line of code that does the same thing, but is a bit less obvious to the casual reader:

[custom-get-did-from-sip]
exten => s,1,Goto(from-trunk,${CUT(CUT(SIP_HEADER(To),@,1),:,2)},1)

(And speaking of naftali5, if you are using his Dialplan Injection module – which may not work with the most recent versions of FreePBX, so don’t run out and get it if you aren’t already using it unless you are sure it has been updated to work with the version of FreePBX that you are using – and want to put the above line in the Destination section, then you will need to use a slightly different syntax, changing the commas to bar characters, so it looks like this:

* Custom App: from-trunk,${CUT(CUT(SIP_HEADER(To)|@|1)|:|2)},1

The part after Custom App: is what you paste into the text box. This ONLY applies to Dialplan Injection users)

Then, in the trunk associated with the provider, change the trunk context statement (which should read context=from-trunk) to:

context=custom-get-did-from-sip

(Or for Dialplan Injection users, just use

context=injection-n

but replace n with the actual injection number, which will appear next to the injection name in the right-hand column menu of injections.)

And note that with such providers, you may have to move that context statement from the USER details to the PEER details section. This is why calls from some SIP providers sometimes fail to come in at all – they effectively never "see" the User context and details, therefore they don’t see the context statement there and have nowhere to go. It’s also why you sometimes see instructions for sip providers that leave the User context and User details sections totally blank, but include a context statement in the peer details – in most such cases it’s because the provider is treating the customer as an end user (like someone using a softphone or a VoiP adapter) rather than as a peer, and they aren’t sending DID information.

The above instructions may also solve the problem where you have two (or more) trunks from the same provider, but Asterisk always treats it as if all calls are coming in on one of the two trunks, therefore again not allowing you to set up separate inbound routes for each trunk. As long as the provider sends the number in the sip To header, the above code should set the DID properly.

If the first part of the To: statement is something other than a DID number (a user name, for example), then you may have to add a line just before the final Goto statement. For example, let’s say the provider is sending To: <sip:Fred@IP ADDRESS> and your DID number (or at least, the number you want to use to denote your inbound route) is really 5551212. You’d then use code similar to this:

[custom-get-did-from-sip]
exten => s,1,Noop(Fixing DID using information from SIP TO header)
exten => s,n,Set(pseudodid=${CUT(CUT(SIP_HEADER(To),@,1),:,2)})
exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="Fred"]?5551212:${pseudodid})})
exten => s,n,Goto(from-trunk,${pseudodid},1)

Or, as long as you only have ONE trunk from that provider, you could always just cheat a little and hardcode the desired DID in a separate custom context, like this:

[custom-stupid-provider]
exten => s,1,Noop(Fixing DID to 5551212)
exten => s,n,Goto(from-trunk,5551212,1)

And use the name of this context in the trunk settings. I hear you asking, why not just do it this way on all trunks with this issue? Well, because if you add a second trunk from the same provider, this won’t work correctly for both trunks, and if you ever change your number and then forget what you’ve done and just try to set your inbound route to the new number, it won’t work. And besides all that, if you have more than one SIP provider that doesn’t send proper DID, you’d have to create a separate custom context for each of them, instead of having one custom context that works for all of them.

One final note for Free World Dialup users, you may find that sip calls will still not come in until you put the following statement in sip.conf:

insecure=invite

I have no idea why that works, but it seems to make a difference.

What if the provider doesn’t send the number in the sip To: header?

There is at least one provider that actually sends a s character instead of a number in the sip To: header. What can you do with a provider like that? Well, all may not be lost. If you only have a single trunk from that provider, you can just use the "cheat" shown above, since it doesn’t rely on the contents of the sip headers. If, however, you have TWO or more trunks from the same provider, you can do a sip debug from the CLI and watch as calls come in on each trunk and note whether there are any consistent differences.

For example, if you have two lines on the same account, the provider will often assume that you are using a VoIP adapter (such as a Sipura or Linksys) and will use port 5060 for line 1, and port 5061 for line 2. That difference might show up in the headers of the sip INVITE packet, for example:

Via: SIP/2.0/UDP 111.222.333.444:5060;branch=z9hQ4bK67sc0a8e;rport

In this case, you see that there is a colon (:) before the port number and a semicolon following, and that there are actually TWO colons on the line before the port number, so maybe this would work:

[custom-really-stupid-provider]
exten => s,1,Noop(Fixing DID using port from SIP VIA header)
exten => s,n,Set(pseudodid=${CUT(CUT(SIP_HEADER(Via),;,1),:,3)})
exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="5060"]?5551111:${pseudodid})})
exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="5061"]?5552222:${pseudodid})})
exten => s,n,Goto(from-trunk,${pseudodid},1)

Or, if you only have two trunks from this provider, you probably could just condense the two test lines into one, by testing for one port number and assuming the other if the conditional test fails, like this:

exten => s,n,Set(pseudodid=${IF($["${pseudodid}"="5060"]?5551111:5552222)})

Note that the code in this section is untested, it’s just to give you some ideas about how to possibly handle the really oddball situation were you have two (or more) lines from the same provider, and cannot find any other way to differentiate them. And, don’t automatically assume you have a bigger problem than you actually have – for example, it may well be that having different port numbers on the different trunks would allow Asterisk to distinguish them enough that the simple "cheat" method would work (you’d have to make one for each trunk, of course).

Again, if a particular piece of code doesn’t seem to work at all, try replacing
exten => s, . . . . .
with
exten => _X!, . . . . .
in all lines of the context (just in case the provider is sending some unknown number). But if that works, you should consider using whatever the provider is actually sending as the DID for your Inbound Route, which would eliminate the need for this extra code altogether.

Addendum

There are a few other reasons that incoming calls may fail that have nothing to do with the main topic of this How-To, but are common enough that they should be mentioned anyway. The first is that in every trunk configuration, there must be a statement that reads:
context=from-trunk
Some providers will tell you to set the context statement to something else – don’t do it (unless you have a valid reason, such as following the instructions above). Providers are generally more familiar with Asterisk than FreePBX, and often don’t realize that you can’t just use any context name you like in FreePBX unless you also create that context somewhere (usually extensions_custom.conf).

Also the placement of that context= statement can be important. If a provider is treating you as an extension (which would likely be the case if most of their customers use VoIP adapters and/or you are a “Bring Your Own Device” customer) then in most cases you will not need to have a USER context or USER details at all in your trunk, but you still need to have a context=from-trunk statement (or another context= statement if you are following instructions elsewhere on this page) and in this case it will need to be in your PEER details, not your user details. So, if incoming calls aren’t working, try putting context=from-trunk in your trunk PEER details, and if that works, then totally remove the USER context and USER settings from your trunk configuration, since they aren’t doing any good anyway.

To further complicate matters, I have noted that with a couple of providers, nothing seems to work until you do the following. The symptom typically is that you have no problem connecting with other providers, and (if you have tried this) you have no problem connecting with the provider in question if you are using an external hardware device (such as an ATA), but no matter what you do you can’t receive calls from the provider. I hate recommending this because I don’t know exactly why it works, but it’s a trick I’ve used for a couple years now to resolve issues with a couple of specific providers. Don’t try this until you’ve first tried adding the DID at the end of the registration string, prefixed by a ‘/’, as shown near the top of this page.

Open the file /etc/asterisk/sip_general_custom.conf in any text editor and check to see if the following lines are in there:

bindport = 5060 ; Port to bind to (SIP is 5060)
bindaddr = 0.0.0.0 ; Address to bind to (all addresses on machine)
insecure=invite
tos=0x68
srvlookup=no

If any of the above lines are missing, add them to the file. In my experience these lines will not cause problems with other providers but they do magically get things working with a select few providers that are more used to serving customers using an ATA than through a trunk into an Asterisk box. If this gets things working you can try removing lines one at a time to find out which are actually doing the trick, or you can just leave them all in place – as I say, in my experience they don’t seem to cause problems with other providers, although obviously your experience might be different.

Yet another issue that you may encounter, especially if you are upgrading from an earlier version of FreePBX, is that if you have disallow= and allow= statements in your trunk configuration (to specify the use of particular codecs), starting with Asterisk 1.4 the disallow= statement(s) (particularly if it’s disallow=all) must be placed above the allow= statements. This is one of many cases where Asterisk upgrades have broken existing functionality for no good reason whatsoever, other than that the Asterisk developers could not be bothered to ensure backward compatibility.

One other note: There is an obscure bug in Asterisk that can cause incoming calls to fail. If Asterisk ever receives a Caller ID NAME that contains only one quotation mark (usually a name with a quotation mark at the start of the string but not at the end) it will not handle the call properly, and may ignore the incoming call completely.

Additional Reference:

Asterisk SIP channels

Why Asterisk-based GUI software should make better use of regular expressions

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Having recently discovered that Asterisk supports regular expressions (with some limitations), I got to wondering why the FreePBX-based GUI’s don’t make better use of them.  For example, in FreePBX, Trixbox, Elastix, etc., the Outbound Route pages all support entering patterns that are then directly used in Asterisk dialplans, in a most inefficient manner.  Let me give you an example.  Let’s say you have created an Outbound Route for toll-free calls, and in the “Dial Patterns that will use this Route” section you have entered these patterns:

1800NXXXXXX
1844NXXXXXX
1855NXXXXXX
1866NXXXXXX
1877NXXXXXX
1888NXXXXXX
800NXXXXXX
844NXXXXXX
855NXXXXXX
866NXXXXXX
877NXXXXXX
888NXXXXXX

Let’s further say that you have specified three trunks in the “Trunk Sequence for Matched Routes” section (this is actually conservative given that there are a number of ways you can route toll-free calls for free).  Anyway, this is an actual representative context that would be generated in extensions_additional.conf:

[outrt-5] ; TollFree
include => outrt-5-custom
exten => _1800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1800NXXXXXX,n,Set(_NODEST=)
exten => _1800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(outisbusy,)
exten => _1844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1844NXXXXXX,n,Set(_NODEST=)
exten => _1844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(outisbusy,)
exten => _1855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1855NXXXXXX,n,Set(_NODEST=)
exten => _1855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(outisbusy,)
exten => _1866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1866NXXXXXX,n,Set(_NODEST=)
exten => _1866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(outisbusy,)
exten => _1877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1877NXXXXXX,n,Set(_NODEST=)
exten => _1877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(outisbusy,)
exten => _1888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1888NXXXXXX,n,Set(_NODEST=)
exten => _1888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(outisbusy,)
exten => _800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _800NXXXXXX,n,Set(_NODEST=)
exten => _800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _800NXXXXXX,n,Macro(outisbusy,)
exten => _844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _844NXXXXXX,n,Set(_NODEST=)
exten => _844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _844NXXXXXX,n,Macro(outisbusy,)
exten => _855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _855NXXXXXX,n,Set(_NODEST=)
exten => _855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _855NXXXXXX,n,Macro(outisbusy,)
exten => _866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _866NXXXXXX,n,Set(_NODEST=)
exten => _866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _866NXXXXXX,n,Macro(outisbusy,)
exten => _877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _877NXXXXXX,n,Set(_NODEST=)
exten => _877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _877NXXXXXX,n,Macro(outisbusy,)
exten => _888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _888NXXXXXX,n,Set(_NODEST=)
exten => _888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _888NXXXXXX,n,Macro(outisbusy,)

; end of [outrt-5]

That is 112 lines of dialplan generated by only 12 lines of pattern in the Outbound Route and three trunk selections.  If you have more patterns and/or more trunks, the generated dialplan can grow MUCH larger.  At one point I had a dialplan so large that whenever I did an “orange bar” reload, it took two to three minutes to finish reloading.  Clearly, there is something wrong here.

Now, let’s suppose that instead of, or better yet, in addition to the current Outbound Route pages, we had pages called “Outbound Route – Regular Expression”.  It would eliminate the “Dial Patterns that will use this Route” section, replacing it with a “Regular Expression to select this Route” text box, and it would also have a text box that says “Extensions allowed to use this route (separated by the | character – RegEx patterns may be used).

Let’s say that the contents of the first text box are ^1?8(00|44|55|66|77|88)[2-9][0-9]{6}$ and are stored in variable string1. And let’s say that the contents of the second text box are 10[0-4]X|106[5-8] (specifying that extensions 1000-1049 and 1065-1068 may use this route) and are stored in string 2. Assuming that the part of the dialplan that processes this were in some kind of macro, and the called number is in the variable OUTNUM, you could perhaps do something like this:

exten => s,n,ExecIf($[${REGEX(“${string2}” ${CALLERID(num)})} != 1]?MacroExit())
exten => s,n,ExecIf($[${REGEX(“${string1}” ${OUTNUM})} != 1]?MacroExit())
exten => s,n,Macro(dialout-trunk,7,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,10,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,8,${OUTNUM},)
exten => s,n,Macro(outisbusy,)

The first line bails out of the macro if the Caller ID doesn’t match one of the permitted extensions (this would assume you by default use a regex that matches any extension on your system, for example ^[0-9]{4}$ would match any 4 digit extension number. If you don’t assume this, then you’d need an extra bit of code to skip a null value in string2).

The second line bails if the called number doesn’t match the pattern in string1.

If the called number does match on the regular expression for this route, and the call is from a permitted extension, then the three trunks are tried. Note that now you only need to list each of the three trunks once.

Granted this doesn’t account for every line in the original context, but the other lines could also be transferred into the macro, with a bit of thought. Wouldn’t this be simpler for those who know how to use regular expressions? And, it has the potential to generate MUCH shorter dialplans.

Those who understand the power of regular expressions and who have long or complicated dialplans would appreciate the ability to use regular expressions!  If anyone implements this, don’t be stingy with the maximum string length in the Regular Expression text boxes; I guarantee you that 256 characters is not enough, and neither is 1,024 characters! At a MINIMUM I would say that you need to allow a 4,096 character (4K) regular expression, and while I personally can’t conceive of anyone exceeding that, my bet is that sooner or later someone will.  Remember, you are potentially saving a whole bunch of memory and drive space by allowing regular expressions, so you don’t have to be miserly on the space allotted for the expressions themselves.

How to send various types of notifications on an incoming call in FreePBX

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

PLEASE NOTE: When you look at examples in this article, be aware that WordPress has a nasty habit of changing quotation marks to “prettified” quotes, which WILL NOT WORK. So, unfortunately, if you copy an example line or code block, you may need to go through and change the quotation marks back to the “real” quotes that most software understands. I get SO sick and tired of WordPress changing my articles on me!

Every so often I’ve had someone ask if they could be sent some sort of notification whenever they get an incoming call. It is, although it will send a notification on any incoming call, not just a missed one (if you only want notifications of missed calls, look toward the bottom of this article). The basic technique is as follows:

First, create or edit a Follow-Me for the user’s extension. Let’s say the extension number is 1122. In the Follow-Me, add a line that looks like this:

****1122#

Note you can use about anything in place **** followed by the extension number and # sign — only the trailing # must be there — but I do it that way so I can keep track of what’s going on.  If you are creating the Follow-Me for the first time, be sure to also put your actual extension number in the Follow-Me list (with NO trailing # sign), and check the other Follow-Me options to make sure that they are what you want (for example, that the “Destination if no answer” is your voicemail, if that’s what you want).

Next, in /etc/asterisk/extensions_custom.conf, find the [from-internal-custom] context (it’s right at the top of the file) and add lines similar to the ones shown below to the bottom of that context. Again, we are using 1122 as the extension, but replace it with the actual extension number:

Note that some of these examples depend on certain other software being present. Also, the FIRST line in each sequence must have the line number 1, but if you use more than one of these for an extension, replace the 1 with n in subsequent lines.

To send an e-mail or SMS message (the mail command must function properly from the Linux command prompt for this to work):

exten => ****1122,1,TrySystem(echo "Call from ${CALLERID(name)} at ${CALLERID(number)} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)}" | mail 9995559999@yourcarrier.com)

Replace 9995559999@yourcarrier.com with the e-mail address (which could be an e-mail to SMS gateway – most wireless carriers have one. Just search for “Email to SMS Gateway” and add your carrier’s name to the search, and you should find the format to use).  You can use “System” or “TrySystem” in the line above, the difference being that TrySystem is non-blocking, so the call should not be delayed if the process of sending the e-mail bogs things down.

To send an instant message using the XMPP/Jabber protocol — for this to work /etc/asterisk/xmpp_custom.conf (/etc/asterisk/jabber.conf in older versions of FreePBX) must be configured correctly, and there must NOT be a noload => res_jabber.so statement in modules.conf:

exten => ****1122,1,JabberSend(asterisk,username@gmail.com,Call from ${CALLERID(name)} at ${CALLERID(number)} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})

In the above, asterisk is the account context in xmpp_custom.conf or jabber.conf, and username@gmail.com is the address of the XMPP/Jabber user you want to send the IM to (in this case we assume you and the user are on Google Chat, but you could use any other XMPP/Jabber-based IM service, including a local XMPP/Jabber server set up with Prosody or Openfire or similar software. EDIT: If you are running FreePBX on a Raspberry Pi, see this article and this thread for information on setting up a Prosody server).

If you have Kodi (formerly XBMC) running on a system on your local network, you could use a line such as this to send notifications to it (EDITED to include Kodi Leia, Matrix, and Nexus):

In Eden and other Pre-Frodo versions:

exten => ****1122,1,TrySystem(wget -b -O /dev/null -o /dev/null "http://192.168.0.234:8080/xbmcCmds/xbmcHttp?command=ExecBuiltIn&parameter=XBMC.Notification(Call%20from%20%22${URIENCODE(${CALLERID(name)})}%22%2C${CALLERID(number)}%20calling%20extension%2C15000%2C%2Fhome%2Fusername%2Fphone.png)")

In Frodo through Krypton:

exten => ****1122,1,TrySystem(wget -b -O /dev/null -o /dev/null "http://192.168.0.234:8080/jsonrpc?request={%22jsonrpc%22:%222.0%22,%22method%22:%22GUI.ShowNotification%22,%22params%22:{%22title%22:%22Call%20from%20${URIENCODE(${CALLERID(name)})}%22,%22message%22:%22${CALLERID(number)}%20calling%20extension%22,%22displaytime%22:15000,%22image%22:%22%2Fhome%2Fusername%2Fphone.png%22},%22id%22:1}")

In Leia, Matrix, Nexus and probably later versions, until they decide to change it again:

exten => ****1122,1,TrySystem(curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"GUI.ShowNotification","params":{"title":"Call from ${CALLERID(name)}","message":"${CALLERID(number)} calling extension","displaytime":15000,"image":"/home/username/phone.png"},"id":0}' http://192.168.0.234:8080/jsonrpc)

Note: If you set Kodi to require authentication, then in place of the example http://192.168.0.234:8080/jsonrpc instead use the format http://kodiusername:password@ip-address:port/jsonrpc where kodiusername, password and port are the same values you used when setting up your Settings | Service | Control | Web Server settings in Kodi. Note that “kodiusername” is the user name set up in Kodi’s web server settings, and is probably not the same as “username” in the phone.png icon path (which in that case is the name of the user’s home directory).

Note that each of the above is a single long line, and to see any of those lines in its entirety you will need to copy and paste it to a text editor. The above examples assume that Kodi is configured to allow control via a Web interface, at a fixed IP address and port (192.168.0.234 port 8080 in this example). Phone icon - right click and copy imageIf you happen to still have a Linux version of Boxee, the Eden version line might also work with it, perhaps with a bit of tweaking.  I have no idea if it would work (with appropriate modification) with any other standalone home theater PC equipment.  You will likely need to replace username with the user’s name, extension with the called extension’s number, and change the icon path to wherever you put the phone.png file (which is an icon you need to supply, such as the one at the right). Note that the icon path requires %2F in place of forward slashes in pre-Leia versions (therefore %2Fhome%2Fusername%2Fphone.png really means /home/username/phone.png) and this refers to the icon directory and filename — if you choose not to use an icon then leave out this part, depending on your Kodi version:

Eden and prior: %2C%2Fhome%2Fusername%2Fphone.png
Frodo through Krypton: ,%22image%22:%22%2Fhome%2Fusername%2Fphone.png%22
Leia through Nexus (and beyond?): ,"image":"/home/username/phone.png"

Note the comma at the start; it must be included in the deleted section).  By the way, if you are wondering why it had to be changed again for Kodi Leia, see this thread in the Kodi forum.

If you want to receive Caller ID popup notifications on your computer, see A Perl script to send Caller ID popups from Asterisk to computers running Notify OSD (such as Ubuntu Linux) or any command-line invoked notification system and/or A Perl script to send Caller ID popups from Asterisk to computers running Growl under OS X on a Mac or Growl for Windows.  These articles are a bit dated and may not be compatible with some of the newer notification methods in use (such as the Notification Center used in MacOS that was introduced in OS X 10.8); in such cases it may be easier to just send instant messages to the computer as described above, although it may be possible to ssh into the computer and send a notification from the command line – this article describes how to send a notification from the command line in MacOS.

After using the any of above lines (and, again, remember to change the line number from 1 to n on any lines after the first), you may want to include a statement like this:

exten => ****1122,n,NoCDR()

That should prevent Asterisk from creating a separate, usually unwanted CDR entry for the ****(extension number) call.  There will still be a CDR record created for the actual call. EDIT: Actually, if you are going to use this, you probably should make it line 1, and move it above the other ****(extension number) lines, so that the CDR record is not saved even if the call gets disconnected in the middle of sending the notification. EDIT 2: In recent Asterisk versions NoCDR() has been deprecated and replaced by CDR_PROP(disable) so be sure to make that change if you are getting BUSY lines in the CDR when this runs.

Finally, to end this part of the context, you need to include a statement that ends the call in a “not completed” state, so that any other extensions in the Follow-Me will be called.  A couple of possibilities are:

exten => ****1122,n,Congestion
exten => ****1122,n,Busy

Use one and if it doesn’t work as expected, try the other. On my system it appears either will work. Remember that you must do a configuration reload in FreePBX before any of your changes to extensions_custom.conf will take effect!

One reason you may want to do instant message or SMS notifications is if you are forwarding calls via a trunk that does not preserve the original caller’s Caller ID.  If you were forwarding the call to a smart phone (perhaps one that also has a Jabber client running), you could deliver the original caller’s name and number to the phone via instant message or SMS.  It may or may not arrive in time for you to make a decision on whether to answer the call, but if you chose not to answer you’d at least know within a few seconds to a few minutes who the call was from, and you could decide whether to call them back.

If you have figured out any neat tricks to do other types of notifications, or if you know of a way to send a notification only when a call was not answered, feel free to leave a comment.

EDIT: At one time this page showed a method for sending SMS notifications using Google Voice rather than an e-mail to SMS gateway. Unfortunately it appears that the unofficial API that was used by such applications is no longer functional. If you know of a way to send a SMS message from a Linux command prompt using Google Voice that is currently working, please leave a message in the comments!

EDIT: I did think of a couple of techniques that might work for sending a notification only if the call is “missed” — the first is a bit of a hack, and has a serious limitation in that if the caller hangs up prior to the call going to voicemail, no notification would be sent (which might or might not be what you want). Basically, you would create (or modify) a Follow-Me as described above, but don’t put the ****extension# line in it. Instead of making the “Destination if no answer” your voicemail (for example), you would point it to a Ring Group that you have created, and in that Ring Group you would place the ****extension# line, and make the “Destination if no answer” of the ring group your voicemail. So the Follow-Me would ring your extension(s) for the number of seconds in the “Ring Time” setting, but if that time expires and no one answers, the call would be sent to the Ring Group where the only “extensions” would be your ****, which would do their thing and immediately return a “busy” or “congestion”. Since there are no other extensions to try, the Ring Group should immediately pass the call on to its “Destination if no answer”, which would most likely be your voicemail.

I haven’t tested this but I see no reason why it would not work, and in that case the notification would be sent just as the caller is being transferred to voicemail. If the caller doesn’t stick around that long you don’t get a notification, however, the fact that you do get a notification doesn’t necessarily mean that the caller actually left a voicemail — if they hang up during the voicemail greeting, a notification would still have already been sent.

EDIT: Here is a different technique for sending notifications on ONLY missed calls, and with this one the notification is sent whether the caller sticks around to leave a voicemail or not. You do NOT add anything to your Follow Me list when using this technique, nor do you modify extensions_custom.conf.

Note that this technique probably will not work on extensions that are part of a Ring Group or Follow Me where more than one extension is rung simultaneously (at least not without some modification — it should work if you use a ring strategy that only rings one phone at a time, though, such as firstnotonphone or firstavailable) (2017 EDIT: Some users have reported those two ring strategies are broken in FreePBX 13 and 14). Be aware that this one is a bit riskier since you will need to re-do the first part of the following procedure after any FreePBX upgrade, otherwise things may break!

  • Load the /etc/asterisk/extensions.conf file into a text editor such as nano
  • Search (Control-W in nano) for the string [from-internal] (including the brackets)
  • Copy the entire context (you can omit the comments) to a new open page in a text editor on your local computer
  • Next, search for the string [from-did-direct] (including the brackets)
  • Also copy that entire context (without the comments) to the same open page in your text editor
  • You MAY also need to copy the context [macro-hangupcall] which is found in /etc/asterisk/extensions_additional.conf. You would do that the same way as the previous two. I don’t show it in the examples below, but further down I’ll explain why you might need to do that one.

At this point the open page in your text editor will look something like this, although this may vary depending on your version of FreePBX. It is important that you copy these contexts from YOUR system, and repeat the procedure every time you upgrade FreePBX to a newer version:

[from-internal] include => from-internal-noxfer
include => from-internal-xfer
include => bad-number ; auto-generated

[from-did-direct] include => ext-findmefollow
include => ext-local

Now add these two lines to the bottom of EACH of the above two contexts (or all three if you also added [macro-hangupcall]):

exten => h,1,Macro(missed-call)
exten => h,n,hangup

The above is the part you will need to redo if you upgrade FreePBX. Next, add a new macro that looks something like this (this is just an example, don’t copy it verbatim!):

[macro-missed-call]
exten => s,1,NoOp(Checking to see if we need to send a missed call notification)
exten => s,n,GotoIf($["${CT_EXTEN}" > "1121" & "${CT_EXTEN}" < "1125" & "${CT_EXTEN}" != "" & "${ANSWEREDTIME}" = ""]?notify1)
exten => s,n,MacroExit()
exten => s,n(notify1),NoOp(Sending notification of missed call on extension ${CT_EXTEN})
exten => s,n,ExecIf($["${CALLERID(num)}" > "1121" & "${CALLERID(num)}" < "1125"]?MacroExit())
exten => s,n,ExecIf($["${CALLERID(num)}" = "8005551212"]?MacroExit())
exten => s,n,JabberSend(asterisk,username@gmail.com,Missed call from ${CALLERID(name)} at ${CALLERID(num)} for extension ${CT_EXTEN} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,MacroExit()

Here’s an explanation of what’s happening in the above Macro, so you can modify it for your specific needs:

  • Line 1 is just a NoOp that tells what the macro is doing.
  • Line 2 says that if the call was for extensions 1122 through 1124 (greater than 1121 and less than 1125), that we didn’t get a null value for the extension, and the call was not answered, go to the label notify1. If we only wanted to test for a single extension such as 1122, instead of “${CT_EXTEN}” > “1121” & “${CT_EXTEN}” < “1125” just use “${CT_EXTEN}” = “1122”. You can add as many lines as you need for various extensions, just be sure to jump to different labels.
  • Line 3 says that if none of the above conditions match, exit the macro.
  • Line 4 is just a NoOp that serves as a placeholder for the label and prepares to send the notification.
  • Line 5 is optional and shows how to exclude notifications for calls if the calling number is within a range, in this case 1122-1124 (so, in this example no notifications would be made on calls from one of your own extensions to another in the 1122-1124 range). You can use as many of these lines as you need to exclude more ranges, or none at all if you don’t want to exclude any ranges. Make sure you get the quotation marks right (and again, if you copy lines from this blog, beware of the quotation marks that WordPress has “prettified” – you’ll need to fix them before the lines will work!).
  • Line 6 is optional and shows how to exclude a specific number from triggering the notification. In this case, if the call comes from 8005551212, no notification would be sent. Note that you could use the ${CALLERID(name) variable as a trigger also, and exclude calls from certain names from triggering a notification. Note you can place multiple tests in the same expression by using the bar character (logical OR in Asterisk) to connect them, for example, “${CALLERID(num)}” = “8005551212” | “${CALLERID(name)}” = “INFORMATION” would match if either condition were true. This line can be omitted completely if you don’t want to exclude calls from any specific numbers.
  • Line 7 is where we actually send the notification. Basically any of the methods mentioned in the previous part of this article can be used here, just replace ****1122,1, in the example lines with s,n,
  • Line 8 is where we gracefully exit the macro. Of course, you can repeat lines 4-7 as many times as needed if you are doing notifications for more than one extension.

Now that you have the three (or four) macros in your text file and edited as they should be, copy the whole mess and paste it into the existing file /etc/asterisk/extensions_override_freepbx.conf — do not just overwrite the existing file! Instead, open it with nano or another text editor and paste in the lines you’ve been working on. Also, do a quick scan to make sure you haven’t duplicated a context that’s already in there (the likelihood of that is pretty remote unless you have done something like this in the past). Note that after you do a FreePBX reload, the contexts in this file will replace the equivalents in the files generated by FreePBX, so that’s why you have to check that the originals haven’t changed each time you upgrade FreePBX.

If you want to know what variables are available for you to trigger on, instead of a line sending a notification you can use a line like this:

exten => s,n,DumpChan

This will show you a lot of information, including how several of the channel variables are set.

One final note, the added macro call under [from-internal] is the one that would be taken after a failed call from another extension, while the one under [from-did-direct] is the one used by failed calls coming in from outside the system. Note that most answered calls (not including ones that go to voicemail) will wind up in [macro-hangupcall] and not go through either of those, BUT in certain circumstances unanswered calls might also end via [macro-hangupcall], such as if they go through a ring group. That is why I mentioned above that you might need to include [macro-hangupcall] along with the other contexts copied into /etc/asterisk/extensions_override_freepbx.conf. However, I have not tested every possible condition, and I do know that some things won’t work — such as if multiple extensions are rung simultaneously, no extension number shows up in the ${CT_EXTEN} variable, so those tests in [macro-missed-call] that depend on that variable being set will fail. You may be able to modify the macro so that it will work in such circumstances by using one or more variables you can find by using DumpChan, but that’s a piece of dialplan I’m not prepared to write, since I personally have no need for it (and have already spent way too much time on this)!

I probably should also include this note about the use of extensions_override_freepbx.conf as taken from this page:

If extensions.conf (or extensions_additional.conf) has a context or macro that you NEED to modify, you place that code here as asterisk will only execute the first occurrences of that code and ignores other occurrences. This file will not be overwritten. Be very careful as replacing an existing piece of code this way is the fastest possible way to break your system. If you are doing this you should probably think about filing for a feature request or bug fix to get it addressed properly.

Of course, my experience is that filing a feature request with FreePBX is a great way to either be ignored, or lectured by some a**h*le on why you don’t need that feature, so I wouldn’t bother. Just be sure that if you upgrade FreePBX, or even just the Core or Framework modules, you check to make sure that the contexts you’ve copied into extensions_override_freepbx.conf haven’t been changed (or if they have, make sure you copy over the changes).