How to use Google Voice with FreePBX and Asterisk without using XMPP or buying new hardware

This post has been edited to include new and changed information.

Unfortunately, the original method described below worked for a time in mid-2018. It has not worked at all since then, for one simple reason: Something has changed on Google’s web site and the PyGoogleVoice script that is central to this method no longer works, period. If you run it from the command line, you just get a bunch of errors.

The source code for PyGoogleVoice is here. If you are conversant in Python, maybe you can fork the project and make a version that works. If so, it would be much appreciated if you would share your working version. There is a discussion here that may be informative, but it appears further development has stalled.

Or, if you are conversant in PHP, there is a PHP script posted here that works in a similar manner to PyGoogleVoice. Maybe you can modify it and get that one to work, and if so again it would be appreciated it you’d post the working version.

Or, there is something called selgooglevoice which is “A Selenium with Python replacement for pygooglevoice.” If someone could post detailed instructions for getting this to work, it would be appreciated. If it is using Selenium as the name implies, then it may be that you’d need to have a web browser running, which of course is not possible on a typical Asterisk/FreePBX server with no desktop environment. This is a case where code exists, but there is pretty much zero information on how to use it. EDIT: Jeff Dobkin posted a comment about using selgooglevoice on June 4, 2021 in the comments section, so apparently at least one person has it working! EDIT 2: It may be possible to use Selenium with a “headless” browser which would NOT require a desktop environment. See Setup Selenium with Python and Chrome on Ubuntu & Debian for possible installation instructions – we have NOT tested this but please leave a comment if you get it to work!

Perhaps the most promising potential solution that I have come across so far is contained in this thread about UI.Vision RPA. As shown in December 2023, it will only work with a single Google Voice account, and you must have the Google Chrome browser up and running and you must already be logged into Google Voice. Given the relative simplicity of the script, it might not be that difficult to modify it to support logging into Google Voice, setting up the call, and then logging out (so that multiple Google Voice accounts could be used). However, keep in mind that Google occasionally throws a curve ball during the login process by bringing up an unexpected screen or popup (usually related to security), so you would need to be able to detect and/or bypass that, and also I have read in various places that Google attempts to detect and block automated logins, and if that is the case it may make logging in a lot more difficult than I had originally envisioned. And, as with selgooglevoice, you would probably want to have Google Chrome (or Chromium, or probably any Chrome-based browser that will work with the UI.Vision RPA extension) running on the same system as Asterisk and FreePBX, which would probably mean you’d need to use a desktop version of Debian (or whatever Linux distro you use), although it could possibly be a very lightweight desktop environment (unless perhaps you can figure out how to make “headless Chrome” or “headless Chromium” work in this application). If you can get this to work, you may be able to use it in a similar manner to the way PyGoogleVoice was used in the instructions below, but it probably won’t be nearly as easy to set up.

I had previously suggested that you might also want to keep an eye on this thread, GV Outbound Calls After EOL in the hope that something useful might be posted there. Another thread that was started by the same person is here: GV Callback script. The major issue with the solution posted in those threads is that the script shown only runs on Windows, which of course means it cannot be run on the same system as Asterisk and FreePBX (and for many users involves paying for a Windows license). And given the Windows propensity to do system upgrades at inopportune times, this is not a solution I would trust.

If no one is able to fix any of these scripts (to support logging into Google Voice, setting up the call, and then logging out), the method shown below will not work at all, ever, period. We now seem to have entered an era where no one wants to post complete instructions on how they get something like this running, or if they do the major search engines won’t index it (for example, as I write this there are no links on either Google or Bing to the thread about UI.Vision RPA that I linked above). I have been searching in vain for a web article or video that would show all the steps involved in getting something like this working with Asterisk and FreePBX, to no avail. There are pieces here and there, but no one is combining those pieces and explaining how they did it. Even this article is rarely accessed anymore, though that is understandable because I’m not able to provide a complete solution here.

Up until mid-December of 2023, the only semi-reliable method for connecting FreePBX to Asterisk involved the use of an Obihai/Polycom device 🤮, for example using the method shown here to connect Google Voice to FreePBX. But now the OBi200, OBi202 and OBi212 are considered obsolete, and if you have not already set one up to work with Google Voice you are probably out of luck, first because they are no longer being sold, but even if you could obtain one, the Obitalk configuration portal no longer supports those devices, so you cannot now set them up to use Google Voice. And if your device configuration is ever lost for any reason, you will not be able to add your Google Voice settings back in.

The only other possible option available now for such devices that were not configured while the Obitak portal was still functional or that have lost their configuration (which, by the way, is an option that the self-proclaimed “Google Voice Product Expert” you may encounter in various forums will never tell you about, nor allow anyone else to tell you about in the forums he moderates) is to use third party firmware and then read this very long thread for further instructions. This will require you to have a certain degree of technical prowess (and to assume responsibility if you manage to put your Obihai/Polycom device into a completely non-functional state). But, ultimately no one knows how long the OBi devices that still support Google Voice will continue to do so, so if you don’t already have an Obihai/Polycom device that is working with Google Voice, I do not advise trying to obtain one – it could be a lot of hassle for something that will only work for a short time, if it works at all.

So, it appears those are your two choices if you want to continue using Google Voice with FreePBX – use hardware that is now considered obsolete and that has already been set up to use Google Voice prior to mid-December, 2023 or figure out how to make one of the scripts work. There is a third choice mentioned on some other sites (naf’s GVSIP) but if you go that route you’ll find that it’s a bit of a pain in the butt to set up and get working unless you are very experienced (though maybe easier than any other solution proposed above), and you run the very real risk that Google will shut down your Google Voice account if they catch you using it, though to my knowledge so far no one has reported that their account has been shut down for that reason.

I’m leaving this article here in case someone does manage to rewrite one of the scripts, but please be aware that if you attempt to do this, I’m sorry to say that you’ll probably be disappointed with the results if you don’t or can’t fix one of the scripts so that it works reliably. If you do, please leave a comment and share how you did it! Also please be aware that much of the information in the text below has become obsolete. For example, Callcentric no longer offers free DID’s; their price for a residential DID is currently $1/month in December, 2023 (you may be able to get similar deals from other providers).


In mid-July, 2018 Google Voice shut down XMPP access. The problem is that if you have been using the Google Voice module in FreePBX, it used the XMPP protocol to connect to Google Voice, and that’s what no longer works.

Various possible solutions are being offered, including buying a new VoIP device and using it as a bridge between Asterisk and Google Voice. Unfortunately, at the time of this writing such devices are made by only one manufacturer, Obihai, a company I could not possibly have a lower opinion of. I used to like and support Obihai, but they have nuked any warm and fuzzy feelings I had for them in the past, several times over. My opinion now is that if you buy a new Obihai device, then P.T. Barnum was right when he said that there’s a sucker born every minute, and when you were born you fulfilled the quota for that minute! But, this is not intended to be a rant about Obihai (you can find those all over the VoIP forums, except in Obihai’s own heavily censored forum), but rather a way to keep using Google Voice with FreePBX and Asterisk, so you don’t need to buy any new hardware.

The FreePBX Google Voice module has become useless, so this method may be a bit more “hands on” than you are used to. To cut to the chase, the basic technique can be found in one of RonR’s posts in the above-mentioned DSLReports thread. Go ahead and open that up in another tab in your browser, because I’ll be referencing it throughout this article. But, that post only provides some basic configuration information, and makes some assumptions that are not clearly stated, such as that you are using FreePBX and that you already know how to perform basic administrative tasks such as creating a SIP trunk. While the general technique shown will also work with bare Asterisk (no FreePBX) or with other Asterisk-based GUI’s, you’re pretty much on your own in making the necessary dialplan modifications, or you could try following Twinclouds’ instructions (as mentioned in his comment on this article), which use a mostly similar but slightly different technique.

I should probably warn you here that this technique could potentially stop working at any time. It’s worked for years now, but when dealing with Google that doesn’t necessarily mean anything. There have been reports in late 2018 that although incoming Google Voice calls always seem to complete, outgoing calls to Google Voice will sometimes fail repeatedly for a period of time, particularly during daytime hours. Also, by publishing this I am explicitly NOT commenting on whether Google would approve of its use – I don’t work for Google, and I’m not a lawyer. If you suspect that using this technique may violate Google’s Terms of Service or some such thing, then please don’t use it until you consult with your own attorney. If you try anything shown in this post you are doing so of your own volition and at your own risk. I will not be responsible for any consequences that may occur. With that in mind, if you still want to try this technique, here’s what you need to do.

First, you must have a number coming into your system that is NOT a Google Voice number. This is sometimes referred to as a DID (and will be called that in this article), and if you don’t already have one, it is possible to get free DID’s from Callcentric. You need to have at least one DID coming into your Asterisk server, and you need to configure it as a destination (forwarding number) in your Google Voice account. So get your DID working first, and make sure you can make incoming calls to it using its assigned number. Then go into your Google Voice account(s) and set it/them up to forward calls to the DID. If you have already set up a forwarding number at some point in the past, you may still want to log in to Google Voice and check to make sure it doesn’t need to be re-validated – if you have let it lapse into an unvalidated state, Google Voice won’t forward calls to it until you re-validate it. If you tell Google Voice that the number is a “Work” number (you may need to go into “Legacy Google Voice” to do that), it can be used as a forwarding destination for at least two Google Voice accounts, and it is possible use one DID with two Google Voice accounts. So once you have your DID set up, you should be able to call your Google Voice number, and it will forward the call to your DID, and then the DID will send the call on to your Asterisk server. Note: If using Callcentric, you may wish to refer to this post: How to receive incoming Callcentric calls in FreePBX without creating multiple trunks.

Assuming you have your incoming DID working and Google Voice configured to use it as a destination, and you have tested it to make sure that the forwarding to your DID works when you call your Google Voice number, the next thing to do is is make a test call using pygooglevoice. But first you should determine whether it is installed on your system. From a Linux command prompt, enter which gvoice – if it returns a path such as /usr/bin/gvoice then it is already installed. If no path is returned, then install it using the instructions in RonR’s post mentioned above. EDIT: I’m interrupting this to note that RonR released a newer version written in PHP rather than Python, called gvcallback, which is an adaptation of an earlier program (you can read the entire thread if interested). However the syntax for calling the program isn’t exactly the same as with PyGooglVoice (though it’s very close) so you’d need to make some adaptations to the following instructions to account for the different syntax. The possible advantage of using the gvcallback program is that you don’t have to install all the dependencies that you have to install to get PyGoogleVoice to work. However, if you already have PyGoogleVoice installed on your system, there’s no compelling reason to switch to gvcallback that I’m aware of. Continuing on with the article…

Once PyGoogleVoice is installed, from a Linux command prompt, enter gvoice – it should prompt you for your Google Voice email address and password. If it connects successfully, you will see a prompt that looks like this: gvoice>. If you see a bunch of error messages (this is not unusual on the first run), refer to this DSLReports post by Cam_, which shows some of the things you may need to do to get it working. In addition, you may also need to let less secure apps access your Google account. (EDIT: Also see this DSLReports post by AllThumbs which notes that in addition to allowing less secure apps using this link, while logged into your Google Voice account and just prior to running pygooglevoice you may also need to visit this “Allow access to your Google account” link to get it working. Immediately after you click the “Continue” button, run pygooglevoice so that Google knows which “new device or application” you are trying to allow access from. Then, log into Gmail using your Google Voice account and check the Inbox – there will probably be an email from Google asking you to confirm that it was really you that just logged in, if so, do whatever the email tells you to do to confirm it. AllThumbs’ DSLReports post implies that you may have to do this again from time to time, and that will be particularly true if you don’t respond to any email Google may send asking for confirmation that it was really you, so save notes on this process. Yes, Google’s ultra-paranoid security can be a real pain in the butt sometimes).

If you think you may have a bad or old version of pygooglevoice, prior to reinstalling remove the following files from the /usr/bin directory: asterisk-gvoice-setup, gvi, and gvoice, and also check any /user/lib/python*/site-packages directories you may have for a directory called googlevoice and/or a file that starts with “pygooglevoice” and if you find either of those, remove those as well. Otherwise you may wind up with a mixture of files from the old and new versions, and that usually won’t work (also, do not run asterisk-gvoice-setup, it’s not meant for what we’re doing here).  Note that there is a current version of pygooglevoice included in a download link in RonR’s post.

If you do get the gvoice> prompt, then enter the word call, and then it should prompt you for the outgoing number, which can be almost any valid 10 digit number in the USA or Canada. After that, it will prompt you for “Forwarding number [optional]:” but it really isn’t optional. What it is wanting here is one of your validated Google Voice destination numbers, such as your DID – NOT your Google Voice number! – and it wants it in the format +1 plus the ten digit number. If you try using just the ten digit number alone, it won’t work (at least not in my tests), it has to be in the form +1XXXXXXXXXX. Finally it will ask for the phone type; if you specified the number as a “work” number in the Google Voice interface then use 3. If you don’t remember how you specified it, try all three of the digits 1 through 3, one at a time – one of those should work. It may not actually matter if this number is correct in some cases; I’ve seen “1” work when the number had been specified as a “Work” number, for which the code should be “3”, but that’s not always the case.

If you entered everything correctly, you should not see any error messages, and in second or two a call should come in on your DID (hopefully you have created an Inbound Route, and made a nearby extension the temporary destination), and when you answer it you should hear the destination number start ringing. This part MUST work before you go any further – if you can’t get this to work then there is no point in doing any further configuration, because it won’t work either.

If it does work, you have a choice at this point. You can follow RonR’s instructions verbatim, which are probably fine if you are only dealing with one or two Google Voice accounts. But if you are dealing with a higher number of accounts, or if you just don’t like the fact that he hardcodes Parking Lot extension numbers, or if you don’t care to install and configure the Parking Lot module, you should know that I have made some modifications to his method, which I have added at the bottom of this article. For now you can do it either way, but be aware that because he hardcodes Parking Lot extensions, it will be impossible to place two calls on the same Google Voice account at the same time. However it may be impossible to do that anyway, if Google limits the number of simultaneous connections per account to three or fewer. Remember that with this method, there are actually two calls being placed from your account for each call you make – one to call your DID, and one to connect your call through to the called party.

I prefer not to use the Parking Lot at all, and instead to use Asterisk’s Bridge application, which I feel is much more suitable for this purpose, as I will explain in a moment. I do have some additional notes on using RonR’s original method, but I have moved them to the bottom of this article to make it a bit easier to read.

There is a caveat here. If Google changes their authentication protocols, this method could stop working until a pygooglevoice maintainer comes up with a patch. It’s happened before, though rarely, and it could happen again. If it does, incoming calls would still work, but outgoing calls would fail until the pygooglevoice code is modified to account for the change.

One other possible drawback to this method is that because pygooglevoice is considered a “less secure” app, Google may nag you with a “Security Alert” email every single time you place a call! One would hope that after you’ve confirmed that it was really you a few times, the emails would at some point stop, but if they don’t there is a way to turn them off – however in that case you won’t be notified if someone else really does try to hack your account. This is the method, according to Google:

We strongly advise leaving alerts on so you can hear about suspicious activity on your account. If you still want to disable alerts, follow the steps below.

Note: It takes about a week for alerts to get turned off. This is because Gmail wants to confirm it’s you that’s turning them off, and not someone else who might have access to your account.

  1. On your computer, open Gmail.
  2. In the bottom right, click Details.
  3. At the bottom of the page next to “Alert preference,” click Change.
  4. Select Never show an alert for unusual activity.

I have discovered that you don’t see the “Details” link unless you switch to the “basic HTML” view.



Here is my revision of RonR’s method – this uses Asterisk’s Bridge application, rather than the Asterisk Parking Lot. The advantage of using Bridge is that you don’t have to deal with the Parking Lot at all (you don’t even need to have the FreePBX Parking Lot module installed), and therefore the number of simultaneous calls is not limited to the number of available Parking Lot slots. Also you don’t have to worry about the changes in the “Park” application syntax between Asterisk versions. And, this method is better about cleaning up after itself, by removing database entries it has created after they are no longer needed. The only disadvantage is that the custom dialplan is slightly more complex.

If for some reason you really prefer to use the Asterisk Parking Lot (which I do NOT recommend), I do show alternative contexts that you can use, that more gracefully deal with a larger number of Google Voice accounts because they assign Parking Lot extensions starting with the highest available, and working down until one is found that is not in use. They are set to start with extension 78 and work down to 71. If you think there might ever be more than eight simultaneous outgoing Google Voice calls then you may need to extend the range of available Parking Lot extensions, but I think this will work for most people.

The instructions for installing PyGoogleVoice are the same (so refer to his post for that), but here’s how I configured it – note that values that you must change are in bold type:


Connectivity -> Trunks -> Add Custom Trunk

Trunk Name : username
Custom Dial String : LOCAL/$OUTNUM$@custom-username-out

Trunk Name : user2name
Custom Dial String : LOCAL/$OUTNUM$@custom-user2name-out

… and so on …

To help you keep the accounts straight, the user names should be the the same as the part of the Google Voice login names before the @ symbol. So if the login name is foo@gmail.com, the username would be just foo. This will be the case wherever you see “username” or “user2name” in bold.


Connectivity -> Outbound Routes -> Add Route

Route Name : username
Dial Pattern : see discussion below
Trunk Seq : username

Route Name : user2name
Dial Pattern : see discussion below
Trunk Seq : user2name

… and so on …

RonR only showed the dial pattern NXXNXXXXXX but that is only valid if your system only allows 10 digit dialing, and it doesn’t restrict a route to use by a particular extension. Possible patterns you might want to use include, but are not limited to:

NXXNXXXXXX/eee (where eee is a specific extension number that can use this route, and you can use patterns here such as 10X for all extensions in the range 100 to 109, or multiple instances of this line if needed for non-contiguous extensions).
1|NXXNXXXXXX/eee (same as above except allows dialing calls with a leading “1”, in case you allow 11 digit dialing)
aaa+NXXXXXX/eee (same as above except allows 7 digit calls to a particular area code, in case you allow 7 digit dialing – replace aaa with the area code)

The basic point here is that Google Voice expects to receive the called number as 10 digits, with no leading 1.


Admin -> Custom Destinations -> Add Custom Destination

Custom Destination : custom-NXXNXXXXXX-in,s,1
Description : Incoming calls NXXNXXXXXX

You need one Custom Destination for each DID you have coming into your system that is used by Google Voice. Replace “NXXNXXXXXX” with the 10 digit DID number, NOT your Google Voice number. Again, this change is just for your convenience in keeping everything straight.


Connectivity -> Inbound Route -> Callback Number
DID Number : NXXNXXXXXX
Destination : Custom Destination -> Incoming calls NXXNXXXXXX

Here you are making an Inbound Route for your DID, and sending it to the Custom Destination that contains the phone number of the DID, that was created in the previous step. So again, you need one of these Inbound Routes for each DID you have coming into your system that is used by Google Voice. In this case the Description can be anything you find meaningful. Leave ALL the other settings at the default – in particular do not enable “Detect Faxes” or “CID Superfecta”, or anything that might slow processing of the incoming call. If you need any of those features in incoming calls, you can enable them in the next section:


Connectivity -> Inbound Route -> Add Incoming Route

DID Number : NXXNXXXXXX*
Destination : Normal destination for this number

Here the DID number is the same as in the previous step except there is an added * character at the end. This is the Inbound Route for your DID that is used when it is NOT a Google Voice callback that arrives because you are making an outgoing call. In other words, if someone other than Google Voice calls your Google Voice number, or calls the DID that your Google Voice number is forwarded to directly, the call will wind up here, so normally you’d want the destination to be an Extension, Ring Group, IVR, or whatever. Since by the time the call gets here you know it’s NOT a Google Voice callback, you can enable features like Detect Faxes or CID Superfecta if you like. Again, the Description can be anything you find meaningful.


Additions to /etc/asterisk/extensions_custom.conf – first, the common contexts used by all accounts – these contexts are only added once, no matter how many Google Voice accounts or DID’s you have. I suggest using the “bridge” method rather than the Parking Lot method:

[custom-gv-out-common]
exten => _X.,1,System(gvoice -b -e ${ACCTUSER} -p ${ACCTPASS} call ${EXTEN} +${RINGBACK} 1 &)
exten => _X.,n,Set(DB(gv_dialout_${CUT(ACCTUSER,@,1)}/channel)=${CHANNEL})
exten => _X.,n,Wait(25)
; The following Noop lines are NOT optional, do not delete them
exten => _X.,n,Noop(Never received callback from Google Voice on channel ${DB_DELETE(gv_dialout_${CUT(ACCTUSER,@,1)}/channel)} – exiting)
exten => _X.,n,Goto(custom-gv-error,s,1)
exten => h,1,Noop(User hung up while waiting for callback from Google Voice on channel ${DB_DELETE(gv_dialout_${CUT(ACCTUSER,@,1)}/channel)})
exten => h,n,Macro(hangupcall,)

Do NOT remove the Noop lines, they contain the function that deletes the unused database item if for some reason the returned call from Google is never received, or if the user hangs up before the callback is received. If you want your callers to hear Music on Hold rather than silence while waiting for the callback, replace

exten => _X.,n,Wait(25)

with

exten => _X.,n,Answer
exten => _X.,n,MusicOnHold(default,25)

You do have to “answer” the call or the music will not be heard. You can optionally replace “default” with any other valid Music on Hold context you have created.

IF FOR SOME REASON YOU PREFER TO USE THE PARKING LOT METHOD (NOT RECOMMENDED), then use this context instead:

[custom-gv-out-common]
exten => _X.,1,Set(PARKINGEXTEN=79)
exten => _X.,n(parkloop),Set(PARKINGEXTEN=$[${PARKINGEXTEN} - 1])
exten => _X.,n,GotoIf($["${EXTENSION_STATE(${PARKINGEXTEN}@park-hints)}" != "NOT_INUSE"]?parkloop)
exten => _X.,n,GotoIf($["${PARKINGEXTEN}" < "71"]?custom-gv-error,s,1)
exten => _X.,n,Set(DB(gv_dialout_${CUT(ACCTUSER,@,1)}/parkslot)=${PARKINGEXTEN})
exten => _X.,n,System(gvoice -b -e ${ACCTUSER} -p ${ACCTPASS} call ${EXTEN} +${RINGBACK} 1 &)
exten => _X.,n,Park(default,t(25)c(custom-gv-error,s,1)rs)
;exten => _X.,n,Park(15000,custom-gv-error,s,1,rs)
exten => h,1,Macro(hangupcall,)

This is the custom-gv-out-common context as used in the “Parking Lot” method. In the first line, 79 is one greater than the highest numbered Parking Lot extension (by default the Parking Lot uses parking extensions 71-78). You may need to change that if you increase the number of Parking Lot extensions. Also, I may be wrong but I believe that for the “${EXTENSION_STATE(${PARKINGEXTEN}@park-hints)}” function to work correctly, the “BLF Capabilities” setting in the FreePBX Parking module must be set to Enabled. While you’re in the Parking configuration, you may wish to set the “Pickup Courtesy Tone” option to None if you don’t want the beep tone when the callback arrives.

If you have an older version of Asterisk you might need to switch which of the “Park” lines is commented out – the one that is uncommented now is for newer versions of Asterisk. Also in the Park lines, the “r” in the “rs” signifies that you want fake ringing generated while waiting for the callback – if you omit it, it will play Music on Hold as specified in the “Parked Music Class” option in the FreePBX Parking module.


Two more additions to /etc/asterisk/extensions_custom.conf – Again these contexts are added only once. The second one is needed only if you are using the “bridge” method, which I recommend:

[custom-gv-error]
exten => s,1,Playback(silence/1&cannot-complete-as-dialed)
exten => s,n,Wait(1)
exten => s,n,Playtones(congestion)
exten => s,n,Wait(10)
exten => s,n,StopPlaytones
exten => s,n,Hangup()
exten => h,1,Macro(hangupcall,)

The above is straight from RonR’s original instructions except for the hangup handler at the end; other than that only the context name is changed. When everything is working as it should, this context should rarely be used.

[custom-gv-cancel]
exten => s,1,Answer
exten => s,n,Wait(1)
exten => s,n,Hangup()

The only real drawback of the “bridge” method is that if a user changes their mind about placing a call, and hangs up before the callback is received from Google Voice, Asterisk doesn’t know what to do with the callback unless you explicitly tell it what to do. So this answers the call and then hangs up, after a one second delay. In previous incarnations of this method there was a “cancel” option in pygooglevoice that could be used to cancel a call. The “cancel” function still seems to exist in pygooglevoice, but in testing it doesn’t seem to actually do anything, at least not for several seconds. I watched as Asterisk repeatedly sent the pygooglevoice cancel, only to see the call come right back in, and that loop lasted several seconds! So, it appears that the only reliable way to cancel the callback is to answer it and then hang up. If you want to experiment with the pygooglevoice cancel option, the syntax is simply:

exten => s,n,System(gvoice -b -e username@gmail.com -p password cancel &)

But if you can get it to work, you are having better luck than I did.


User-specific additions to /etc/asterisk/extensions_custom.conf – You must create these contexts for each user account or DID, as explained below:

[custom-username-out]
exten => _X.,1,Set(ACCTUSER=username@gmail.com)
exten => _X.,n,Set(ACCTPASS=password)
exten => _X.,n,Set(RINGBACK=1NXXNXXXXXX)
exten => _X.,n,Goto(custom-gv-out-common,${EXTEN},1)
exten => h,1,Macro(hangupcall,)

You need one of the above for each Google Voice account, and you must change the username value in both places (don’t skip the context name) and the password value, and set the RINGBACK setting to the 11-digit DID number associated with this account (NOT your Google Voice number). “username” is just the part of the Google Voice login name before the @ symbol, same as in the Custom Trunks. If you are one of the very few people who has a Google Voice account where the login name doesn’t end in @gmail.com, then change that in the ACCTUSER setting as well.

[custom-NXXNXXXXXX-in]
exten => s,1,GotoIf($["${CALLERID(number)}" = "Google Voice Number"]?cb1)
;exten => s,n,GotoIf($["${CALLERID(number)}" = "Google Voice Number 2"]?cb2);
exten => s,n,Goto(from-trunk,NXXNXXXXXX*,1)
exten => s,n(cb1),NoCDR()
exten => s,n,GotoIf($[${ISNULL(${DB(gv_dialout_username/channel)})}]?custom-gv-cancel,s,1)
exten => s,n,Bridge(${DB_DELETE(gv_dialout_username/channel)})
exten => s,n,Hangup()
;exten => s,n(cb2),NoCDR()
;exten => s,n,GotoIf($[${ISNULL(${DB(gv_dialout_user2name/channel)})}]?custom-gv-cancel,s,1)
;exten => s,n,Bridge(${DB_DELETE(gv_dialout_user2name/channel)})
;exten => s,n,Hangup()
exten => h,1,Macro(hangupcall,)

This uses the “bridge” method, which I recommend. You need one of the above for each of your DID’s that Google Voice sends calls to. You can have one or two Google Voice accounts coming to a DID if you have specified the DID as a “Work” phone in both accounts. If you have two, then uncomment the five commented-out lines. As usual, replace both instances of NXXNXXXXXX with the DID number (don’t skip the context name, and don’t lose the * character at the end of the number in the Goto line). “Google Voice Number” and (optionally) “Google Voice Number 2” should be replaced by the Google Voice numbers associated with your accounts in 10-digit format (no leading +1 here). “username” and (optionally) “user2name” are once again just the part of the Google Voice login names before the @ symbol – make sure to change them in both lines where they appear.

IF FOR SOME REASON YOU PREFER TO USE THE PARKING LOT METHOD (NOT RECOMMENDED), then use this context instead:

[custom-NXXNXXXXXX-in]
exten => s,1,GotoIf($["${CALLERID(number)}" = "Google Voice Number"]?cb1)
;exten => s,n,GotoIf($["${CALLERID(number)}" = "Google Voice Number 2"]?cb2);
exten => s,n,Goto(from-trunk,NXXNXXXXXX*,1)
exten => s,n(cb1),ParkedCall(default,${DB_DELETE(gv_dialout_username/parkslot)})
;exten => s,n(cb2),ParkedCall(default,${DB_DELETE(gv_dialout_user2name/parkslot)})
exten => h,1,Macro(hangupcall,)

This is the custom-NXXNXXXXXX-in context as used in the “Parking Lot” method. You need one of the above for each of your DID’s that Google Voice sends calls to. You can have one or two Google Voice accounts coming to a DID if you have specified the DID as a “Work” phone in both accounts. If you have two, then uncomment the two commented-out lines. As usual, replace both instances of NXXNXXXXXX with the DID number (don’t skip the context name, and don’t lose the * character at the end of the number in the Goto line). “Google Voice Number” and (optionally) “Google Voice Number 2” should be replaced by the Google Voice numbers associated with your accounts in 10-digit format (no leading +1 here). “username” and “user2name” are once again just the part of the Google Voice login names before the @ symbol. In the last two lines, in the ParkedCall options, you may need to omit the string “default,” if you are using an older version of Asterisk, because at some point prior to the current version the order of the items was flipped. In current versions the Parking Lot name is specified first, followed by the Parking Lot extension (which in this case is retrieved from a temporary database location), but in older versions the Parking Lot extension was given first, and then optionally the Parking Lot name.


EDIT: When using this method, you may run into a weird issue if you set one or more of your Inbound Routes for DID’s used by Google Voice to “Force Answer” under the Advanced tab, as you might if you are trying to keep calls from ever going to Google’s voicemail, or if your calls are answered immediately by an announcement or an IVR or by an Asterisk voicemail greeting or something similar. What happens is that when Google Voice forwards a call to a DID, and it’s not a “callback” of the type that the system receives when placing an outgoing call, you cannot answer the call too quickly. If you do, you will hear nothing but silence while the caller will continue to hear ringing until the call is sent to Google’s voicemail, which is in many cases exactly what you are trying to avoid.

In FreePBX inbound routes there is a setting called “Pause Before Answer” but for whatever reason it doesn’t seem to fix this problem. I think perhaps that may be because it doesn’t send a Ringing signal back to Google Voice, and Google Voice apparently really wants to see a couple seconds of Ringing before it will let the call audio go through!

So the only solution is to create a custom context in /etc/asterisk/extensions_custom.conf and then use that as the Context in the trunk associated with the provider of your DIDs. Here’s an example context:

[custom-from-my-provider]
exten => DID1/GV1,1,Goto(from-pstn-e164-us,${EXTEN},1)
exten => DID1/GV2,1,Goto(from-pstn-e164-us,${EXTEN},1)
exten => DID2/GV1,1,Goto(from-pstn-e164-us,${EXTEN},1)
exten => DID2/GV2,1,Goto(from-pstn-e164-us,${EXTEN},1)
exten => _X!,1,Ringing()
exten => _X!,n,Wait(2)
exten => _X!,n,Goto(from-pstn-e164-us,${EXTEN},1)
exten => h,1,Macro(hangupcall,)

The four lines that include DIDx and GVx are just examples – you would replace DIDx with your DIDs that you use with Google Voice (only the ones that are used for Google Voice callbacks when you are placing outgoing calls), and replace GVx with the one or two Google Voice numbers you have forwarded to that particular DID. So you might have only one such line, or you might have a few, depending on how many Google Voice numbers come into your system. The purpose of those lines is to avoid the delay when placing an outgoing call, because in that specific case it’s apparently not needed. The rest of the context puts the trunk in a Ringing state, delays two seconds, and then lets the call proceed to its ultimate destination. If your current trunk Context is not “from-pstn-e164-us” you can substitute whatever context it’s currently using in all the above lines where that context is referenced.

Change the Wait value if two seconds isn’t sufficient. This seems to be somewhat of a regional thing – in certain areas it seems no delays are needed at all, while in other areas an even longer delay might be required. Test using different delay values until you find the shortest one that lets your calls arrive reliably. Two seconds seems to be good for many people, but maybe not for all.



I’m including the following additional hints and information for those that for whatever reason really would prefer to user RonR’s original instructions, rather than the instructions shown above. I actually wrote these notes prior to documenting the above method. A small subset of these comments may still be applicable to the above method, but others clearly will not be applicable.

RonR shows two contexts, pgv-out-1 and pgv-out-2. First of all, I like to rename those to the Google Voice account names (the part of the email address before the @gmail.com) followed by -out, but if you are going to do that, you need to change those strings in all the other places where they appear in his instructions. If you have several accounts, using more meaningful context names could make it easier to know which account you are dealing with (I do this in my revisions above). And, you don’t need the second context (pgv-out-2) unless you are using the same DID as the forwarding destination for two Google Voice accounts. In the contexts themselves, you need to replace Usernamen@gmail.com and Passwordn with the actual login username and password for the account, and you must replace Callback Number with the actual 11 digit number of your DID (you do use the leading “1” here, but in this case the “+” character is added for you). The Callback Number is NOT your Google Voice number!

Also, the CALLPARK numbers must be unique in each context. You can see (and change) the allowable range of Parking Lot numbers by going into Applications | Parking. If you can’t find that selection, you may need to install the FreePBX Parking Lot module. Note that if you actually use the Parking Lot feature for something other than Google Voice, then make sure you use the highest available Parking Lot numbers in these contexts. If, probably like many users, you’re asking “What’s a Parking Lot doing in my PBX?” and have never used it and can’t imagine why you would, then you can use any of the allowable range of parking lot numbers (71 through 78 by default) in the CALLPARK settings.

In the pgv-out-common context there is a line that begins with exten => _X.,n,Park … and the line shown there is correct for the newest version of Asterisk, but the older ones used a much different syntax. For example, exten => _X.,n,Park(15000,pgv-error,s,1,rs) would be the correct equivalent in somewhat older versions. You may need to consult the Asterisk Wiki to figure out the proper syntax – in the left hand column, select your version of Asterisk’s Documentation, then Command Reference, then Dialplan Applications, and then Application_Park to see the proper syntax and the command options in your Asterisk version. By the way, the “r” option (in the “rs” string in the example) controls whether you hear a fake ringing tone while waiting for the callback from Google Voice – if you omit that “r”, you’ll hear the Music on Hold specified in the Parking Lot configuration for that (hopefully brief) period. I mention this only because some people may prefer not to hear a ringing tone until the called phone has actually started ringing.

Finally, the pgv-in-common context is another I like to rename, replacing pgv with something more meaningful (again you could use the Google Voice username, but that might not be appropriate if your DID is shared by two Google Voice accounts – I use the ten digit DID number in my revisions below). But again, if you change that string then you need to make the same change everywhere it appears in RonR’s instructions. In the first lines of the context, the “Google Voice Number n” strings must be replaced by the actual 10-digit Google Voice numbers, and if there’s only one Google Voice account associated with the DID then you can comment out (or remove) the line with “Google Voice Number 2” in it, and also the line with the (cb2) label. Also note that the ParkedCall(nn) statements must contain the correct parking lot numbers as set in the corresponding -out contexts. So each Parking Lot number should appear twice (and only twice), once in a -out context (or pgv-out-n if you use RonR’s naming system), and then again in a line with the (cbn) label in a -in-common context.

In the middle of the context there is a line, “exten => s,n,Goto(from-trunk,Callback Number*,1)” – here you replace “Callback Number” with the DID number that Google Voice is sending your incoming calls to, but in this case you’ll probably want it to be in 10-digit format (no leading + or 1). Do leave the * character, it’s not a typo. The purpose of this line is to handle calls that come in where someone has called your Google Voice number or your Callback Number (DID) directly, in other words when someone is calling you and the call is not a callback from Google Voice as a result of you placing an outgoing call. This line is why you’ll need a separate -in-common context for each of your DID’s used with Google Voice.

If you have multiple Google Voice accounts, then the general rule is that there has to be one -out (or pgv-out-n) context, and an associated Custom Trunk and Outbound Route for each Google Voice account. And, there must be one pgv-in-common context (if there is more than one context, each must have a unique name) for each DID that is used as a destination number by one of your Google Voice accounts, along with an associated Custom Destination. As for Inbound Routes, you’ll need two of those for each DID used by Google Voice, but one of them will have the * character appended to the phone number, as shown in RonR’s instructions. When you create the Inbound Routes, if you want to enable Caller ID Superfecta or any other type of local Caller ID lookup, do it only in the route with the * character at the end of the number. The use of any kind of Caller ID lookup on the non-starred number is pointless, and will slow down the connection time for your outgoing calls.

I also like to end all my contexts with a line of the form
exten => h,1,Macro(hangupcall,)
So just on the off chance the caller hangs up while in that part of the context, the hangup is correctly handled. But that is up to you.

It bothers me just a little that there appears to be no good way to cancel the call if the caller hangs up before the callback is received, but if that happens it appears that when the call comes in from Google the PBX will play a very short message about the parking lot number being invalid and then disconnect. So just be aware that if you call a number and then after dialing the last digit quickly change your mind and hang up, the called party’s phone may still ring once.

Up near the top of RonR’s post he shows how to set up the Outbound Routes (“Connectivity -> Outbound Routes -> Add Route”) and shows a Dial Pattern of NXXNXXXXXX which is correct for 10-digit dialing, but obviously you may need to add additional patterns if your system supports 7 or 11 digit dialing. You can limit the use of any particular Outbound Route to one or more specific extensions using Asterisk’s “ex-girlfriend logic” if you like.


That’s pretty much it. If you spot any errors, including any typos I haven’t yet caught, or know of a better way to do this, please leave a comment. This article may be updated if new information is received.

Why your game console or home VoIP PBX won’t work with OPNsense or pfSense, and how to fix it

If you have been using a standard router and decide to upgrade to OPNsense or pfSense (I personally recommend OPNsense, solely because of the heavy-handed moderation in the pfSense user forum, where a user can apparently get banned for life for even a small inadvertent infraction), you may find that making a game console or a VoIP PBX work isn’t as simple as just forwarding some ports. The other thing you have to do is shown in this video:

Although the video specifically mentions the PS4 and XBOX, the advice shown is equally valid for other types of game consoles and for home PBX servers. Note the section starting at 3:20 in the video, where the “Static Port” checkbox is checked – this is the key to making it work!

The OPNsense user interface will look a bit different than the one in pfSense but the principle is the same; you still need to make sure the “Static Port” checkbox is checked. And in either case, you may still need to do port forwarding, the same as you did on your previous router, but generally speaking port forwarding alone will not work until the additional configuration shown in the video is applied. Here’s an example of setting up a static port rule in OPNsense (note that the source address field refers to a previously-set alias for the IP address of the Asterisk PBX):

VoIP PBX users, there is one other thing you may need to do, at least in OPNsense, particularly if you find that you have a non-local extension that is unable to connect to your PBX. If you are using a Dynamic DNS address, make sure you go to System: Settings: Administration and put that dynamic DNS address in the “Alternate Hostnames” field.

A Bash script to rewrite the "static" IP address in the FreePBX Asterisk SIP Settings when it is changed by your ISP

 

Important
This is a revision of the post, A Perl script to rewrite the "static" IP address in the FreePBX Asterisk SIP Settings when it is changed by your ISP, but modified to use a Bash script. Much of the explanatory text is directly copied, or in some cases heavily modified, from the earlier article, which in turn was taken (with permission) from the old Michigan Telephone blog after it went defunct. THIS SCRIPT IS STILL CONSIDERED EXPERIMENTAL – USE AT YOUR OWN RISK!!!

This post is going to be a bit long because I first need to explain the “why” behind this script, then the script itself and how to test it after installation. Please note that the screenshots below are from earlier versions of FreePBX, and you probably will be using a more modern GUI interface, but the settings shown should still appear.

If you are using a recent version of Asterisk and FreePBX you may be using the Asterisk SIP Settings module (under the “Settings” tab) to automatically set various SIP parameters.  This module is a great help to those who don’t know what they are doing, but there is a trap for the unwary (and in this case it’s NOT the fault of FreePBX – it’s a longstanding bug in Asterisk that’s the problem).

At the top of the Asterisk SIP Settings configuration page, in the NAT Settings section, there are two options that can be set.  The first is NAT and there are four possible choices:

  • yes = Always ignore info and assume NAT
  • no = Use NAT mode only according to RFC3581
  • never = Never attempt NAT mode or RFC3581
  • route = Assume NAT, don’t send rport

In theory, if you have a fixed IP address AND your Asterisk server is not behind an external router that does NAT translation, you should use “no” (and most of the rest of this article will not be relevant to you).  This article is intended more for home and SOHO users that both have their Asterisk server behind a hardware router of some kind, and that get their broadband service from a company that occasionally changes their IP address without warning.  For such users, the preferred setting is “yes”.  I’m not enough of a networking guru to tell you under what circumstances one of the other settings might be appropriate (if you understand this stuff, feel free to leave a comment and enlighten us).

FreePBX: Asterisk SIP Settings page, Chan SIP Settings tab, NAT Settings (Public IP Option)

It’s the next set of settings that can get us into trouble.  This is the IP Configuration and there are three possible choices:

  • Public IP
  • Static IP
  • Dynamic IP

If your IP address never changes AND you aren’t behind a hardware firewall then you can usually just set this to “Public IP” and let it go at that.  You will not be asked to fill in any other values.  But most users that are not in that situation will pick one of the other two choices, and this is where the problem arises.  Conventional wisdom has it that if your ISP ever changes your IP address without advance warning (which is the case for most cable broadband and DSL users), you should use the Dynamic IP setting.  In this case there is an auto-configure button that will fill out the fields for you, although you may need to fill in the Dynamic Host field yourself.  This is the “External FQDN as seen on the WAN side of the router and updated dynamically, e.g. mydomain.dyndns.com” (as explained if you mouse over the words “Dynamic Host”).  You can use a DynDNS address (or an address from a similar service) or an address you have purchased.  But the problem is that for some users, THIS METHOD SIMPLY DOES NOT WORK.

FreePBX: Asterisk SIP Settings page, Chan SIP Settings tab, NAT Settings (Dynamic IP Option)

If you try to use Dynamic IP and it won’t work for you, what happens is you will get all sorts of weird errors.  You may get one way audio, some calls may disconnect for no apparent reason after about five seconds, and you will see other weird errors in your CLI.  If you change this setting to “Static IP” and click the auto-configure button and then submit the changes, the problems magically go away – UNTIL your ISP changes your IP address, at which point you suddenly have no connectivity to the outside world.  If you ask for help, everybody and their brother will tell you to use the Dynamic IP setting, and the minute you try that you’ll get all the weird errors again.

FreePBX: Asterisk SIP Settings page, Chan SIP Settings tab, NAT Settings (Static IP Option)
FreePBX: Asterisk SIP Settings page, General SIP Settings tab, NAT Settings, showing static external IP address

So if that’s your situation, you need set a Static IP address as shown in the above two screenshots (this used to be all on one page, but now it’s under two separate tabs in the new interface), and you need this Bash script.  Coupled with a cron job, it goes out and checks your IP address every five minutes and if it notices it has changed, it changes it in the MySQL database (same as if you entered it into the External IP text box on the Asterisk SIP Settings configuration page) and then reloads Asterisk.  Therefore, you can use the Static IP method and it hopefully it will work reliably.  If and when your IP address changes, you should only be down for about five to ten minutes at most (hopefully your broadband provider usually does such changes in the middle of the night!).

A word to the wise, do NOT enter anything into the “Override External IP” field under the “Chan SIP Settings” tab unless you are certain that you know what you are doing, or you may have “no audio” issues. You should only put your external IP address in the “External Address” field under the “General SIP Settings” tab.

Prerequisites:

You will need to use a Dynamic DNS service to keep track of your IP address if you want external extensions to be able to find your server on the Internet.  It’s not required for this script to work, though, so I won’t say any more about that except to note that if you use a recent vintage hardware router, it probably has DDNS support built in.

The Script:

Note that WordPress MAY change apostrophes and quotes into “prettified” versions, and if it does that will totally mess up Bash.  I’m going to put this in a preformatted text block so hopefully WordPress won’t change anything (it doesn’t appear that it has), but you never know.  Also, don’t confuse backticks (`) with apostrophes (‘) – backticks are used around the word `key` in the script below.

This script was written for use with FreePBX 14; there are some minor changes that need to be made for earlier versions (for example, you may need to use kvstore in place of kvstore_Sipsettings where it appears in the script). If you are running an earlier version you can either use the Perl version of this script from A Perl script to rewrite the "static" IP address in the FreePBX Asterisk SIP Settings when it is changed by your ISP, or at least see that article to see an explanation of the changes.

There are long lines in this script that overflow the allotted display area, so you will probably want to copy and paste the entire script (except for the final three commented out lines if you don’t need it to update a Dynamic DNS; see below) into a text editor such as nano. Make sure you do copy the last line, which contains only “fi”, or it won’t work.

#!/bin/bash
# This program gets the current IP address (as assigned by the ISP) from
# OpenDNS and modifies the FreePBX Asterisk SIP settings if the external IP
# address has changed. Invoke it as cron job that runs every 5 minutes.
# THIS SCRIPT IS STILL CONSIDERED EXPERIMENTAL - USE AT YOUR OWN RISK!!!
user="user"
pass="pw"
check=$(dig +short myip.opendns.com @resolver1.opendns.com) || { echo "Problem getting current IP address"; exit 1; }
if [[ ! $check =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  echo "Invalid IP address"; exit 1;
fi
readip=$(mysql asterisk -u $user -p$pass -se 'SELECT val FROM kvstore_Sipsettings where `key` = "externip"') || { echo "Can't read externip from MySQL asterisk database"; exit 1; }
if [ "$check" != "$readip" ]; then
# IP address has changed
# Send email
# echo "This is an automated message - please do not reply. It appears that our external IP address has been changed to $check" | mail -s "External IP address may have been changed" you@someaddress.com
# Save new IP address to Asterisk SIP settings
mysql_response=$(mysql asterisk -u $user -p$pass -se 'update kvstore_Sipsettings set val='\"$check\"' where `key`="externip" ')
# Reload Asterisk
/var/lib/asterisk/bin/module_admin reload
# Update freedns
# date > /tmp/freedns_uodate.log
# /bin/sleep 21 ; /usr/bin/wget --no-check-certificate -O - https://freedns.afraid.org/dynamic/update.php?uniqueupdatestring== >> /tmp/freedns_update.log 2>&1 &
fi

NOTES on the above script, including THINGS YOU MUST CHANGE:

Note the two bolded variables user and pw. These must be changed to the correct values for YOUR system. You will usually find these in one of two places. You can look in /etc/amportal.conf and look for the variables AMPDBUSER and AMPDBPASS — these will usually be near the bottom of the file in newer installs, in a “— CATEGORY: Bootstrapped or Legacy Settings —” section, but they can be anywhere in the file.

Another place they may be found is in the file /etc/freepbx.conf — in that file, look for lines similar to:

$amp_conf[‘AMPDBUSER’] = ‘freepbxuser’;
$amp_conf[‘AMPDBPASS’] = ‘password’;

Those will give you the values to insert into the user and pw variables in the script. YOU MUST INSERT THE CORRECT VALUES OR THE SCRIPT WILL NOT WORK! By the way, if you have both of the above-mentioned files, make sure that the AMPDBUSER and AMPDBPASS variables are set to the same respective values in both files, otherwise your CDR Reports page may not work.

If you want an e-mail notification when your IP address has changed (which is recommended), uncomment the next line under “# Send email” and modify the email address appropriately (make sure you use one or more valid e-mail addresses; you use a comma to separate addresses if you use more than one). Note this will only work if your system is already configured to send outgoing email.

You can also use this script to update a Dynamic DNS; if you use freedns.afraid.org as your Dynamic DNS then you could uncomment the two lines under “# Update freedns” and change the update URL to the unique one for your account. Don’t uncomment those lines unless and until you have the correct update URL for YOUR freedns account!. If you don’t want this script to update a Dymanic DNS account (as might be the case if you have your router set to perform this function) then you can just omit the final three commented out lines of the script – make sure you DO include the “fi” at the end of the script, though!

Note that in two places in the script (the lines that access MySQL) the word `key` appears within backtick quotes. If you leave out the backticks, or change them to something else such as apostrophes, IT WILL NOT WORK. Backticks and apostrophes are NOT the same character!

Save your script to either the /root directory or the /var/lib/asterisk/agi-bin directory, or to another location of your choosing. I named it checkip.sh. You must make the script executable, for example:

chmod u+rx /var/lib/asterisk/agi-bin/checkip.sh

Of course you will specify the correct filename and directory. Now it’s time to test the script. From the Linux command prompt, navigate to the directory where you stored the script:

cd /var/lib/asterisk/agi-bin

Now run the script from the command prompt:

./checkip.sh

Hopefully you won’t see any error messages. Remember it’s going out to do a query to get your external IP address, so don’t get concerned if it takes a second or two. If you had an incorrect address stored in your FreePBX Asterisk SIP Settings configuration, it will take longer because it will reload the FreePBX configuration. The script has a check to make sure it only stores a real IP address (and not something invalid like an error message) in the database, so if it appears to not be working, make sure the underlying call to the dig command is returning a valid IP address. By the way, that IP address check is rather rudimentary, and really only checks that the address only contains four sets of numbers separated by three dots, so if the dig command were to somehow return a totally bogus, but valid looking IP address, it might not get caught, however that’s highly unlikely to happen. That said, if you know of a better way to validate an IP address (or for that matter, see a better way to do anything in this script), please leave a comment!

If you do get an error, check that you copied the entire script correctly (including the ending “fi” statement), and that you made all the necessary changes as indicated above. Also check to make sure that you made the script executable.

Setting up a cron job

Once it runs without errors, you will want to create a cron job so it runs automatically every five minutes. Do NOT run it more often than that, or the lookup service may ban your IP address, and you don’t want that to happen, and besides, it’s not polite to hog the resources of someone else’s server! And if you are running it on multiple servers at the same IP address, then adjust the polling speed so that the total polling from all servers doesn’t exceed once every five minutes. An occasional additional test is probably not an issue, but if you try to poll every minute you just might get banned!

The usual way to add a cron job is to run this command:

crontab -e

(If you’re not currently running as root use sudo crontab -e instead)

This will open a text editor showing your current cron jobs. Just add a new line to the bottom of the file with your new cron job. To run the script every five minutes, you could use something like this:

*/5 * * * * /var/lib/asterisk/agi-bin/checkip.sh

Or to be more specific as to when the script runs (this will run it exactly on the hour, at five minutes after the hour, at ten minutes after the hour, and so on):

0,5,10,15,20,25,30,35,40,45,50,55 * * * * /var/lib/asterisk/agi-bin/checkip.pl

Just save the changed file when you are finished. The alternate method is to use Webmin’s System | Scheduled Cron Jobs module to set up your cron job.

Final testing:

The easiest way to test to make sure this is all working is to wait until a time that there are no active calls on the system, then go to the Asterisk SIP Settings configuration page and change the External IP address to something invalid (just change the last digit of the current address and Submit Changes, then do the usual configuration reload). On the next five minute interval, the script should detect that the external IP address doesn’t match the one stored in the database, and it will write the correct value to the database and reload the FreePBX configuration. If you watch the Asterisk CLI during this time, you should actually see the reload take place. After that, if you go back to the Asterisk SIP Settings configuration page, the correct IP address should be there. To be extra safe, you should also view the contents of the file /etc/asterisk/sip_general_additional.conf and make sure that the externip= line shows the correct IP address.

Now you don’t have to worry about frantic calls from users at inopportune times because your ISP changed your IP address and none of the phones are working, and you also won’t have any of the problems associated with the Dynamic IP method!

NOTE: Again, this script should be considered experimental, and as usual, there are no warranties — we’re experimenters here, and sometimes we don’t catch all the bugs, especially on the first go around! However, I would assume that anyone who is running a “professional” installation would pay their ISP for a true static IP address (one that never changes), and therefore wouldn’t need this type of script.

OAuth 2.0 Support for Asterisk 13, so XMPP connections to Google Voice can be made using the more secure oAuth authentication

If you are running Asterisk 13 (or are ready to upgrade to Asterisk 13) and are using it to connect to one or more Google Voice accounts, you can now use oAuth authentication instead of the problematic username/password, without resorting to the use of a pre-built distribution that may contain features you don’t need and don’t want. The details are here:

OAuth 2.0 Support for Asterisk 13

Also, if you have a Raspberry Pi and would like to make a clean build of Asterisk and FreePBX, the same author (RonR) has provided instructions here. Just be sure to select Asterisk 13 when installing if you want to use the oAuth 2.0 support:

FreePBX for the Raspberry Pi

Or, if you’re sick of FreePBX and are ready to try a new interface to Asterisk, he has you covered there as well:

XiVO PBX for the Raspberry Pi

All of the above links are to threads at DSLReports. Note that the install scripts in the last two links can take some time to run, especially on an older model Raspberry Pi where they could take a few hours to complete (I believe you must have a Raspberry Pi 2 at a minimum to use the XiVO build). But when you are through, you’ll have a nice clean install, without the extraneous and mostly non-useful stuff found in a certain pre-built image.

How to hack the FreePBX blacklist for better call blocking capability, take 2 – adding TrueCNAM scoring

This article is mostly a repeat of the article How to hack the FreePBX blacklist for better call blocking capability, the only difference being that this article adds the ability to use TrueCNAM to help determine if an incoming call is from a telemarketer or robo-caller. It’s not foolproof, of course, and some spam calls may still slip through, but this will help you deal with the majority of them. BE AWARE THAT THERE MAY BE FALSE POSITIVES, where numbers that are NOT associated with telemarketers or spammers nevertheless show up with a high score. This is probably a rare occurrence, but not totally outside the realm of possibility. Therefore:

BY USING THIS METHOD YOU AGREE THAT IMPORTANT CALLS COULD BE BLOCKED. YOU ALONE ARE RESPONSIBLE FOR DETERMINING WHICH CALLS ARE BLOCKED. WE ASSUME NO RESPONSIBILITY IF YOU MISS AN IMPORTANT CALL DUE TO USE OF THIS METHOD. IF YOU DO NOT AGREE WITH THIS, DO NOT USE ANY OF THE INFORMATION IN THIS ARTICLE!!!

You will need to have an account with TrueCNAM, so go here to create one. If you are just a home user the free plan should be sufficient, particularly if you add the numbers of your most frequent callers to the Asterisk phonebook so that they are not sent to TrueCNAM for scoring (in effect the Asterisk Phonebook serves as a “whitelist” in this setup). The only “price” for this service is that you will have to submit one of your phone numbers and provide a Caller ID name to associate with it. They will call you to verify it’s your number, so make sure you can answer the verification call while you create the account.

You will also need an API Key and Password, so after you have created the account go to the API Keys page and click the “Add API Key” button to generate an API Key and Password. Keep this page open during the next steps because you will need to plug these into the script below.

Next, on your FreePBX server create a file named /var/lib/asterisk/agi-bin/truecnam.agi, and change the ownership and permissions so that Asterisk can use it (you will need to be root to do this, or use sudo in front of each of the following commands):

touch /var/lib/asterisk/agi-bin/truecnam.agi
chown asterisk:asterisk /var/lib/asterisk/agi-bin/truecnam.agi
chmod 754 /var/lib/asterisk/agi-bin/truecnam.agi

Next use a text editor such as nano to open the file:

nano /var/lib/asterisk/agi-bin/truecnam.agi

Paste in the following lines, changing the API Key and Password values to the ones shown on the API Keys page:

#!/bin/bash
declare -a array
while read -e ARG && [ "$ARG" ] ; do
        array=(` echo $ARG | sed -e 's/://'`)
        export ${array[0]}=${array[1]}
done

APIKEY="APIKey"
APIPASS="APIPassword"
RESULT=""
SPAMSCORE=0
RESULT=$(curl -s -k -m 2 --connect-timeout 2 "https://api.truecnam.net/api/v1?username=$APIKEY&password=$APIPASS&resp_type=extended&resp_format=csv&calling_number=$agi_callerid&call_party=terminating")
SPAMSCORE=`echo $RESULT | cut -d , -f 9`

if [[ $SPAMSCORE == "" ]]
then
  SPAMSCORE=0
fi

echo "SET VARIABLE SPAMSCORE ${SPAMSCORE}"

(The above script was modified from scripts posted in this thread at DSLReports)

In case you missed it above, THIS SCRIPT WILL NOT WORK UNLESS YOU PASTE IN YOUR API KEY AND PASSWORD IN THE APPROPRIATE LOCATIONS.

What follows next is the original article How to hack the FreePBX blacklist for better call blocking capability, but with the modifications to use the above script:

Have you ever wished that you could blacklist calls in FreePBX using criteria other than just the exact phone number? It’s quite possible, and here’s an explanation of how it can be accomplished.

First, using a text editor such as nano, go into the file /etc/asterisk/extensions_additional.conf and find the context headed [app-blacklist-check]. It will probably look like this:

[app-blacklist-check] include => app-blacklist-check-custom
exten => s,1(check),GotoIf($["${BLACKLIST()}"="1"]?blacklisted)
exten => s,n,Set(CALLED_BLACKLIST=1)
exten => s,n,Return()
exten => s,n(blacklisted),Answer
exten => s,n,Wait(1)
exten => s,n,Zapateller()
exten => s,n,Playback(ss-noservice)
exten => s,n,Hangup

Note that this is a pretty simple macro context. The first line checks to see if a caller ID number appears in the Asterisk blacklist, and if so, it shuffles the call off to a “number not in service” recording.

If you copy and paste this entire context into the file /etc/asterisk/extensions_override_freepbx.conf and remove the “include” line at the start of the macro, you can then modify it to do what you want. Let’s look at that same context again, but with a few added lines, as it might appear in /etc/asterisk/extensions_override_freepbx.conf

[app-blacklist-check]

;Make sure CDR DID field is set on blacklisted calls
exten => s,1(check),Set(CDR(did)=${FROM_DID})
;Next line is from the original blacklist context but moved higher
exten => s,n,Set(CALLED_BLACKLIST=1)
;Special CallerID name lookup; uses Asterisk Phonebook which also acts as whitelist
exten => s,n,GotoIf($["${DB_EXISTS(cidname/${CALLERID(num)})}"!="1"]?blcheck)
exten => s,n,Set(CALLERID(name)=${DB(cidname/${CALLERID(num)})})
exten => s,n,Set(__SKIPCIDLOOKUP=1)
exten => s,n,Return()
;Original first line of context with line number changed and blcheck label added
exten => s,n(blcheck),GotoIf($["${BLACKLIST()}"="1"]?blacklisted)
;Blacklist Caller ID names found in custom Asterisk database blistname if used
exten => s,n,GotoIf($["${DB_EXISTS(blistname/${TOUPPER(${CALLERID(name)})})}"="1"]?blacklisted)
;Blacklist Caller ID names of single letter followed by 9 or more digits
exten => s,n,Set(regx=^[A-Z][0-9]{9}[0-9]* *$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(name)})} = 1]?blacklisted)
;Blacklist numbers where first digit of 7 digit local number is 0 or 1
exten => s,n,Set(regx=^[+]?1?[0-9]{3}[01][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(num)})} = 1]?blacklisted)
;Blacklist numbers where first digit of area code is 0 or 1
exten => s,n,Set(regx=^[+]?1?[01][0-9]{9}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(num)})} = 1]?blacklisted)
;Blacklist numbers where first three digits of 7 digit local number are 555
exten => s,n,Set(regx=^[+]?1?[0-9]{3}555[0-9]{4}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(num)})} = 1]?blacklisted)
;Blacklist numbers that start with 1 but are longer than 11 digits total
exten => s,n,Set(regx=^[+]?1[0-9]{11}[0-9]*$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(num)})} = 1]?blacklisted)
;Blacklist numbers of less than 7 digits in length
exten => s,n,GotoIf($[${LEN(${CALLERID(num)})} < 7]?blacklisted)
;Blacklist numbers of 8 digits in length
exten => s,n,GotoIf($[${LEN(${CALLERID(num)})} = 8]?blacklisted)
;Query TrueCNAM
exten => s,n,AGI(truecnam.agi)
exten => s,n,Noop(TrueCNAM SPAMSCORE: ${SPAMSCORE})
;Show non-zero spam score in Userfield of Call Detail Record
exten => s,n,ExecIf($[${SPAMSCORE} > 0]?Set(CDR(userfield)="Spam Score ${SPAMSCORE}"))
exten => s,n,ExecIf($[${SPAMSCORE} > 39]?Set(CALLERID(name)=SPAM:${CALLERID(name)}))
exten => s,n,GotoIf($[${SPAMSCORE} > 99]?highscore)
;More original blacklist context
exten => s,n,Return()
;Set Call Detail Record Userfield to Blacklisted (splits statement from original blacklist context)
exten => s,n(blacklisted),Set(CDR(userfield)=Blacklisted)
exten => s,n(highscore),Answer
;More original blacklist context
exten => s,n,Wait(1)
exten => s,n,Zapateller()
exten => s,n,Playback(ss-noservice)
exten => s,n,Hangup

;--== end of [app-blacklist-check] ==--;

The lines in blue are from the original context that was copied from /etc/asterisk/extensions_additional.conf. We should mention here that not all the ideas in the new lines are original with us; some are adaptations of things we’ve read over many months in various forums.

EDIT: Also please note that in recent versions of FreePBX the last four dialplan lines have been changed. Instead of

exten => s,n,Wait(1)
exten => s,n,Zapateller()
exten => s,n,Playback(ss-noservice)
exten => s,n,Hangup

It now uses

exten => s,n,Set(BLDEST=${DB(blacklist/dest)})
exten => s,n,GotoIf(${LEN(${BLDEST})}?${BLDEST}:app-blackhole,zapateller,1)

Or in even newer versions:

exten => s,n,Set(BLDEST=${DB(blacklist/dest)})
exten => s,n,GotoIf($["${returnhere}"="1"]?returnto)
exten => s,n,GotoIf(${LEN(${BLDEST})}?${BLDEST}:app-blackhole,zapateller,1)
exten => s,n(returnto),Return()

This underscores the importance of starting with the original [app-blacklist-check] context in your FreePBX application, and not just blindly copying the example dialplan shown here. (End of edit.)

You don’t need to use all of these added lines shown. On the other hand, you can add your own customizations. Here is what each added line (or set of lines) means:

The change to line 1 (moving the original line 1 down a few lines) adds Set(CDR(did)=${FROM_DID}), which causes the call detail record to show the DID the call came in on for blacklisted calls.

The next added section consists of these lines:

;Special CallerID name lookup; uses Asterisk Phonebook which also acts as whitelist
exten => s,n,GotoIf($["${DB_EXISTS(cidname/${CALLERID(num)})}"!="1"]?blcheck)
exten => s,n,Set(CALLERID(name)=${DB(cidname/${CALLERID(num)})})
exten => s,n,Set(__SKIPCIDLOOKUP=1)
exten => s,n,Return()

What this does is checks the Asterisk Phonebook to see if the calling number appears there, and if it does, it sets the Caller ID name to the name associated with that number in the Phonebook. It also allows the Asterisk Phonebook to act as a “whitelist”, so that numbers in the Phonebook are not sent to TrueCNAM for spam scoring or subjected to any further blacklist tests. If a number appears in both the Asterisk Phonebook and the Blacklist, the Asterisk Phonebook will “win” and the call will be allowed through, so don’t put any number from which you want to block calls in the Asterisk Phonebook! Originally I had put this section lower in the context but then I realized that if a number appears in the whitelist, no further blacklist checking is necessary or desirable.

There is one issue with doing this, and that is that if you have the FreePBX CallerID Lookup module installed and enabled, and you use another Caller ID lookup source it will override the Asterisk Phonebook lookup. To avoid that, you can either uninstall or remove the CallerID Lookup module, OR you can copy the [cidlookup] context from /etc/asterisk/extensions_additional.conf to /etc/asterisk/extensions_override_freepbx.conf and change the first line to a test for the SKIPCIDLOOKUP variable. For example, if the first line is:

exten => cidlookup_return,1,ExecIf($["${DB(cidname/${CALLERID(num)})}" != ""]?Set(CALLERID(name)=${DB(cidname/${CALLERID(num)})}))

You could change it to:

exten => cidlookup_return,1,,ExecIf($["${SKIPCIDLOOKUP}"="1"]?Return())
exten => cidlookup_return,n,ExecIf($["${DB(cidname/${CALLERID(num)})}" != ""]?Set(CALLERID(name)=${DB(cidname/${CALLERID(num)})}))

Don’t forget to change the line number 1 to n in the original first line, since it’s no longer line 1.  If you do not have the [cidlookup] context in /etc/asterisk/extensions_additional.conf then you probably have not installed or enabled the CallerID Lookup module, and in that case you don’t need to make these changes, and you can also omit the line that contains exten => s,n,Set(__SKIPCIDLOOKUP=1) from the added section under discussion.

Also, if you have the CID Superfecta module installed and enabled, then you should not enable the Asterisk Phonebook data source (since you would be doing the same lookup twice), but you should enable the Trunk Provided data source (so that CID Superfecta will see that there is already a Caller ID name associated with the call, and not further attempt to find a name to associate with the number).

Getting back to the additions to the app-blacklist-check context…

;Blacklist Caller ID names found in custom Asterisk database blistname if used
exten => s,n,GotoIf($["${DB_EXISTS(blistname/${TOUPPER(${CALLERID(name)})})}"="1"]?blacklisted)

This addition allows you to blacklist by exact Caller ID name. To add a name to the blacklist-by-name database, go into the Asterisk CLI and enter this:

database put blistname NAME 1

Where NAME is the name you want to add – enclose it in quotation marks if it contains spaces:

database put blistname "SOME NAME" 1

You can use a descriptive word if you like in place of the number 1 at the end of the line. To delete an already entered name:

database del blistname NAME

And to see what’s already in the blacklist-by-name database:

database show blistname

Next we come to:

;Blacklist Caller ID names of single letter followed by 9 or more digits
exten => s,n,Set(regx=^[A-Z][0-9]{9}[0-9]* *$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(name)})} = 1]?blacklisted)

These lines are an example of blacklisting a Caller ID name using a regular expression. This will catch any Caller ID name that starts with a single letter and is followed by 9 or more digits, and any number of spaces at the end. This particular example will get rid of those irritating Vnnnnnnnnn… name calls, even if they change the starting letter to something other than “V”.

;Blacklist numbers where first digit of 7 digit local number is 0 or 1
exten => s,n,Set(regx=^[+]?1?[0-9]{3}[01][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(num)})} = 1]?blacklisted)

;Blacklist numbers where first digit of area code is 0 or 1
exten => s,n,Set(regx=^[+]?1?[01][0-9]{9}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(num)})} = 1]?blacklisted)

;Blacklist numbers where first three digits of 7 digit local number are 555
exten => s,n,Set(regx=^[+]?1?[0-9]{3}555[0-9]{4}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${CALLERID(num)})} = 1]?blacklisted)

These are three examples of blacklisting numbers by regular expression patterns. The first example blacklists numbers that have an invalid exchange (where the 7-digit part of the number starts with 0 or 1), while the second blacklists numbers that come in with an area code starting with 0 or 1, which is invalid. The third blocks numbers that appear to come from the “directory assistance” exchange 555 in any area code. In all cases, it does not matter if the number starts with a leading + and/or 1. Note that Asterisk has a fairly restrictive syntax for regular expressions, so you can’t do many of the substitutions that would be possible in other computer languages. See the article, Did you know that Asterisk has the ability to evaluate Regular Expressions, though not in the same way as Perl or FreeSWITCH? for more information.

You can always add additional blacklist patterns by duplicating an appropriate pair of lines (for blacklisting by name or number pattern) and then modifying the regular expression. For example, to block calls from certain specific area codes, you could use a regular expression of the form ^[+]?1?(xxx|yyy|zzz)[0-9]{7}$ where xxx, yyy, and zzz are the area codes you want to block. You can include as many area codes as you need, separated by the | character.

;Blacklist numbers of 8 digits in length
exten => s,n,GotoIf($[${LEN(${CALLERID(num)})} = 8]?blacklisted)

This is an example of blocking numbers of a specific length. If you have a bad caller that sends numbers that are exactly 8 characters in length, this will catch them. You can, of course, change the “8” to catch other bad length numbers, but be careful not to inadvertently block wanted international or other valid calls with non-standard number lengths.

The next section is where the TrueCNAM scoring is performed:

;Query TrueCNAM
exten => s,n,AGI(truecnam.agi)
exten => s,n,Noop(TrueCNAM SPAMSCORE: ${SPAMSCORE})

The above calls the bash script to get the TrueCNAM spam score for the number (from 0 to 100).

;Show non-zero spam score in Userfield of Call Detail Record
exten => s,n,ExecIf($[${SPAMSCORE} > 0]?Set(CDR(userfield)="Spam Score ${SPAMSCORE}"))

Newer versions of FreePBX have a “Userfield” in the call detail records (CDR), and here we are placing the spam score (if it is higher than zero) in the CDR so that when you review the call detail you can determine how a call scored.  For example if a calling number had a relatively high score, but not quite high enough to get it blocked, you could review it using an online lookup source such as 800notes to see if it’s a number you want to add to the Blacklist.

exten => s,n,ExecIf($[${SPAMSCORE} > 39]?Set(CALLERID(name)=SPAM:${CALLERID(name)}))
exten => s,n,GotoIf($[${SPAMSCORE} > 99]?highscore)
exten => s,n,Return()

The above section is where we act on the spam score. In this example, if the spam score is 40 or higher, the word “SPAM:” is prepended to the existing caller ID name.  This does not mean that the call is unequivocally from a telemarketer or robocaller, but that you probably should be suspicious of that caller.  You can change the value but be careful, because in our experience calls from some perfectly valid numbers can have a score of 30 (it’s rare but it happens).  If the spam score is 100 (the highest possible score), then it basically treats the call as if you had blacklisted it.  You can change this to a lower value (TrueCNAM recommends 80 or higher, so you could change > 99 to > 79) but that is entirely up to you.  We didn’t want anyone blaming us for missing that all important call that would have awarded them the vacation of a lifetime (but only after sending gift cards to some Nigerian prince to cover “fees”), so in this example we set it to only block the call outright if it scores a solid 100.

;Set Call Detail Record Userfield to Blacklisted (splits statement from original blacklist context)
exten => s,n(blacklisted),Set(CDR(userfield)=Blacklisted)

This is where calls that fail the other tests (not TrueCNAM) land. We don’t recall where we first saw this, but it is a great idea. Here the “Userfield” in the call detail record is set to the word “Blacklisted”. When you are looking at your call records, this tells you instantly if a problem caller is already being blacklisted, so you don’t need to wonder if you should add them to the blacklist. Note that if you see a Spam Score, the call is NOT blacklisted, so if you want to blacklist that number you will need to do that separately. You might want to do that for a repeat problem caller, so you’re not reaching your TrueCNAM daily limit prematurely by looking up what you already know is a bad caller.

exten => s,n(highscore),Answer

The above line is where calls that scored above the “highscore” limit in TrueCNAM land. So they are treated like any other blacklisted call, except the spam score appears in the CDR “Userfield” instead of the word “Blacklisted”.

Keep in mind that any time you replace a context in /etc/asterisk/extensions_override_freepbx.conf, it is your responsibility to make sure it “tracks” any changes to the original context in /etc/asterisk/extensions_additional.conf. So, after any major FreePBX upgrade, you should take a look at the original [app-blacklist-check] context (and the [cidlookup] context, if you have modified that) and make sure that they have not changed, and if they have, that you make the same changes to your replacement contexts.

One additional point – at the top of each of the original contexts, there is an “include” statement that we suggest you remove when you copy the context to /etc/asterisk/extensions_override_freepbx.conf, for example, “include => app-blacklist-check-custom“. You might wonder if you could create a context named app-blacklist-check-custom in /etc/asterisk/extensions_custom.conf and copy the original context there. But, for whatever reason, that just doesn’t seem to work, and there’s no real advantage to putting the copied context there anyway.

Related articles:
How to hack the FreePBX blacklist for better call blocking capability
Link: Asterisk: Blacklisting For Multiple Users
Forum thread: User-specific Caller ID/whitelist/blacklist in Asterisk/FreePBX

Read this before you pay $10 to Obihai for support

 

Notice
EDIT (May, 2018): FreePBX and Asterisk users that wish to continue using Google Voice after Google drops XMPP support should go here: How to use Google Voice with FreePBX and Asterisk without using XMPP or buying new hardware.

Various parts of this article have been edited numerous times throughout 2018 to reflect recent changes.

As you may be aware, I removed all of the Obihai-related articles on this blog in protest of Obihai’s decision to attempt to charge users an additional $10 support fee in order to get firmware updates, and I no longer recommend Obihai devices. Since mid-2018 there is simply no way to get older OBi100 or OBi110 devices to work with Google Voice, since Google no longer supports the XMPP protocol that those devices used, so if you own one of those devices there is no reason to upgrade the firmware or pay Obihai for continued support, which they may not even offer on those models anymore. Those devices will still continue to work fine with anything that uses standard SIP protocol, which is to say, almost any VoIP service EXCEPT Google Voice. They also work perfectly fine for extensions on a FreePBX/Asterisk based system, or any other SIP-based software PBX.

However, if you previously purchased a newer Obihai (Obi200 or OBi202 or later) and find that you need to upgrade the firmware in order to continue using Google Voice, this Reddit thread explains how to do it without paying the $10. But before you do anything else, first try dialing * * * 6 from a phone that’s connected to your Obihai device, since you might be able to upgrade the firmware that way (although that’s not likely if you are still using username and password authentication for Google Voice). If that doesn’t work, try the instructions found here:

Read this before you pay $10 to Obihai for support (Reddit)
(Also posted at DSLReports: *** READ THIS before you pay $10 to Obihai for support ***)

The first post in the thread, from Reddit user Mango123456, reads as follows:

Obihai’s $10 support fee is optional. If you wish to avoid it, you can update your firmware manually to save the $10 support fee. This solves the May 2016 problem of Google Voice not working on OBi100/OBi110.

1a) If you have an OBi100/OBi110, follow the directions to update your firmware manually. Thanks to taoman54, rchandra, yorktown, and others who contributed to this.

1b) If you have an OBi200/OBi202, you can download the latest version of the 2xx firmware here (thanks taoman54).

2) If you cannot manually update your firmware because you do not know your OBi ATA’s admin password, follow the directions to factory reset your OBi ATA.

Please note: the links http://fw.obihai.com/OBi-latest.fw http://fw.obihai.com/OBi2-latest.fw are not the latest versions any more. At the time of this post, they are builds 2872 and 5110 respectively.

m.

Note that if you are reading this article after May, 2016 there might be newer versions of the firmware available (particularly for the OBi 200 series models) but after you have installed the correct firmware from one of the above links, you should be able to get any later firmware updates by dialing * * * 6 from a phone connected to the Obihai device. For example, here is a link to a newer firmware version that apparently worked for OBi200 and OBi202 models in late 2018 (I found this link in a blog post linked on Reddit: How to use an Obihai 200 series VoIP device as a gateway between Google Voice and FreePBX and I have not tested it in any way, so use at your own risk, however the link does point to the official fw.obihai.com download site and does at least appear to download Obihai firmware).

I have also read reports of Obihai users getting new firmware if they completely removed their device from the Obitalk portal (as they would if they were going to sell the device), then did a factory reset on the device itself, and then added it back to their account in the Obitalk portal as if it were a newly-purchased device. However I cannot guarantee that will work, and you would lose your existing configuration by doing this. You will know that you have the latest firmware if it says Polycom in the upper corner, where the Obihai brand was formerly located.

If you have an older OBi100 and OBi110 model, you MAY for some reason have some interest in Crowdsourced updates for Obihai ATAs and IP Phones. I have NOT tested these personally, and I take no responsibility if they don’t work as intended. Whether you stay with the original Obihai firmware or try the modded firmware, those older devices usually continue to work perfectly well for anything other than Google Voice. However, I don’t really see buying a new unit from Obihai as a solution to restoring Google Voice connectivity, since you don’t know if or when they will come out with yet a newer model and drop support for the current models. For more current information about alternative firmware for Obihai devices, see the ObiHAI Obi100/Obi110 Firmware Mod Discussion and/or the Obihai OBi200/202/302 + OBi1022/1032/1062 firmware mods thread at DSLReports. FreePBX and Asterisk users that wish to continue using Google Voice now that Google has dropped XMPP support should go here: How to use Google Voice with FreePBX and Asterisk without using XMPP or buying new hardware.

Final word, at the end of 2021 the announcement was made that the 200-series devices are being discontinued.

Link to forum thread: Obihai sucks – buyers paid for Google Voice, now they want more!

obihaisucks
Obihai now wants more money to give you the functionality you originally purchased!

This was kind of buried in a previous thread but I thought it deserved its own thread. I have been a supporter of Obihai devices in the past, but no longer! I have an Obihai OBi100 and when I first got it they were selling it with the idea of free connections to Google Voice, with no mention whatsoever of there being any possible future subscription fees. Anyway, today I went to see if I could add a Google Voice account to SP2 on my OBi100 and was greeted with the above screen.

At the time of purchase there was NO mention of needing to purchase technical support in order to keep using Google Voice, which after all was one of the advertised features and probably the primary reason anyone ever purchased an Obihai device. I have no issue with them charging extra to extend the warranty, but there’s nothing wrong with my device, other than that THEY require you to get new firmware before you can add a new Google Voice account or modify an existing one. In my book, this is extremely sleazy and is enough of a slap in the face that I would never consider purchasing another Obihai device.

If you read further down in the thread, you will see that technically-proficient users can still download the required firmware and install it manually (at your own risk) without paying the $10, but I don’t know how long that will last. The full thread is here:
Obihai sucks – buyers paid for Google Voice, now they want more! (DSLReports.com)

EDIT: Also see the post, Read this before you pay $10 to Obihai for support, which tells how to possibly upgrade your Obihai without paying the extortion support fee.

EDIT 2: If you have an OBi100 or OBi110 device that is now considered “obsolete” by Obihai, and has stopped working with Google Voice, you MAY be interested in Crowdsourced updates for Obihai OBi100 and OBi110 ATAs. Unfortunately, they no longer have the ability to make those older units work with Google Voice, and I have NOT tested them personally, so have no idea how well they work, and I take no responsibility if they don’t work as intended). See the thread Obihai OBi200/202/302 + OBi1022/1032/1062 firmware mods for additional information on available third-party firmware for newer Obihai devices. Obihai apparently does NOT want people to know that this alternative firmware exists, and has allegedly banned users that so much as mention the existence of this firmware in their forums.

EDIT 3: Google Voice ended the use of XMPP protocol in mid-2018. FreePBX and Asterisk users that wish to continue using Google Voice now that Google has dropped XMPP support should go here: How to use Google Voice with FreePBX and Asterisk without using XMPP or buying new hardware.

I had formerly hosted several how-to articles about the Obihai devices on this forum, that I had obtained permission from the author to repost when the old Michigan Telephone blog went defunct, but because of this action by Obihai I have taken those articles offline because I don’t want to appear to be encouraging anyone to purchase an Obihai device, or to be providing any kind of support for them. Many of those articles were rather dated anyway, but if anyone really misses them I could possibly put one or more of them back up, but with a suitable disclaimer, or if you have a URL for one of those old articles you could try accessing it via the Wayback Machine. However, at this point I emphatically DO NOT RECOMMEND the purchase of an Obihai device by anyone.

EDIT 4: Obihai was purchased by Polycom, and now at the end of 2021 they have announced that they are discontinuing the newer 200-series devices.

How to use a custom SIP header to pass the DID (or other data) between two Asterisk servers that jointly handle extensions

NOTE: This post has been edited to show a newer method that should work with both PJSIP and Chan_SIP trunks.

If you have multiple Asterisk or FreePBX servers at different locations that pass Intra-Company traffic between each other using SIP trunks, you may have wished for a way to pass the Calling DID number (or some other bit of data stored in an Asterisk variable) from one server to another. For example, let’s suppose that all extensions in your company are four digits in length, and that the extensions on server A are numbered 1000-1099, and on server B they are numbered 1100-1199, and when a call comes in on system A that goes to an extension on system B, you want to pass the original DID that the call arrived on to system B so that it will be included in the call detail for that call.  It’s easy to do this; I’ll first show what you’d need to do on a server A that is running FreePBX.

In /etc/asterisk/extensions_custom.conf, you should find a context named [macro-dialout-trunk-predial-hook], and if you’ve not previously modified it then you should only need to add these four lines to that context (the last two of which may already be present):

exten => s,1,Set(regx=^11[0-9]{2}$)
exten => s,n,ExecIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?Set(HASH(__SIPHEADERS,X-DID)=${CDR(did)}))
exten => s,n,MacroExit()
exten => h,1,Macro(hangupcall,)

The above has been modified to show the new way to do this in more recent versions of FreePBX. It is the only way that will work (without adding a lot more dialplan) if you are using a PJSIP trunk. If you are curious as to why this works, it turns out that in recent FreePBX versions, at the time the Asterisk Dial statement is executed a context named func-apply-sipheaders is executed, and the purpose of the context is to create additional SIP headers that have been set using a statement similar to the one we have used here. So, that works out rather well for our intended usage.

The previous method was to use this line as the second line of the context, which may still work for Chan SIP trunks:

exten => s,n,ExecIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?SIPAddHeader(X-DID: ${CDR(did)}))

Anyway, to explain the context above, the first line sets a regular expression to match the digits 11XX – if you also do this on server B you would of course change the 11 to 10. There is probably a way to do this without using a regular expression but I just wanted to show this method because if offers the most flexibility in matching numbers (although Asterisk has a somewhat unique way of implementing regular expressions; that’s why I have to use [0-9] rather than d to indicate “any digit” in the expression). In the second line, if the regular expression matches the called number (the destination extension), the statement Set(HASH(__SIPHEADERS,X-DID)=${CDR(did)}) (formerly SIPAddHeader(X-DID: ${CDR(did)}) ) is executed, which takes the contents of the CDR(did) variable and puts it into a custom SIP header named X-DID, which follows the standard convention of adding X- to the start of a user-created SIP header. This is then transmitted to server B as part of the first INVITE message in the channel. In raw Asterisk (no FreePBX), you just need to make sure that something similar to the statements in the first two lines come before whatever statement sends the call to the other system (usually a Dial statement of some kind).

Now when the call comes into server B, we need a way to do the opposite, which is to get the content of the X-DID SIP header and place it into the CDR(did) variable.  So in the trunk, instead of making the context=from-internal, we can make it something like context=custom-from-server-A, and then in /etc/asterisk/extensions_custom.conf we add that context, which simply contains this:

[custom-from-server-A]
exten => _X!,1,Set(CDR(did)=${SIP_HEADER(X-DID)})
exten => _X!,n,Goto(from-internal,${EXTEN},1)

On Server A, you’d use custom-from-server-B in place of custom-from-server-A.

You can transmit the contents of any Asterisk variable in this manner, but of course you’d probably want to change the custom SIP header name to something that more closely matches the original variable name.  I wouldn’t recommend going hog wild with adding custom SIP headers, but they are a valid technique for passing a few variables or other bits of data between your servers.