FreePBX: Creating a Simple Paging System Without XactDialer

Free PBX Logo

A forum member recently posted about wanting to make a FreePBX paging system with the following requirements.

  • User has a number they can dial to initiate a page.
  • The initiating user will hear a confirmation sound and the system will hang up.
  • The PBX will start a new call to a specified extension that is required to hear the page.
  • When the user answers, they will hear the specified audio prompt before having the call ended.

Creating Extensions

The first thing we need to do is make custom type extensions for this to happen. We will need 1 extension to call to start the pager, and an extension to be used for calls from the paging system. In my case we are making 9999 for calling and starting a page. Users being paged will be called from 9998. This extension has the caller ID name of PAGING which will be useful later.

image 7

Creating our Call File Generator Script

To make this paging system work, we will need to have FreePBX and Asterisk create call files for us. So let’s make the script that will handle it.

  • Logon to your FreePBX via SSH as root.
  • Use the cd command to move to /home/asterisk
cd /home/asterisk
  • Create our script file. Note the name you use here for later. In our case we will make “page.sh”
touch page.sh
  • Make our script executable.
chmod +x page.sh
  • Give permissions to user asterisk to use this file.
chown asterisk:asterisk page.sh
  • Finally, we can edit our script!
nano page.sh

Programming our Call File Generator Script

The below script reads as follows (complete script at the bottom):

Bash

  • This is a bash script so we need to define it as such
#!/bin/bash

Command Line Arguments

  • We will be handling to arguments into this script the originating extension, and the destination extension. The origin will be the paging system extension we made before (9998 in our case), and the the destination will be the phone we want to call. These get saved in variables
if [ "$#" -ne 2 ]; then
    echo "Usage: $0 <OriginatingExtension> <DestinationExtension>"
    exit 1
fi

ORIGIN_EXT="$1"
DEST_EXT="$2"

Validation

  • We then validate the input of the arguments into the script. In my case we are only allowing 4 digit extensions to call 4 digit extensions. You can modify this, but it is always good to sanitize inputs to scripts like this so you cant accidentally call somewhere you don’t want. Like 911.
# Validate 4-digit extensions.
if [[ ! $ORIGIN_EXT =~ ^[0-9]{4}$ ]] || [[ ! $DEST_EXT =~ ^[0-9]{4}$ ]]; then
    echo "Error: Invalid extension format. Only 4-digit extensions are allowed."
    exit 1
fi

Variables

  • Next we need to define some variables.
  • CALLFILE will be the location to store our call file. $$ will be the PID of the spawned process added to the file name
  • TOUCH, CHMOD, MV, ECHO, are the locations for these programs for use later
# Define the temp call file path and paths for commands.
CALLFILE="/tmp/callfile_$$.call"
TOUCH="/usr/bin/touch"
CHMOD="/bin/chmod"
MV="/bin/mv"
ECHO="/bin/echo"

Lookup Caller ID

  • Lookup the caller ID of the extension initiating the call.
  • NOTE THAT THE DATABASE LOOKUP BELOW IS SPECIFICALLY FOR FREEPBX GENERATED EXTENSIONS, IF YOU NEED THIS FOR ANOTHER PLATFORM YOU WILL NEED TO EDIT THIS.
  • The Caller ID number will passed through from the originating extension.
# Fetch CallerID info from the FreePBX database.
CALLERID_NAME=$(asterisk -rx "database show AMPUSER/$ORIGIN_EXT/cidname" | grep "/AMPUSER/$ORIGIN_EXT/cidname" | awk -F': ' '{print $2}')
CALLERID_NUM=$ORIGIN_EXT

Create Our Call File

  • Use touch to actually create the call file to start working with.
# Populate the call file.
$TOUCH $CALLFILE

Populate Call File

  • We need to populate our call file with the required fields so the call actually works.
  • Channel will be PJSIP with the destination extension.
  • Caller ID will be the name we looked up in the database before.
  • Context: paging-audio-playback is a call to a dialplan context. We have not made this yet but it will become more clear soon.
  • Extension and Priority are required in the dialplan. You should know what these are, or don’t worry about them for now.
$ECHO "Channel: PJSIP/$DEST_EXT" >> $CALLFILE
$ECHO "CallerID: \"$CALLERID_NAME\" <$CALLERID_NUM>" >> $CALLFILE
$ECHO "Context: paging-audio-playback" >> $CALLFILE
$ECHO "Extension: s" >> $CALLFILE
$ECHO "Priority: 1" >> $CALLFILE

Set Permissions

  • Set permissions to our call file that we made with chmod.
# Set permissions and move to Asterisk's outgoing directory.
$CHMOD 777 $CALLFILE

Make the Call File Run

  • We then make Asterisk execute the call file by moving it to the outgoing spool.
$MV $CALLFILE /var/spool/asterisk/outgoing/

Complete page.sh Script

#!/bin/bash

# Script to originate a call in Asterisk using call files. CallerID info is fetched from the FreePBX database.

if [ "$#" -ne 2 ]; then
    echo "Usage: $0 <OriginatingExtension> <DestinationExtension>"
    exit 1
fi

ORIGIN_EXT="$1"
DEST_EXT="$2"

# Validate 4-digit extensions.
if [[ ! $ORIGIN_EXT =~ ^[0-9]{4}$ ]] || [[ ! $DEST_EXT =~ ^[0-9]{4}$ ]]; then
    echo "Error: Invalid extension format. Only 4-digit extensions are allowed."
    exit 1
fi

# Define the temp call file path and paths for commands.
CALLFILE="/tmp/callfile_$$.call"
TOUCH="/usr/bin/touch"
CHMOD="/bin/chmod"
MV="/bin/mv"
ECHO="/bin/echo"

# Fetch CallerID info from the FreePBX database.
CALLERID_NAME=$(asterisk -rx "database show AMPUSER/$ORIGIN_EXT/cidname" | grep "/AMPUSER/$ORIGIN_EXT/cidname" | awk -F': ' '{print $2}')
CALLERID_NUM=$ORIGIN_EXT

# Populate the call file.
$TOUCH $CALLFILE
$ECHO "Channel: PJSIP/$DEST_EXT" >> $CALLFILE
$ECHO "CallerID: \"$CALLERID_NAME\" <$CALLERID_NUM>" >> $CALLFILE
$ECHO "Context: paging-audio-playback" >> $CALLFILE
$ECHO "Extension: s" >> $CALLFILE
$ECHO "Priority: 1" >> $CALLFILE

# Set permissions and move to Asterisk's outgoing directory.
$CHMOD 777 $CALLFILE
$MV $CALLFILE /var/spool/asterisk/outgoing/

Wrapping up Our Script

  • Exit and save in nano with Ctrl+X
  • We should now have our filled page.sh script in the /home/asterisk directory with the correct permissions.

Editing extensions_custom.conf

FreePBX is unique that when creating manual dial plans you must do so in extensions_custom.conf

We can edit this using the Admin > Config Edit tool inFree PBX

image 8

Adding Our Custom Contexts

Paging

  • Add a context called [paging] for our paging system. It will look like this.
[paging]
exten => s,1,NoOp(Entering Paging Context) ; Debugging
     same => n,Playback(access-granted)
     same => n,System(/home/asterisk/page.sh 9998 5001)
     same => n,Hangup()
  • [paging] is the name of our custom context
  • The NoOp command will provide feedback in Asterisk console for debugging, it’s always nice to have this
  • Playback(access-granted) will play the access-granted audio file to the caller. This can be any confirmation that the paging system has been dialed.
  • The System() command is what calls the custom script we created. After saying hello-world, we will invoke our page.sh script in /home/asterisk. We will pass 2 arguments to this script. The extension to originate the call from, and the number to call. In my example we are originating a call from the paging system (9998) to the person I want to hear the page at extension (5001)
  • We can now hangup on the person who called the paging system

Paging-audio-playback

You may remember in our page.sh file we created before, there was a line calling a custom context named paging-audio-playback.

$ECHO "Context: paging-audio-playback" >> $CALLFILE 

We need to create this context as well in the dial plan. Think of it as our paging system driver.

  • Add a context called [paging-audio-playback] for our driver. It will look like this
[paging-audio-playback]
exten => s,1,NoOp(Playing Paging Audio)
    same => n,Playback(hello-world)  ; Play "hello-world" message
    same => n,Dial(PJSIP/${DEST_EXT}) ; Dial the intended recipient
    same => n,Hangup()  ; Hang up the call
  • [paging-audio-playback] is the name of our context we are calling in page.sh
  • The NoOp command will provide feedback in Asterisk console for debugging, it’s always nice to have this
  • Playback(hello-world) will prep the audio channel for the call
  • For consistency and clarity, when designing dialplan logic in Asterisk, always ensure that pre-call actions (like playing a sound file) are placed before call initiation commands (like Dial).
  • Starts the call from the phone system itself, using the destination extension we got from earlier.
  • The phone will ring, playback hello world, then hangup the call.

Wrapping Up extensions_custom.conf

When all is said and done your extensions_custom.conf will look something like this. Save and apply changes.

image 9

Adding Custom Destination

  • In FreePBX, go to Admin > Custom Destinations
  • Add a new destination.
image 10
  • Our target will be paging,s,1 to target the [paging] context we created earlier
  • Description is a helpful name for reference used later.

Editing The Paging Input Extension

  • In FreePBX go to Applications > Extensions
  • Edit our Paging Input Extension from earlier. In my case (9999)
  • Chose the Advanced Tab
  • At the bottom, find optional destinations. Set these to the custom Paging destination we made earlier.
  • Technically we should only need the “not reachable” destination, but it can be useful to set all especially for testing.
  • SAVE AND APPLY ALL OUR CHANGES!
image 11

Description of call flow

As setup the following will happen.

  • User calls the extension to start paging (9999)
  • Phone system replies with access granted
  • Phone system initiates a call to the predefined party, as defined in our context in extensions_custom.conf
  • The paging user will receive a call from the PAGING <9998> extension.
  • When they pick up the phone they will hear the hello-world audio file.

Demo

Conclusion

Hopefully this has been helpful and you see the power in this approach. While it might not scale as easily as something like XactDialer, it should get you started in the right direction!