Today’s #FridayFun goal is to create a detailed resource for adding a custom in-call feature code to fpbx. I’ve written how to do this in the past, but never as a stand alone thread. This is not something that I’ve had to do very often, and never on any of my own systems where I’ve been able to monitor the result over time. It’s possible that this technique will evolve.
The Setup
We are going to create an Asterisk dynamic feature, an action that can be initiated by entering specific DTMF tones during an active call. This Asterisk wiki page will form the starting point, with modifications where needed for fpbx specific config.
From the Asterisk CLI, you can see a list of features already defined on a fpbx system with the command:
tangopbx3*CLI> features show
Builtin Feature Default Current
--------------- ------- -------
Pickup *8 *8
Blind Transfer # ##
Attended Transfer *2
Disconnect Call * **
Park Call
One Touch MixMonitor
Dynamic Feature Default Current
--------------- ------- -------
apprecord no def *1
Whatever we do here, we must be careful not to break any of the current features.
Double Feature
As with most (all?) custom Asterisk config in fpbx, we start by editing conf files. And as with all Asterisk custom config in fpbx, you must edit ONLY the conf files specifically reserved for custom config. Taking a look at /etc/asterisk/features.conf
:
;--------------------------------------------------------------------------------;
; Do NOT edit this file as it is auto-generated by FreePBX. All modifications to ;
; this file must be done via the web gui. There are alternative files to make ;
; custom modifications. ;
;--------------------------------------------------------------------------------;
[general]
#include features_general_additional.conf
#include features_general_custom.conf
[applicationmap]
#include features_applicationmap_additional.conf
#include features_applicationmap_custom.conf
[featuremap]
#include features_featuremap_additional.conf
#include features_featuremap_custom.conf
The Asterisk wiki page linked above tells us to edit the [applicationmap]
section of features.conf, but we are warned not to edit this file. The fbpx system includes two external files for this purpose, one only for fpbx to write to (with a similar warning not to edit), and one for custom config. We are going to be editing the file features_applicationmap_custom.conf
.
For today’s purposes we’ll be creating two new feature codes, with these lines in /etc/asterisk/features_applicationmap_custom.conf
:
debug_self => *6,self,Gosub(dump-variables,s,1)
debug_peer => *7,peer,Gosub(dump-variables,s,1)
These two codes define feature codes *6 and *7 which can be used to trigger an event that executes the Asterisk dialplan context dump-variables
. When the user dials the dtmf, it will run the Asterisk dialplan dump-variables
. Depending on which feature code is used, it will run the context either on the channel of the person dialing the code, or the other channel they’re bridged with.
Once this file is saved, and Asterisk reloaded, you will now see new entries when you query the CLI for features:
tangopbx3*CLI> features show
Builtin Feature Default Current
--------------- ------- -------
Pickup *8 *8
Blind Transfer # ##
Attended Transfer *2
One Touch Monitor
Disconnect Call * **
Park Call
One Touch MixMonitor
Dynamic Feature Default Current
--------------- ------- -------
apprecord no def *1
debug_peer no def *7
debug_self no def *6
Custom Dialplan
Having defined the features, we now need to create the dialplan context that it references. For this example, we’re going to run a few lines of custom dialplan when we use the in-call feature code. I have these lines in /etc/asterisk/extensions_custom.conf
[dump-variables]
exten => s,1,Noop(Entering user defined context dump-variables in extensions_custom.conf)
exten => s,n,DumpChan
exten => s,n,Return
Think Globally
In order for the dynamic features to be accessible from the dialing channel, they must be assigned by feature name to the variable DYNAMIC_FEATURES
prior to the channels being dialed. Luckily fpbx defines this as a global variable so there is no need to hook dialplan, we just need to re-define the global with our new feature names, being careful to also preserve whatever values fpbx has assigned. To get the current value of DYNAMIC_FEATURES
from the bash prompt
root@tangopbx3:~# asterisk -x "dialplan show globals" | grep DYNAMIC_FEATURES
DYNAMIC_FEATURES=apprecord
To add our own features to the global, edit the file /etc/asterisk/globals_custom.conf
and add the lines:
DYNAMIC_FEATURES = ${DYNAMIC_FEATURES}#debug_self#debug_peer
save and reload asterisk, then confirm that the new features have been added
root@tangopbx3:~# asterisk -x "dialplan show globals" | grep DYNAMIC_FEATURES
DYNAMIC_FEATURES=apprecordl#debug_self#debug_peer
The Result
Once the above is done, you can do one final fwconsole reload
and then enter the asterisk console with verbosity at 3+ asterisk -rvvv
. Make a call between two endpoints, and with the active channel up, watch the asterisk console as you dial the two new feature codes, *6 and *7. You will see something like this:
-- PJSIP/1.us-east.clearlyip.com-00000025 Internal Gosub(dump-variables,s,1) start
-- Executing [s@dump-variables:1] NoOp("PJSIP/1.us-east.clearlyip.com-00000025", "Entering user defined context dump-variables in extensions_custom.conf") in new stack
-- Executing [s@dump-variables:2] DumpChan("PJSIP/1.us-east.clearlyip.com-00000025", "") in new stack
Dumping Info For Channel: PJSIP/1.us-east.clearlyip.com-00000025:
================================================================================
Info:
Name= PJSIP/1.us-east.clearlyip.com-00000025
Type= PJSIP
UniqueID= 1748618100.143
LinkedID= 1748618100.143
CallerIDNum= 19203833100
CallerIDName= Clearly Ip
*snip*
In addition to seeing the current channel info, you get all of the channel variables with their values.
Can We Make Money With This?
Maybe. If we alter the custom dialplan to comment out the DumpChan and display two specific variables like this:
[dump-variables]
exten => s,1,Noop(Entering user defined context dump-variables in extensions_custom.conf)
; exten => s,n,DumpChan ; commentted to reduce log lines
exten => s,n,Noop(Activating Channel: ${DYNAMIC_WHO_ACTIVATED})
exten => s,n,Noop(Bridged Channel: ${BRIDGEPEER})
exten => s,n,Return
Now reload, and do *6 from a live call. You will see lines like this in the Asterisk console and full log
-- PJSIP/4002-0000002b Internal Gosub(dump-variables,s,1) start
-- Executing [s@dump-variables:1] NoOp("PJSIP/4002-0000002b", "Entering user defined context dump-variables in extensions_custom.conf") in new stack
-- Executing [s@dump-variables:2] NoOp("PJSIP/4002-0000002b", "Activating Channel: PJSIP/4002-0000002b") in new stack
-- Executing [s@dump-variables:3] NoOp("PJSIP/4002-0000002b", "Bridged Channel: PJSIP/2.us-east.clearlyip.com-0000002a") in new stack
-- Executing [s@dump-variables:4] Return("PJSIP/4002-0000002b", "") in new stack
== Spawn extension (from-internal, , 1) exited non-zero on 'PJSIP/4002-0000002b'
-- PJSIP/4002-0000002b Internal Gosub(dump-variables,s,1) complete GOSUB_RETVAL=
We see two channel variables, one is the channel that dialed the feature code, and the other is the channel it’s bridged to. Regulars might think there is a link between this post and the one I did a week ago.