Blog Knowledge Base

Recording Announcements in FreePBX 13 and later

This was recorded a while ago as an aid to a customer, Its a short video on recording prompts and then adding them to an announcment so they can be used in call flow.

FreePBX Knowledge Base

FreePBX EPM Whoopsing

We have seen on some installations of FreePBX with EPM that when you upload a photo it Whoops when trying to rebulid the config.

The clue to whats happening is that it cant call function imagecreatefromjpeg or imagecreatefrompng depending on your file format

The most common cause of this is php-gd isnt installed. To Check this is simple, create a simple PHP file in the web root called phpcheck.php add the following to it:


if (extension_loaded('gd') && function_exists('gd_info')) {
    echo "PHP GD library is installed on your web server";
else {
    echo "PHP GD library is NOT installed on your web server";
<?php phpinfo(); ?>

Now browse in your favourite browser to this and it will show at the top if gd is installed or not.

If its not and php was installed via yum then :

yum search php-gd
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
 * base:
 * extras:
 * updates:
=============================================================== N/S Matched: php-gd ================================================================
php-gd.x86_64 : A module for PHP applications for using the gd graphics library

Will show if its avalible. (your version of PHP may mean this is different for you) then to install yum install php-gd and restart the httpd service

You should now be able to go to phpcheck.php again and it should show as installed. if it does you are good to go installing screen images for phones

Asterisk Support FreePBX Knowledge Base Support Technical

Resetting root password on FreePBX 14 and other Centos 7 servers

Boot your system and wait until the GRUB menu appears. On some systems you may need to press the “Escape” key to access the GRUB menu. FreePBX should show this for a few seconds on Boot

Highlight your Operating System and then press “e” to edit. You have to be quick here simpler to just press e when the menu appears and you will see similar to below.

Find the line beginning with linux. In this example the line begins linux16.

Manually delete the entries quiet and rhgb from the line. then append the following statement to the end of the line init=/bin/sh Don’t worry if your command is spread across more than one line. A continuation character “\ will be inserted automatically.

Now reboot your system now using the options specified by pressing the keys Ctrl +X

Once the system has re-booted, you will be presented with a shell prompt without having to enter any user name or password.

At this command prompt you will need to enter the following commands:

Remount the “/” root filesystem in Read/Write mode: mount -o remount,rw /

Issue the passwd command to reset the root account password: passwd

Then enter the new password as prompted twice

Then remount the “/” root filesystem in Read Only mode: mount -o remount,ro /

You can now restart the system and login with your new password.

FIrmware releases Knowledge Base

Sangoma Phone Firmware

This firmware version fixes a problem with the S400 and low audio level when making calls.

Blog Knowledge Base

Transcribing Voicemail with Google Speech api

This is part 2 and rather long awaited description of how to transcribe voicemails to email and deliver them with text and an attached MP3

You will need to install the files from here and also have a Google Developers account.

Also create a directory:-


Lets begin.

  • Script to create the mp3 and the file for transcription
FILENUM=$(/bin/ls ${PATH}${origmailbox}/INBOX |/bin/grep txt | /usr/bin/wc -l)

##Added to allow 999 messages
if  (( $FILENUM <= 9 ));
elif (( $FILENUM <= 99 ));

IN=$(/bin/grep "${origmailbox} =>" /etc/asterisk/voicemail.conf)
set -- "$IN"
IFS=","; declare -a Array=($*)

/bin/echo "[message]" >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo origmailbox=${origmailbox} >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo "context=demo" >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo "macrocontext=" >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo "exten=s" >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo "priority=11" >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo callerchan=${callerchan} >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo callerid=${callerid} >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo origdate=${origdate} >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo origtime=${origtime} >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo "category=" >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt
/bin/echo "duration=${duration}" >> ${PATH}${origmailbox}/INBOX/${FILENAME}.txt

/bin/nice /usr/bin/sox /var/lib/asterisk/sounds/catline/${origdir}.wav ${PATH}${origmailbox}/INBOX/${FILENAME}.flac   silence -l 1 0.1 1% -1 0.3 1% 

/bin/nice /usr/bin/lame -b 16 -m m -q 9-resample /var/lib/asterisk/sounds/catline/${origdir}.wav  ${PATH}${origmailbox}/INBOX/${FILENAME}.mp3

voicemailbody=$(/usr/bin/perl -w /usr/src/asterisk-speech-recog-cloud_api/cli/ -k $apikey -o detailed -r 8000 -n 1  /var/spool/asterisk/voicemail/default/${origmailbox}/INBOX/${FILENAME}.flac)

/bin/cp /var/lib/asterisk/sounds/catline/${origdir}.wav ${PATH}${origmailbox}/INBOX/${FILENAME}.wav

echo "You have a new voicemail from ${callerid} it was left on ${origdate} and is ${duration} seconds long ${voicemailbody}" | /bin/mail -s "A new voicemail has arrived from ${callerid}" -a "${PATH}${origmailbox}/INBOX/${FILENAME}.mp3" "$email"

/bin/rm -f ${PATH}${origmailbox}/INBOX/${FILENAME}.flac
/bin/rm -f ${PATH}${origmailbox}/INBOX/${FILENAME}.mp3
  • Asterisk Dialplan to pass the call to the above script
exten => _XXXX,1,Set(__EXTTOCALL=${EXTEN})
exten => _XXXX,n,Noop(${EXTTOCALL})
exten => _XXXX,n,Goto(s,1)

exten => s,1,Answer()  ; Listen to ringing for 1 seconds
exten => s,n,Noop(${EXTTOCALL} , ${DIALSTATUS} , ${SV_DIALSTATUS})
exten => s,n,GotoIf($["${DIALSTATUS}"="BUSY"]?busy:bnext)
exten => s,n(busy),Set(greeting=busy)
exten => s,n,Goto(carryon)
exten => s,n(bnext),GotoIf($["${DIALSTATUS}"="NOANSWER"]?unavail:unext)
exten => s,n(unavail),Set(greeting=unavail)
exten => s,n,Goto(carryon)
exten => s,n(unext),Set(greeting=unavail)
exten => s,n,Goto(carryon)
exten => s,n(carryon),Set(origmailbox=${EXTTOCALL})
exten => s,n,Set(msg=${STAT(e,${ASTSPOOLDIR}/voicemail/default/${origmailbox}/${greeting}.wav)})
exten => s,n,Set(__start=0)
exten => s,n,Set(__end=0)
exten => s,n,NoOp(${UNIQUEID})
exten => s,n,Set(origdate=${STRFTIME(${EPOCH},,%a %b %d %r %Z %G)})
exten => s,n,Set(origtime=${EPOCH})
exten => s,n,Set(callerchan=${CHANNEL})
exten => s,n,Set(callerid=${CALLERID(num)})
exten => s,n,Set(origmailbox=${origmailbox})
exten => s,n,Answer()
exten => s,n,GotoIf($["${msg}"="1"]?msgy:msgn)
exten => s,n(msgy),Playback(${ASTSPOOLDIR}/voicemail/default/${origmailbox}/${greeting});(local/catreq/how_did)
exten => s,n,Goto(beep)
exten => s,n(msgn),Playback(vm-intro)
exten => s,n(beep),System(/bin/touch /var/lib/asterisk/sounds/catline/${UNIQUEID}.wav)
exten => s,n,Playback(beep)
exten => s,n,Set(__start=${EPOCH})
exten => s,n,Record(catline/${UNIQUEID}.wav,3,60,kaq)
exten => s,n,Playback(beep)
exten => s,n,Hangup()
exten => h,1,Noop(${start} ${end})
exten => h,n,GotoIf($["${start}"!="0"]?ok:end)
exten => h,n(ok),Set(end=${EPOCH})
exten => h,n,Set(duration=${MATH(${end}-${start},int)})
exten => h,n,System(/usr/local/sbin/ "${callerchan}" ${callerid} "${origdate}" ${origtime} ${origmailbox} ${UNIQUEID} ${duration})
exten => h,n(end),Noop(finished)
  • Modified api script, Note the language and enhanced mode setting
    • For these to work you need “datalogging ” enabled in the dialogflow api settings
#!/usr/bin/env perl

# Render speech to text using Google's Cloud Speech API.
# Copyright (C) 2011 - 2016, Lefteris Zafiris <>
# This program is free software, distributed under the terms of
# the GNU General Public License Version 2. See the COPYING file
# at the top of the source tree.
# This has been altered to work with Googles new Speech models

use strict;
use warnings;
use File::Temp qw(tempfile);
use Getopt::Std;
use File::Basename;
use LWP::UserAgent;
use LWP::ConnCache;
use JSON;
use MIME::Base64;

my %options;
my $flac;
my $key;
my $url        = "";
my $samplerate = 16000;
my $language   = "en-US";
my $output     = "detailed";
my $results    = 1;
my $pro_filter = "false";
my $error      = 0;
my $thetext = ".";
my $score = ".";
getopts('k:l:o:r:n:fhq', \%options);

VERSION_MESSAGE() if (defined $options{h} || !@ARGV);


my %config = (
        "encoding"         => "FLAC",
        "sampleRateHertz"      => $samplerate,
        "languageCode"    => $language,
        "profanityFilter" => $pro_filter,
        "maxAlternatives" => $results,
        "model" => "phone_call",
        "useEnhanced" => 'true' 

my $ua = LWP::UserAgent->new(ssl_opts => {verify_hostname => 1});
$ua->agent("CLI speech recognition script");

# send each sound file to Google and get the recognition results #
foreach my $file (@ARGV) {
        my ($filename, $dir, $ext) = fileparse($file, qr/\.[^.]*/);
        if ($ext ne ".flac" && $ext ne ".wav") {
                say_msg("Unsupported file-type: $ext");
        if ($ext eq ".wav") {
                if (($file = encode_flac($file)) eq '-1') {
#       print("File $filename\n") if (!defined $options{q});
        my $audio;
        if (open(my $fh, "<", "$file")) {
                $audio = do { local $/; <$fh> };
        } else {
                say_msg("Cant read file $file");
        my %audio = ( "content" => encode_base64($audio, "") );
        my %json = (
                "config" => \%config,
                "audio"  => \%audio,
        my $response = $ua->post(
                Content_Type => "application/json",
                Content      => encode_json(\%json),
        if (!$response->is_success) {
                say_msg("Failed to get data for file: $file");
        if ($output eq "raw") {
                print $response->content;
        my $jdata = decode_json($response->content);
        if ($output eq "detailed") {
                foreach (@{$jdata->{"results"}[0]->{"alternatives"}}) {
                        $score = $_->{"confidence"};
                        $thetext = $_->{"transcript"};
        } elsif ($output eq "compact") {
                print $_->{"transcript"}."\n" foreach (@{$jdata->{"results"}[0]->{"alternatives"}});

print "\n\nThe transcription of message is below:\n\n$thetext\n\nWe are $score out of 1 sure its correct\n\nTranscribed using Googles Cloud Speech API ";

exit(($error) ? 1 : 0);

sub parse_options {
# Command line options parsing #
        if (defined $options{k}) {
        # check API key #
                $key = $options{k};
        } else {
                say_msg("Invalid or missing API key.\n");
                exit 1;
        if (defined $options{l}) {
        # check if language setting is valid #
                if ($options{l} =~ /^[a-z]{2}(-[a-zA-Z]{2,6})?$/) {
                        $language = $options{l};
                } else {
                        say_msg("Invalid language setting. Using default.\n");
        if (defined $options{o}) {
        # check if output setting is valid #
                if ($options{o} =~ /^(detailed|compact|raw)$/) {
                        $output = $options{o};
                } else {
                        say_msg("Invalid output formatting setting. Using default.\n");
        if (defined $options{n}) {
        # set number or results #
                $results = $options{n} if ($options{n} =~ /\d+/);
        if (defined $options{r}) {
        # set audio sampling rate #
                $samplerate = $options{r} if ($options{r} =~ /\d+/);
        # set profanity filter #
        $pro_filter = "true" if (defined $options{f});


sub say_msg {
# Print messages to user if 'quiet' flag is not set #
        my @message = @_;
        warn @message if (!defined $options{q});

# Help message #
        print "Speech recognition using Google Cloud Speech API.\n\n",
                "Usage: $0 [options] [file(s)]\n\n",
                "Supported options:\n",
                " -k <key>       specify the Speech API key\n",
                " -l <lang>      specify the language to use (default 'en-US')\n",
                " -o <type>      specify the type of output formatting\n",
                "    detailed    print detailed output with info like confidence (default)\n",
                "    compact     print only the transcripted string\n",
                "    raw         raw JSON output\n",
                " -r <rate>      specify the audio sample rate in Hz (default 16000)\n",
                " -n <number>    specify the maximum number of results (default 1)\n",
                " -f             filter out profanities\n",
                " -q             don't print any error messages or warnings\n",
                " -h             this help message\n\n";
  • In Freepbx create a Custom Destination as    “vmail2text,s,1”  and if you require certain queues to go to specific mailboxes one like “vmail2text,2000,1” so calls will be sent to mailbox 2000
  • Then in extensions that want to use transcription set the “Optional Destinations” to the custom destination.

And thats it. Enjoy.

Knowledge Base

Connecting to Serial console ports with Macs

Many devices and servers still require connection to them with console cables. Sangoma IPPBX and SBCs for example.
I will cover here how to connect to then with a Mac as they do not have a serial port.

First you will need a USB serial console cable. These can be purchased cheaply from Amazon or ebay.
For example the “KUMEED FTDI RS232 USB to RJ45 Serial for Cisco Console Rollover Cable for Cisco Routers” costs £10.99 inc delivery and works with Windows and Macs

To connect to a console port you need a few bits of information, The port speed, in the case of Sangoma SBCs the is 115200. also you need the device address.

To get teh device address open a terminal window and type:

ls /dev/*usb*

you will be returned something like:

/dev/cu.usbserial-DN01YED6 /dev/tty.usbserial-DN01YED6

so now to connect to the console port you need to enter:

screen /dev/tty.usbserial-DN01YED6  115200

you should now be connected, and can interact as if on a ssh session.

to disconnect is not as simple as just closing the terminal window, as a screen session will still be running. to exit a screen session enter the following key combination.

ctrl a \ 

If you do close a terminal you can see if any sessions are active by opening a new terminal and entering:

screen -list

Something like below will be returned if a session is active.

There is a screen on:

5177.ttys000.Ians-MacBook-2 (Detached)

1 Socket in /var/folders/bl/7k0f_2695njbsqwx762kr_380000gn/T/.screen.

to reconnect type

screen -r

and you should reconnect.

then exit as normal with ctrl a \

Blog Knowledge Base Security Uncategorized

GDPR and Call recordings

The effects of the GDPR on call recording will be to further strengthen the rights of individuals when it comes to businesses collecting, recording and using their personal data, placing greater onus the business to demonstrate compliance & increasing the penalties for not doing so.

All of this will have a direct impact on how you manage call recording. We will ask try to explain what the changes will be, what you need to know, and what you can do to get ready.

The Law As it Was

Previously, call recording was classified as a form of data processing. The Data Protection Act states that individuals must be informed and aware that they are being recorded and why they are being recorded.

This is because recorded calls have the ability to capture:

  • Personally identifiable information such as, names and addresses
  • Sensitive information such as, banking, financial, health, family, religious etc. detailsThe Data Protection Act also, sets outs rules for the correct handling of data, which requires any calls recorded to be stored securely with steps to be taken to avoid breaches.

So the main principles behind the GDPR are quite similar to those that were in place within UK legislation. With regards to call recording, the key principles are the expectation to protect privacy, notification and consent, and the requirement to adequately protect stored data from misuse.

The main difference with the GDPR will be that it strengthens the rights of the individual over the rights of an organisation. The DPA focuses on balancing the interests of individuals and businesses – as long as steps to protect privacy are followed, collecting and recording personal data is generally assumed to be justified.

Not so under the GDPR. Businesses wishing to record calls will be required to actively justify legality, by demonstrating the purpose fulfils any of six conditions:

  1. The people involved in the call have given consent to be recorded.
  2. A recording of a call is necessary for the fulfilment of a contract.
  3. Recording is necessary for fulfilling a legal requirement.
  4. The call recording is necessary to protect the interests of one or more participants
  5. The call recording is in the public interest or necessary for the exercise of official authority.
  6. Recording is in the legitimate interests of the recorder, unless those interests are overridden by the interests of the participant in the call.

Some of these conditions will apply specifically to certain uses of call recording in certain sectors. Number three, for example, could be used by firms in the financial services sector, which are required by the FCA to record all calls leading up to transactions. Number five will apply to the emergency and security services, who use call recording for investigatory purposes and in the interests of public protection.

But for general call recording, for example to monitor service levels or for staff training in a contact centre, the options left to businesses will be numbers one or six. And as the ‘legitimate interests’ of a business to evaluate customer service are not likely to outweigh the interests of personal privacy under the new regulations, so that only leaves gaining consent.

So unlike the previous law, assumed consent will not be satisfactory. With the GDPR strengthened rights of individuals to know what is happening with their personal information and to restrict and object to what happens to it, explicit consent to record calls will be required.


Along with the new GDPR comes a new ‘Principle of Accountability’ which puts a requirement on organisations to demonstrate their compliance. Data protection policies will soon become a statutory compliance document, rather than a recommended option. Therefore, businesses wishing to record calls will be required by law to draw up a specific call recording policy.

Next Step

So what to do, carry out a thorough audit of call recording practices, from the notifications given to how recordings are stored, is the first step to take. This should be done in the context of a wider evaluation of data protection, taking into account factors like how data breaches are identified, impact assessments and training and awareness within the business. From there, policies and protocols can begin to be drawn up, giving you plenty of time to make sure you hit the ground running come May 2018.

ICO Views on file retention and encryption

Data controllers must consider the security of lawful recordings and whether this can be achieved through the use of full-disk or file encryption products. However, some types of audio recording devices such as a dictation machines may not routinely offer encryption. The data controller must consider whether an alternative device is more appropriate or consider additional technical and organisational safeguards such as deleting the data as soon as practicable and locking the device away when not in use.

In the event that an unencrypted version of the recording should be retained (eg for playback in a Court of Law) then a range of other compensatory measures must be considered. These can include storage within a secure facility, limited and authorised access and an audit trail of ownership and usage.

The data controller must also consider the security of recordings once transferred from the device for long-term storage and be aware of other requirements which may prohibit audio recording of certain types of data.

Asterisk Support Blog Design FreePBX Knowledge Base Software

G.729 Goes Royalty Free


As of January 1, 2017 the patent terms of most Licensed Patents under the G.729 Consortium have expired.

With regard to the unexpired Licensed Copyrights and Licensed Patents of the G.729 Consortium Patent License Agreement, the Licensors of the G.729 Consortium, namely Orange SA, Nippon Telegraph and Telephone Corporation and Université de Sherbrooke (“Licensors”) have agreed to license the same under the existing terms on a royalty-free basis starting January 1, 2017.

For current Licensees of the G.729 Consortium Patent License Agreement, no reports and no payments will be due for Licensed Products Sold or otherwise distributed as of January 1, 2017.

For other companies selling G.729 compliant products and that are not current Licensees of the G.729 Consortium, there is no need to execute a G.729 Consortium Patent License Agreement since Licensors have agreed to license the unexpired Licensed Copyrights and Licensed Patents of the G.729 Consortium Patent License Agreement under the existing terms on a royalty-free basis starting January 1, 2017.

As soon as we hear how this is going to affect Digium Asterisk we will update here.


Blog Design FreePBX Knowledge Base

Voice recognition and Asterisk.

This is primarily about Googles new Cloud Speech API and Asterisk recordings.

Having worked on many Voice rec systems including Mitels attendant system, Oranges Wildfire virtual assistance and Lumenvox’s add on for Digium’s Asterisk system one thing none could do was transcribe speech such as voicemails and this is what people want. There was a startup in the UK called Spinvox  but as anyone knows this wasn’t all it seems and when I questioned them while working on a project they clammed up and withdrew our testing account and the rest is history as they say.

So now we are many years on and Google have their second API for this service. The first API was a little flaky to say the least and came up with some amusing translations. The cloud version is much better and does a good job with most voice and also can be localised.

So what have we done. Well we have mixed together some existing code we use and created a “mini voicemail” that records your message converts it to text saves it as a voicemail and emails the resultant Text and recording to you.  In the process we did find a few “gotchas” with the API for example a pause of more than a couple of seconds will result in the translation stopping there, also a big one is that the translation takes as long as the recording is, and the API has a 60 second limit. Both of these can be overcome by limiting the record time in Asterisk to 60 seconds and using sox to remove silence of more than a second.

exten => s,n,Record(catline/${UNIQUEID}.wav,3,60,kaq)
/usr/bin/sox /var/lib/asterisk/sounds/catline/${origdir}.wav ${PATH}${origmailbox}/INBOX/${FILENAME}.flac  lowpass -2 2500 silence -l 1 0.1 1% -1 0.8 1% 

As you can see from these snippits of code above we have used variables where possible to that it can be incorporated easily with existing asterisk systems using GUIs such as Freepbx, We use the voicemail greetings that the user recorded and also use the email address thats linked with their mailbox for simplicity of management.

Now having Voicemails as text is nice but where it comes into its own is with structured mailboxes or simply put questionnaires where the caller is asked a number of predefined questions and these are recorded as one single voicemail. We already do this for some customers but they still have to have some one transcribe teh voicemail to text to input it. The quality of the Google translation means that soon they will be able to just copy the text over. Other applications are only limited by your imagination, Such as automated voice menus for Takeaways or Taxi firms.

To be Continued…HERE

Blog Knowledge Base

Do you hate having to use Module admin to update Freepbx

One of my pet hates is having to use module admin to update the Freepbx modules via the GUI. Its not a big deal but as we use SSH to connect to servers and then tunnels to connect to the GUI. Which is all fine unless you have multiple SSH sessions open and things get complicated..

So I have written a small “dirty” Bash script to prompt you through the fwconsole method of updating all or just one module of your choice.

echo ssh freepbx update tool. 2016
echo "Welcome"
echo "We will check for upgrades"

read -p "Do You want to check upgrade status of freepbx modules? (y/n) " RESP
if [ "$RESP" = "y" ]; then
  echo "Glad to hear it"
 fwconsole ma showupgrades

echo "We will now apply all upgrades"

read -p "Do You want to upgrade all freepbx modules? (y/n) " RESP
if [ "$RESP" = "y" ]; then
  echo "Glad to hear it"
 fwconsole ma upgradeall
 echo "OK We will just upgrade the module you choose"
  read -p "Please enter the name of the module you want to upgrade " MODU
  echo "We Will Now Upgrade $MODU"
  fwconsole ma upgrade $MODU 

read -p "Do You want to update permissions? (y/n) " RESP
if [ "$RESP" = "y" ]; then
 echo "Glad to hear it"
fwconsole chown
echo "Dont forget to apply changes on GUI then"

read -p "Do You want to apply the changes? (y/n) " RESP
if [ "$RESP" = "y" ]; then
  echo "Glad to hear it"
 fwconsole reload
  echo "Dont forget to apply changes on GUI then"

As I said it was quick and “dirty” but it does work and can save a bit of time.