Concurrent Line Usage

PBX Metrics…  One of the first things folks want to do with a fancy phone system is to gain access to the data that previously may have been a huge mystery.  Call centers live and die by various metrics or measurements like average handle time, occupancy rate, etc. but sometimes even basic data like maximum concurrent calls can be elusive.  Knowledge is power and also lends itself to saving big bucks!  Recently a client about to change phone service had to figure out how many outside lines they really needed.  They had 15 or 16 lines, the client thought they probably only needed 9 or 10.  No need to guess I said, lets look at the data…

Their FacetPhone system has super detailed logs about almost everything that the PBX thinks and does.  Of particular interest here is line state changes.  By examining line state changes it is easy to calculate how many lines are in use at a given point in time and tracking the max in use over a period of time provides a real hard metric that matches reality.  A line state could be things like “idle”, “ringing”, “disabled”, “connected”, etc.  The PBX process of FacetPhone (fp_pbx) logs line state changes like this:

2009-03-17 12:00:37 fp_pbx   :MSGS  :  #<line=23><user_line_stat=1>

Where “user_line_stat” can be one of:

  • 0 – Disabled
  • 1 – Idle
  • 2 – Ringing
  • 3 – Dialing
  • 4 – Connected to Station
  • 5 – Connected to IVR
  • 6 – On Hold
  • 7 – Out of Order
  • 8 – Idle w/Auto-attendant

Knowing that, we can break things down into whether the line state indicates the line is in use or is not in use.  A state of “Disabled”, “Idle”, “Out of Order”, or “Idle w/Auto-attendant” would be for a line not currently in use.  A state of “Ringing”, “Dialing”, “Connected to Station”, “Connected to IVR”, or “On Hold” would be for a line that is currently in use.  Okay!  Now we must simply follow the log entries and every time a line changes from a “not in use” state to an “in use” state we add to the current lines in use counter.  Every time a line state changes from an “in use” state to a “not in use” state we subtract from the current lines in use counter.  This task is not too overwhelming when you think about it like that.  There is no need to calculate call start times plus durations and try to figure out if the timeline of one call touches the timeline of another call or anything like that at all…  A simple UNIX/Linux shell script can handle this task quite nicely!

There are so many ways to do this… but if we want to make a pretty report out of the line state changes, and provide the maximum concurrent call data that the client needs, here is one way to do that…

First, “grep” out the pertinent log entries — no need to parse through things we are not interested in for this…

grep -h "fp_pbx.*user_line_stat" $LOGS

This will provide us a stream of only log entries like the one above.  Useful information from these log entries are:  date, time, line number, and state.  One could use a combination of “echo’s” and “cut’s” to parse out the data, but the powerful “sed” command can do it in one fell swoop:

| sed -n -u 's/^\([^ ]*\) \([^ ]*\).*line=\([0-9]*\)><user_line_stat=\([0-9]*\)
>.*$/\1 \2 \3 \4/p'

Okay, that is a little tricky looking, but it is really not too bad if you understand a little about regular expressions…  We’re using the “sed” substitute command (s/xxx/yyy/) which substitutes the replacement string (yyy) for the matched regular expression (xxx) in its input.  In our log entry, I’ve highlited the stuff we want to parse out to use in our report:

2009-03-17 12:00:37 fp_pbx   :MSGS  :  #<line=23><user_line_stat=1>

The trick we’re using here is to use sub-expression matching to grab part of our pattern to use later with the special escapes “\1″, “\2″, “\3″, and “\4″.  A sub-expressionss is matched inside a left and right parenthesis.   The first matching sub-expression can be referred to later as “\1″, the second as “\2″, etc.  So to put a matching sub-expression with the date in “\1″ we match from the beginning up to the first space with “^\(^ ]*\)”, then after skipping 1 space we do the same thing to put the time into “\2″.  All of the digits after the “line=” are put in “\3″ with “\([0-9]*\)” and then the same for putting the line state in “\4″.  Bottom line is that this “sed” command takes the log entry line above and spits out what we are interested in:

2009-03-17 12:00:37 23 1

Let’s take that stream of line state information and read it in to variables for counting and reporting…

| while read DATE TIME Y Z

So now it is just a matter of keeping track of the old line state and comparing it to the new new line state and when a line changes from a “not in use” state” to an “in use” state, then bump the concurrent lines in use counter up by one and when a line changes from an “in use” state to a “not in use” state reduce the counter by one.  Keep track of the high count to get a max lines in use at any given point number.  That’s it — pretty easy!  To turn this into an interesting report that shows the line states at every point, we can keep each line’s state in per line variables with a character code indicator so at a glance one can tell the state of any given line at any given point in time by reading the report.  At each line state change we print out all of the line states and end up with a report that looks like this:

    FacetPhone Concurrent Lines in Use & Status Report

    Line State Codes:

        x - Disabled                T - Connected (Talking)
        : - Out of Order            I - Connected to IVR
        R - Ringing                 H - On Hold
        D - Dialing                 _ - Idle

    Columns: C - Current, H - High, L - Line#, S - State, 1234567890 - Lines

   DATE      TIME    C  H L #=S  1234567890 1234567890 1234567890 1234567890 1234567890
2012-10-01 16:01:29  1  1 L 1=_  _T________ __________ __________ __________ __________
2012-10-01 16:01:34  2  2 L23=D  _T________ __________ __D_______ __________ __________
2012-10-01 16:01:34  2  2 L23=T  _T________ __________ __T_______ __________ __________
2012-10-01 16:01:34  3  3 L 1=R  RT________ __________ __T_______ __________ __________
2012-10-01 16:01:34  3  3 L 1=I  IT________ __________ __T_______ __________ __________
2012-10-01 16:01:38  3  3 L 1=T  TT________ __________ __T_______ __________ __________
2012-10-01 16:10:13  3  3 L 2=T  TT________ __________ __T_______ __________ __________
2012-10-01 16:20:09  4  4 L 3=R  TTR_______ __________ __T_______ __________ __________
2012-10-01 16:20:15  4  4 L 3=T  TTT_______ __________ __T_______ __________ __________
2012-10-01 16:21:05  3  4 L 3=_  TT________ __________ __T_______ __________ __________
2012-10-01 16:28:59  4  4 L22=D  TT________ __________ _DT_______ __________ __________
2012-10-01 16:29:00  4  4 L22=T  TT________ __________ _TT_______ __________ __________
2012-10-01 16:29:00  5  5 L 3=R  TTR_______ __________ _TT_______ __________ __________
2012-10-01 16:29:05  4  5 L22=_  TTR_______ __________ __T_______ __________ __________
2012-10-01 16:29:05  3  5 L 3=_  TT________ __________ __T_______ __________ __________
2012-10-01 16:29:05  3  5 L 3=_  TT________ __________ __T_______ __________ __________
2012-10-01 16:29:28  4  5 L22=D  TT________ __________ _DT_______ __________ __________
2012-10-01 16:29:28  4  5 L22=T  TT________ __________ _TT_______ __________ __________
2012-10-01 16:29:43  3  5 L22=_  TT________ __________ __T_______ __________ __________
2012-10-01 16:30:18  2  5 L 2=_  T_________ __________ __T_______ __________ __________

    Maximum Concurrent Calls: 5
    7423 Line State Changes Parsed

Okay!  There’s our answer… The most calls the client had at any given moment was 5.  They’ve been paying for 15 or 16 lines for years but the most they actually used at any given moment during our reporting period was 5 lines.  Quite often perception benefits from a reality check – engineering is the art of doing for 10 shillings what any fool can do for a pound…  Here is the source of the complete reporting script.  Hopefully it can give you some ideas about how easily a custom report can be created.

FACETPHONEDIR=/usr/facetphone
[ -f /etc/facetphonedir ] && . /etc/facetphonedir

[ -f "$1" ] && LOGS="$*" || {
    LOGS="$FACETPHONEDIR/logs/`date +%Y_%m`/facetphone.log*"
    LOGS="$LOGS $FACETPHONEDIR/logs/facetphone.log"
}

MAXLN=`sed -n '$s/<line=\([0-9]*\).*/\1/p' $FACETPHONEDIR/config/lines.cfg` I=0 STATES= HEADER="   DATE      TIME    C  H L #=S " while [ $I -lt $MAXLN ] do     I=`expr 1 + $I`     eval LINE$I=_     J=`expr $I % 10`     [ $J = 1 ] \ 	&& { HEADER="$HEADER 1"; STATES="$STATES \$LINE$I"; } \ 	|| { HEADER="$HEADER$J"; STATES="$STATES\$LINE$I"; } done LEGEND="    Line State Codes:         x - Disabled                T - Connected (Talking)         : - Out of Order            I - Connected to IVR         R - Ringing                 H - On Hold         D - Dialing                 _ - Idle     Columns: C - Current, H - High, L - Line#, S - State, 1234567890 - Lines" echo -e "\n    FacetPhone Concurrent Lines in Use & Status Report" echo -e "\n$LEGEND\n" echo "$HEADER" COUNT=0 HIGH=0 I=0 grep -h "fp_pbx.*user_line_stat" $LOGS \     | ( sed  -n -u 's/^\([^ ]*\) \([^ ]*\).*line=\([0-9]*\)>.*$/\1 \2 \3 \4/p'; echo FIN; ) \
    | while read DATE TIME Y Z; do

	[ "$DATE" = "FIN" ] && {
	    echo -e "\n    Maximum Concurrent Calls: $HIGH"
	    echo "    $I Line State Changes Parsed"
	    break
	}

	I=`expr 1 + $I`
	[ `expr $I % 40` = "0" ] && { echo; echo "$HEADER"; }

        [ "$Z" = "0" ] && Z=x       # Disabled
        [ "$Z" = "1" ] && Z=_       # Idle
        [ "$Z" = "2" ] && Z=R       # Ringing
        [ "$Z" = "3" ] && Z=D       # Dialing
        [ "$Z" = "4" ] && Z=T       # Connected - Station
        [ "$Z" = "5" ] && Z=I       # Connected - IVR
        [ "$Z" = "6" ] && Z=H       # On Hold
        [ "$Z" = "7" ] && Z=:       # Out of Order
        [ "$Z" = "8" ] && Z=_       # Idle - Auto-attendant

        OLDZ=`eval echo \\$LINE$Y`
        [ "$OLDZ" = "_" -o "$OLDZ" = ":" -o "$OLDZ" = "x" ] \
	    && [ "$Z" != "_" -a "$Z" != ":" -a "$Z" != "x" ] \
		&& COUNT=`expr $COUNT + 1`
        [ "$OLDZ" != "_" -a "$OLDZ" != ":" -a "$OLDZ" != "x" ] \
	    && [ "$Z" = "_" -o "$Z" = ":" -o "$Z" = "x" ] \
		&& COUNT=`expr $COUNT - 1`

        [ "$COUNT" -gt "$HIGH" ] && HIGH=$COUNT
        eval LINE$Y=$Z

        printf "%s %s%3d%3d L%2d=%c  " $DATE $TIME $COUNT $HIGH $Y $Z
	eval echo $STATES
    done

echo -e "\n$LEGEND\n"

Gmail (TTS) Text-to-Speech

Mail Call - Bugle MusicMail Call!

Don’t have time to constantly check your email?  Not on the computer 24/7 with one window in the corner checking your email every few minutes?  Not a problem…  This little diddy shows how your phone system can do it for you.  Through a combination of a few background utilities and built in TTS (Text-to-Speech) capabilities, your PBX can check your email, call you up, and read you email to you.  If you only want this personal delivery method for special messages — not a problem.  Just create a filter on your main incoming mailbox that catches the special messages and forwards them over to a different email account which is used just for this purpose.  Email accounts are cheaper than a dime-a-dozen these days.  Create a new one on Gmail if you wish, and have your email read to you over the phone with TTS like this…

This project can be broken down into a few simple steps:

  1. Fetch the email from your Gmail account
  2. Parse the message to extract the text
  3. Convert the text of the message to speech with TTS
  4. Call your designated number and speak the message

I’m doing this on a FacetPhone Linux system which is rich with tools.  The ones I use to fetch email from my Gmail account and extract the text:

  • fetchmail to login to Gmail and download a message
  • small shell script using grep and sed to strip out the text message
  • qprint to decode the MIME-encoded text message to plain text

To fetch the email from Gmail I created a new user called “mailcall”.  In that user’s home directory I created a “fetchmail” configuration file with the details about how to login and download email messages.

   adduser mailcall

   "/home/mailcall/.fetchmailrc":
   =============================================================================
   poll pop.gmail.com proto POP3 port 995 user USER@gmail.com password ********
   ssl
   keep
   fetchlimit 1
   mda "/usr/local/bin/mailcall %T"
   set postmaster root
   set no bouncemail
   =============================================================================

The manpage on “fetchmail” explains the configuration file in more detail, but it basically tells “fetchmail” to login to the Gmail account “USER@gmail.com” with the specified password, download 1 message at a time, leave a copy of the message on the server, and pass the message to the “mailcall” handler script to parse it and deal with it.

Put a simple “mailcall” handler script in place to catch the email

:
# mailcall - fetchmail MTA to decode text from Gmail message.
#

LOG=/tmp/mailcall.log
MFILE=/tmp/mailcall.email
TFILE=/tmp/mailcall.txt
echo `date "+%Y/%m/%d %H:%M:%S"` mailcall: $* >> $LOG

(   echo
    echo `date "+%Y/%m/%d %H:%M:%S"` mailcall: $* 
    echo
    cat
) > $MFILE

BOUNDARY=--`grep Content.*boundary= $MFILE | cut -d= -f2- | tr -d\"`
set `grep  -m 2 -n "^$BOUNDARY" $MFILE | cut -d: -f1`
sed "`expr $1 + 4`,`expr $2 - 2`!d" $MFILE \
    > $TFILE

and then test your “fetchmail” configuration manually with:

su - mailcall -c "fetchmail -v"

fetchmail: 6.3.6 querying pop.gmail.com (protocol POP3) at Sun 21 Oct 2012 11:48:58 PM CDT: poll started
Trying to connect to 173.194.77.109/995...connected.
fetchmail: Issuer Organization: Google Inc
fetchmail: Issuer CommonName: Google Internet Authority
fetchmail: Server CommonName: pop.gmail.com
fetchmail: Subject Alternative Name: pop.gmail.com
fetchmail: pop.gmail.com key fingerprint: FF:69:18:0D:72:8C:17:61:F5:EE:FC:F9:85:7D:F5:0E
fetchmail: POP3< +OK Gpop ready for requests from 74.7.152.227 w9pf17085673igs.1
fetchmail: POP3> CAPA
fetchmail: POP3< +OK Capability list follows
fetchmail: POP3< USER
fetchmail: POP3< RESP-CODES
fetchmail: POP3< EXPIRE 0
fetchmail: POP3< LOGIN-DELAY 300
fetchmail: POP3< TOP
fetchmail: POP3< UIDL
fetchmail: POP3< X-GOOGLE-VERHOEVEN
fetchmail: POP3< X-GOOGLE-RICO
fetchmail: POP3< .
fetchmail: POP3> USER USER@gmail.com
fetchmail: POP3< +OK send PASS
fetchmail: POP3> PASS *
fetchmail: POP3< +OK Welcome.
fetchmail: POP3> STAT
fetchmail: POP3< +OK 32 389830
fetchmail: POP3> LAST
fetchmail: POP3< -ERR Not supported
fetchmail: Not supported
fetchmail: POP3> UIDL
fetchmail: POP3< +OK
fetchmail: POP3< 1 GmailId138787949ac7f285
fetchmail: POP3< 2 GmailId138787bb894882c6
fetchmail: POP3< 3 GmailId138787cd0544049f
[ ... ]
fetchmail: POP3< 30 GmailId13a71c21be3e864a
fetchmail: POP3< 31 GmailId13a7b3002cac397d
fetchmail: POP3< 32 GmailId13a7c23e3019e870
fetchmail: POP3< . 32 messages for USER@gmail.com at pop.gmail.com (389830 octets).
fetchmail: POP3> LIST 1
fetchmail: POP3< +OK 1 2771
fetchmail: POP3> RETR 1
fetchmail: POP3< +OK message follows reading message USER@gmail.com@gmail-pop.l.google.com:1 of 32 (2771 octets)
#*********************************.******************************.********* not flushed
fetchmail: fetchlimit 1 reached; 31 messages left on server gmail-pop.l.google.com account USER@gmail.com
fetchmail: POP3> QUIT
fetchmail: POP3< +OK Farewell.
fetchmail: 6.3.6 querying pop.gmail.com (protocol POP3) at Sun 21 Oct 2012 11:49:01 PM CDT: poll completed
fetchmail: Query status=13 (MAXFETCH)
fetchmail: normal termination, status 13

This should leave you with “/tmp/mailcall.email”, the full email message complete with headers, and “/tmp/mailcall.txt”, the first MIME part decoded as simple text.  This scheme could be expanded into a verbal web-browser if you wish. [Note to self: Let's do it!] However, in this case… let’s just simplify any URL’s to be spoken as… “URL google.com” versus spelling “h-t-t-p-:-/-/-w-w-w-.-g-o-o…” etc.  Our good buddy “sed” can do that for us with interesting syntax like this:

sed 's~https\?://w\?w\?w\?\.\?\([-\.A-Za-z0-9]*\)[^[:space:]]*~\1~'

Piping the decoded text through this “sed” command will take URL’s like this:

https://www.google.com/#hl=en&safe=off&pwst=1&spell=1&q=butterfly

and convert them into much more pleasant on the ears webpage names like this:

google.com

[ to be continued... ]

Teleworking from the Jungle

Six or seven years ago… or maybe it was five, a friend called me up out of the blue and said, “We gotta take the women on vacation… I gotta buddy that’s got condos in Costa Rica and he said we can stay in one for a week if we just leave $25 for the maid.  Wanna Los Sueños Resort - Costa Ricago?”  I had never considered visiting Costa Rica before, but it sounded like a grand adventure to me, so I told him,  ”Let’s go!”  I started studying up on Costa Rica and by the time we hit Costa Rica, I knew more about the country than our tour guide…  Fast forward a few months and this buddy and I found a land-owner in Costa Rica that needed a PBX and we did a little bartering…  He got a good deal on a fancy phone system and we got a good deal on a farm.  We started dividing up the farm into lots to sell and built a little airstrip.  In 3 months we sold enough lots to pay for the farm, in 3 more months we sold enough lots to pay for infrastructure.  During this time, being the partner with mostly sweat equity, I had to go down to Costa Rica every other month or so to help further along the project.  Every time I travelled with a laptop loaded with a softphone and a nice headset so I could hit Internet cafés in any little village and make calls back to the office.  After 6 or 7 months of this it occurred to me that I could actually work from Costa Rica — the majority of my job is dealing with customers on the phone and fixing things over the Internet.  D’oh!  I don’t know why it took so long to think of that, but I mentioned the possibility of teleworking from Costa Rica to the president of our company and he said if I thought I could do it to go for it…  Wow!  Super-WOW!

 Bienvenidos a la jungla!

It took all of about 4 weeks for me to get my affairs in order… I packed 2 suitcases, a backpack, and my trusty laptop and hopped a plane for my next great adventure.  In one suitcase I had a spare computer, flat-screen monitor, and a UPS.  The other had a VPN router, a wireless router with extender antennae, an IP phone, hot-plate, and some camping gear.  I managed to squeeze a few clothes into my backpack along with a jungle hammock and that’s all I took with me as I left the rat-race…

I found a nice apartment to rent in a little town at the very end of the DSL line halfway up a sleeping volcano.  It took almost 2 weeks to get a DSL circuit installed — maximum bandwidth available… 128 kbps.  Snapgear SG300 RouterI figured if I had to I could go with G.729 compressed audio, but calculating 64 kbps for G.711 plus a little extra for packet and layer 2 overhead, 90 kbps, it seemed doable with the better audio.  G.711 audio is slightly better quality and more robust in that if you lose a packet or two, you lose much less of the audio stream than you would with the more compressed G.729 audio.  If my Internet connection turned out not to be good enough for VoIP, I had backup plans to work from an Internet café at the bottom of my volcano — they had 1 mbps bandwidth and only charged $0.40 USD per hour.  The phone company told me it was possible to get 4 mbps at the bottom of the volcano, so renting some office space down there was a fall-back option as well.  Turns out my 128 kbps DSL circuit worked out okay.  It didn’t leave much room for surfing the web or doing other things online while using the bandwidth for voice, but I worked over that circuit at the end of the Internet on the edge of the jungle for over 1 year before high-technology crept up my volcano to provide me with better options.

Costa Rica's Connection to the Internet

Now-a-days, there are 4 different submarine cables for my packets to reach the rest of the Internet-connected world… and I’m teleworking in style!  I’ve got 3 Internet connections (DSL, Cable, 3G) in my Costa Rica office, 8 UPS battery backups (1.5 hours of power), and more computer horse-power than the local Internet café.

For a teleworker, the audio quality on VoIP phone calls can be excellent with the help of QoS (Quality of Service) priority queuing in perimeter routers.  Without QoS, at any bandwidth bottleneck where voice and data traffic compete, voice traffic will eventually get delayed by data traffic resulting in poor quality audio.  QoS is a must have for the teleworker using VoIP unless using separate Internet connections for voice traffic and data traffic.  Nobody notices if a data packet is slightly delayed (100, 200, 300 ms or more) but a delayed voice packet might as well be pitched — one cannot rewind an ongoing Traffic Jams - Just say No!conversation to insert delayed packets after the voice stream has moved on.  VoIP equipment (IP Phones, media gateways, etc.) can deal with a very small variable delays, up to the size of its jitter buffer, but anything that doesn’t make it in the jitter buffer might as well have not been sent in the first place because it is too late to make it in the voice stream.  Packet jitter greater than 30 ms degrades voice quality significantly.  Packet latency (one-way) can be a problem if the latency gets upwards of 150 – 200 ms or worse.  Latency greater than 200 ms will usually result in people talking over each other.  Packet loss ideally should be 0%, although clever PLC (packet loss concealment) techniques can pretty well gloss over a single missing packet (20 ms of voice.)  The basic PLC technique used in the G.711 codec is to replay the audio from the last received packet which works pretty well with human speech because the audio doesn’t change a lot from one 20 ms sample to the next (that’s 50 samples per second.)  However, get 2 or more lost packets in a row and you’ve got noticeable degradation of voice quality.  Mathematically speaking, with 20 ms samples of audio, a packet loss of 1% will result in a loss of 2 consecutive packets every 3 minutes on average.

Dallas to Costa Rica in 45 short hours!

The take home message is that voice quality for a teleworker using VoIP can be every bit of as good as that of an office worker stuffed in a cubicle.  But to be successful, one must have QoS configured properly for a shared (data & voice) Internet connection, packet loss should be (way) less than 1%, packet jitter less than 20 ms, and packet latency less than 150 ms.  Put that in your backpack and you can work from anywhere in the world with an Internet connection.  As I type this I’m sitting in an open-air office, midway up a volcano in Costa Rica, with the jungle in my backyard and warm Pacific beaches just down the mountain.  Clients call my Dallas phone number and through the magic of VoIP my phone rings 3800 kilometers away.  Unless someone hears hungry parrots squawking outside my office, nobody knows that I’m not sitting right next door.  That is Pura Vida, my friend.

Dialing out of FileMaker Pro

“We can dance if we want to…”

Earlier this year I attended Macworld/iWorld in San Francisco.  It was the 2nd year we’ve had a booth at Macworld promoting the FacetPhone PBX running on a Mac and the interest amongst Mac users has been high.  An attendee asked about being able to dial out of his FileMaker database and I told him, “Of course!  If you can make a system call out your FileMaker database, then we can dial your contact numbers…”  He assured me that it was an easy thing to do in FileMaker Pro and we left it at that.  Trade-shows can be pretty hectic and I didn’t think any more about it…

FacetPhone runs on a Mac!

A few weeks later one of our sales associates called me with a prospect asking about dial-enabling their FileMaker Pro database — time to put up or shut up…  I bought a copy of FileMaker Pro 12, hopped on a Mac mini and wrote up this little tutorial about dialing out of FileMaker Pro.  I figured you can do anything with AppleScript, right?  So how hard could it be… Turns out it really was pretty easy.  FileMaker Pro was very easy to customize and of course, you CAN do almost anything with AppleScript.  Here’s how I did it for a quickie demo and this is how you can do it too…

Dialing from a command-line or with a system call is easy with FacetPhone.  Simple utilities for dialing and retrieving caller ID are part of FacetPhone’s “UTAPI” (Universal Telephony Application Programming Interface) feature.  The 2 main utilities are “fp_dial” and “fp_cid”.   To dial with “fp_dial”, you provide the phone number to dial and the FacetPhone user whose phone will place the call.  For this demo I made the assumption that Mac usernames match FacetPhone usernames to keep things simple, but if they don’t a simple lookup table can be used to correlate the two…  So, to dial from the command-line, all we need is something like this:

fp_dial -n 9,972-985-9901 -u $USER

      -n NUMBER_TO_DIAL
      -u FACETPHONE_USER
To fetch caller ID from the current or last phone call for a user is even easier:
fp_cid -u $USER

      -u FACETPHONE_USER

fp_cid -u $USER -n
fp_cid -u $USER -n -s

      -n  (return DNIS info)
      -s  (return call state)

The reply is an easy to parse text string with Caller ID Number, Name, and if requested, the DNIS and call state (“active” or “completed”).  A reply could look like one of these examples:

<number=9729859901><name=FACETCORP>
<number=9729859901><name=FACETCORP><dnis=6920>
<number=9729859901><name=FACETCORP><state=active>
<number=9729859901><name=FACETCORP><dnis=6920><state=completed>

I’ve got to admit, I was impressed with how easy FileMaker Pro was to use.  For this demo, I made a simple Contacts database using one of the stock templates and populated it.  Relevant phone number fields are “Work Phone”, “Mobile Phone” and “Home Phone.”  There are many ways to dial-enable an application, in this case I opted to use simple buttons a user could click to dial each of the different phone numbers and a button for fetching caller ID information.  I cloned the existing “Contact List” button to make a “Dial Work”, “Dial Mobile”, “Dial Home”, and a “Find Caller” button.

Contact Record with Dial Buttons

To define a button’s functionality in FileMaker, click “Edit Layout”, right-click the button, and select “Button Setup”.  Dialing is a simpler task than looking up Caller ID.  Calculated AppleScript is an easy mechanism to use to get the phone number field contents for our dial command.  So for each of the Dial buttons, select “Perform Applescript”, “Specify…”, then “Calculated AppleScript”, and “Specify…”

Edit Dial Button

In the Calculated AppleScript area, enter this snippet of code, including each of the double-quote marks and everything else exactly as shown as 1 line:

"do shell script \"/usr/facetphone_utapi/bin/fp_dial -u $USER -n 9" & GetField( "Work Phone" ) & "\""

Repeat the Button Setup for each Dial button, changing only the “GetField” parameter to match corresponding phone number fields.

The “Find Caller” button functionality requires a bit more involved AppleScript. We want to fetch the Caller ID information, parse it, then tell FileMaker Pro to search the phone number fields for the Caller ID phone number.  So for the “Find Caller”, select “Perform AppleScript”, “Specify…”, then “Native AppleScript”.

Edit Find Caller Button

In the Native AppleScript area, enter this AppleScript code:

--
-- $Date:	2012/03/12
-- $Revision:	1.0.a
--
-- fp_cid_search.scpt
--
--  Searches for Caller ID Number of incoming FacetPhone call in Filemaker
--  Contacts database.  If Caller ID Number is not found then a new Record
--  can be created.  FacetPhone's "fp_cid" program used to retrieve Caller ID
--  of the current or last call for logged in user sends back a reply like this:
--
--	<number=2817318429><name=WIRELESS CALLER>
--
--  Assumes 10-digit phone numbers w/hyphens stored in contact records:
--
--	Work Phone, Mobile Phone, and Home Phone
--
--  For example:
--
--	Work Phone: 972-985-9901
--
--  Tested on Mac OS X 10.7.4 with:
--
--	FacetPhone v5.0, Build 715
--	AppleScript 2.2.1
--	FileMaker Pro 12.0v1
--
--
-- 2012/03/12  Eric Yundt - FacetCorp Support, support@facetcorp.com
--
-- ---------------------------------------------------------------------------
--

set cidLine to do shell script "/usr/facetphone_mac_utapi/bin/fp_cid -u $USER"
set cidNum to text 9 thru ((offset of ">" in cidLine) - 1) of cidLine

-- set cidNum to "1234567890"
-- set cidNum to "No caller id"
-- set cidNum to "103"

if length of cidNum is 10 then
  set cidNum2 to text 1 thru 3 of cidNum & "-" & text 4 thru 6 of cidNum & "-" & text 7 thru -1 of cidNum
else
  set cidNum2 to cidNum
end if

tell application "FileMaker Pro"
  tell database "Contacts"
    try
      show (every record whose cell "Work Phone" = cidNum2 or cell "Mobile Phone" = cidNum2 or cell "Home Phone" = cidNum2)
    on error
      try
        display dialog "Caller ID not found. [" & cidNum & "]" buttons {"Cancel", "Create New Record"}

        if result = {button returned:"Create New Record"} then
          set newRecord to create new record

          try
            if cidNum mod 1 is 0 then
              tell newRecord
                set cell "Work Phone" to cidNum2
              end tell
            end if
          end try

          show newRecord
        end if

      end try
    end try
  end tell
end tell

This little AppleScript works like this…

1. Call “fp_cid” to get Caller ID information
2. Parse reply to extract Caller ID number part
3. Insert hyphens to convert number into pretty phone format
4. Search database phone number fields for Caller ID number
5. If no match is found, offer to create new record
6. Fill Work Phone field of new record with Caller ID number.

That’s pretty much all there’s to it!

Salma Hayek on Line 3?

Salma Hayek is on the phone?  Really?!  Why, yes… I believe so…

Salma Hayek on the Phone

While describing the differences between displays on various Polycom IP phones, a client asked me if I could put Salma Hayek on the phone.  He was joking (I think) but I told him of course!  20 minutes later while he was learning about other PBX features, I provided the proof, and here is how I did it…

The Polycom SoundPoint IP 450, 550, 560, 650, and 670 IP phones all support custom background images.  They support both BMP and JPEG images and up to 6 custom background images can be loaded into the phones from their boot server as they download their configurations.  A default image can be preset and the rest are available via user preferences.  This really is pretty easy.  Whether it is a company logo or a foto of the kid, there is no excuse for having a dull phone on your desk.

The first step is to find an image.  Salma is evidently pretty popular because I had plenty of Salma fotos to choose from… I selected a pretty one and loaded it up in GIMP to resize it to better fit the Polycom 550 screen — the max image sizes are 250 x 116 pixels for the 450 and 320 x 160 pixels for the 550 and above.  The foto I grabbed was 1024 x 768 pixels sized for a PC desktop background image so I resized the canvas to make the image 1536 x 768 pixels (same aspect ratio as 320 x 160) and then rescaled the image to 320 x 160 pixels.   The 550 puts line key labels on the left-side of the display, so I centered the picture about 2/3rds of the way to the right side and filled in the left side of the canvas with a blended blur that nobody would notice on the 550′s grayscale display.

The Polycom IP phones can load their configuration in a number of ways — I normally use regular FTP to provision the phones and collect logs, etc.  So I uploaded the resized image to the Polycom user (PlcmIpSp) home directory on the FacetPhone server and modified one of the phone’s config files to include the XML snippet below telling the phone about the custom background image.  I’m sure Polycom configuration files will be a subject of a future post, but in this case I have the phones loading an “adjust[MACADDR].cfg” file for various soft-keys and other custom tweaks, so that was the perfect spot for this snippet:

<!– START: IP 550/560/650 Background Image –>

   <bg>
      <bg.hiRes>
         <bg.hiRes.gray bg.hiRes.gray.selection=”3,1″>

            <bg.hiRes.gray.bm
               bg.hiRes.gray.bm.1.adj=”0″
               bg.hiRes.gray.bm.1.name=”SalmaHayek.jpg”
            </bg.hiRes.gray.bm>

         </bg.hiRes.gray>
      </bg.hiRes>
   </bg>

<!– END: IP 550/560/650 Background –>

The Polycom 550 has a hi-res grayscale display, so it uses the “bg.hiRes.gray” parameters for its background image configuration. In this case I’m only loading one image so I only included a “.1.” section.  If I wanted to load more I could have replicated the “.1.adj” and “.1.name” parameters up to “.6.adj” and “.6.name” to provide for up to 6 background images.  The “.adj=” parameter must be an integer between -8 (to darken the background) and 3 (to lighten the background.)  The “.name=” parameter is the image file name in the Polycom user home directory.  The “.selection=3,1” parameter tells the phone to use an image background (selection=3,1) and to use image “.1.” (selection=3,1).  See the “features.cfg” file that comes with the Polycom SIP load for other related parameters.

Okay, with this in place we’re ready for a test…  Reboot the phone (“Menu, 3, 1, 6, Yes” or “Menu, 3, 1, 8, Yes” or Press and hold for 4 secs the “+, -, Mute, Messages” keys) and see if you really get Salma Hayek on the phone.  Worked for me!  ;-)

Server Monitor IVR for Admins

LinuxScrew, Artem Nosulchik’s Linux and Open Source blog, had a nice review of Linux server web-based monitoring tools earlier this year.  There is some nice stuff out there designed to help the overtasked sysadmin stay on top of a server herd.  These tools monitor CPU load, memory usage, disk space, etc.  Here’s how to get the server to tell you this stuff with a phone call using simple IVR scripting in conjunction with very basic UNIX shell scripting.  I’m using FacetPhone IVR scripting for this, but it should be doable in any IVR system that can run simple server commands.

First, let’s define what information we want to get and figure out how to query the server for the information we want to access via the PBX… CPU utilization and disk space are good things to monitor.  This sort of IVR system could be expanded upon to provide just about any info that can be gleaned from a server command including real-world sensing using monitoring devices like those of EnviroTrack.  In our example here, let’s use the ”iostat” command to check on CPU utilization.  ”iostat -c” provides a CPU report that is easy to parse:

"iostat -c":
Linux 2.6.20-1.2962.fc6 (facetphone2.sssi.com) 	10/07/2012

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0.24    0.48    0.77    0.12    0.00   98.39

All we care about here is the “%idle” figure. Snatching the number is easy:

iostat -c | grep . | tail -1 | tr -s ' ' \
      | cut -d ' ' -f7 | cut -d '.' -f1
98

Call it 98% to keep it simple… So we know how to get our number… CPU is 98% idle.  Do this from inside an IVR script, tell the caller the results, and you’ve got a nice Server Monitor IVR which can easily be enhanced to report on all sorts of things.

FacetPhone’s IVR scripting language has a powerful “popen” type of functionality perfect for this sort of thing.  There are three related functions we’ll use:  ”Popen”, “Pread”, and “Pclose”.  The “Popen” function opens a command pipe and runs the command, the “Pread” function reads the results of running that command, and the “Pclose” function closes the command pipe.  Don’t get hung up on complicated technicalities… It is easy to do and here is how:

cmd  = "iostat -c | grep . | tail -1 | tr -s ' ' ";
cmd += "| cut -d ' ' -f7 | cut -d ' ' -f1";
cpuidle = "";
fd = Popen( cmd );
Pread( cpuidle, fd );
Pclose( fd );

Build the command string, initialize a variable to hold results, open the command pipe, read the command pipe, close the command pipe. The end result is that our “cpuidle” variable will contain our “98″ %idle figure.  Not so bad, right?  The rest of this is just a matter of having a menu option that our dear sysadmin can get to which gets the “cpuidle” value and then tells him what it is.  FacetPhone has a pretty good collection of default system prompts — it can handle saying numbers okay.  Specialized stuff like we need here can either be done with TTS (Text to Speech) or by pre-recording any prompts you’ll need.  I’m sure we’ll be talking about TTS later, so for now, let’s assume we’ve already got pre-recorded helper prompts (“cpu_is” and “percent_idle”) we’ll use as we present our “98″ %idle number to the sysadmin.

Here is an IVR script snippet for doing this which assumes taking menu option 3 to find out about CPU utilization:

if ( ret == E_TT_3 ) {

    cmd  = "iostat -c | grep . | tail -1 | tr -s ' ' ";
    cmd += "| cut -d ' ' -f7 | cut -d ' ' -f1";
    cpuidle = "";
    fd = Popen( cmd );
    Pread( cpuidle, fd );
    Pclose( fd );

    Play_list_of_prompts( "cpu_is;" + cpuidle + ";percent_idle" );
}

Nada más! That’s it! The very same sort of thing could be done for reporting disk usage or job status or flight arrival time or order status, etc.  This is a very powerful technique…  Please share cool things you are doing like this so we can all be inspired!

Grandpa did VoIP?!

“I hope the pregnancy is over, and that the newborn is not a dinosaur.”

That was Danny Cohen’s wish for the NVP-II (Network Voice Protocol) spec as they released this early predecessor to the VoIP protocol in 1981 as the follow on to the original NVP protocol introduced in 1973.  So many things were different back then that it is almost like trying to explain to a modern day teenager how cars used to be started by cranking… from the outside… with a real hand-crank!1912 Cadillac - The car that has no crank  100 years ago the 1912 Cadillac became the first car to replace the hand crank with an electric starter.  33 years ago, in 1979, the Internet Stream Protocol (ST) was introduced in Internet Experiment Note (IEN-119) by James Forgie, Estil Hoversten, and Danny Cohen, and it became the transport protocol of NVP.  The protocol was notable for introducing packetized voice — VoIP!  As an interesting bit of trivia… ST, the first VoIP implementation, is also the missing IP version: IPv5.  IP/ST was used by a workstation dubbed “Voice Funnel” in 1981 and through the early ’80′s to do video conferencing across the ARPANET.  You’ll probably have an easier time explaining the hand-cranked car to your kid than you would explaining how VoIP and video conferencing existed before the Internet, before the IBM PC, Microsoft, and Skype.  VoIP has been around for a while.  Don’t let anyone fool you with labels of “new-fangled” or “immature”.  A car more than 20 years old is considered a Classic by law in most states.

Polycom’s Annoying VMWI Chirp

The VMWI (Visual Message Waiting Indicator) light on Polycom IP phones alerts users about new voice-mail messages waiting for their attention.  Polycom IP phones also provide an audible clue and “chirp” a reminder about new voice-mail messages.  Folks who regularly check voice-mail and delete new messages might appreciate this courtesy.  They promptly notice new messages when returning to their desk and deal with them.  However… some of us ALWAYS have new messages and never have an empty voice-mail box.  The VMWI and its incessant chirp then just become annoying.  What’s a fella to do?

Here is how to get rid of the “chirp” in Polycom SIP v3.3.2 or later

In one of the config files loaded by the IP phone when it boots up, change the “messageWaiting” sound-effect pattern with this snippet:

<polycomConfig>
  <!– get rid of annoying new voice-mail chirp –>
  <se>
    <se.pat>
      <se.pat.misc>
        <se.pat.misc.messageWaiting
          se.pat.misc.messageWaiting.name=”message waiting”>
          <se.pat.misc.messageWaiting.inst
            se.pat.misc.messageWaiting.inst.1.type=”silence”
            se.pat.misc.messageWaiting.inst.1.value=”1″
            se.pat.misc.messageWaiting.inst.2.type=”silence”
            se.pat.misc.messageWaiting.inst.2.value=”1″
            se.pat.misc.messageWaiting.inst.3.type=”silence”
            se.pat.misc.messageWaiting.inst.3.value=”1″>
          </se.pat.misc.messageWaiting.inst>
        </se.pat.misc.messageWaiting>
      </se.pat.misc>
    </se.pat>
  </se>
</polycomConfig>

 

That’s it!  Reboot the IP phone to pick up the new configuration and no more annoying VMWI Chirp!  See a later post about Polycom config files or refer to this Polycom technical note about Configuration File Management.

“Hackability”

The Urban Dictionary defines Hack as:

hack
v.1. To program a computer in a clever, virtuosic, and wizardly manner. Ordinary computer jockeys merely write programs; hacking is the domain of digital poets. Hacking is a subtle and arguably mystical art, equal parts wit and technical ability, that is rarely appreciated by non-hackers. See hacker.

We’re talking about doing clever things with PBX’s…

Welcome to PBX Hacks

PBX’s… everyone’s got ‘em.  Most are dull and drab.  Computerized PBX’s have dramatically changed the flexibility and “hackability” of PBX’s forever.  No longer is the PBX just a faded yellow box on the wall of a dusty phone closet.  New PBX’s live in shiny new servers hosted in spotless humming computer rooms like any other respectable important part of a computer-assisted business.  From incredible reporting capabilities to interfacing with databases to entertaining callers, the computerized PBX can be one of the most important business tools seen (heard) by customers.  Here we discuss those capabilities and how to get the most out of a phone-system.  How to maximize the phone-face put forward to the calling world.  I work in the support trenches of a PBX manufacturer (FacetCorp) and these are some of the tricks we use to enhance the customer experience.  Welcome to PBX Hacks.