Problem Restated
So you have a few hundred or a few thousand phones floating around in the environment. You have an image that you just created using a procedure similar to this one. You have uploaded the .png files to the TFTP server, created the List.xml file, and tested this on one of your phones. Everything is great so far. Then you go about the process of working on a plan to push this new image out to all of your phones. Ahhhh. Now here’s the rub. How does one go about doing this?
Options
As this blog unfolds, you will find that there are essentially three options that I have come across:
- Using API on phone to do a “batch keypress” job. This is near useless as you will see.
- Using API on phone to do a “setBackground” job. Way easier than #1 but still requires you to write up some code/script/app.
- Using a third party application to do the same thing that #2 is accomplishing.
This blog is going to focus on item #2. We will provide some background on how I got to this solution and provide a sample script that you can test on your own. A third party option is provided at the end of this blog.
Cisco Phone Designer
This is likely one of the first places you land when doing research on this topic. This tool is something Cisco developed and made freely available to customers. The tool allows a user to open up an image file (like a picture of their kids or their Harley) and “push” it to the phone from their desktop. Man! That does sound like the type of tool an admin could use to push an image to a ton of phones. Unfortunately, this is a user tool and not an admin tool. It can push an image to a phone associated to a user. I suppose one could try and go the route of associating different phones to a user account and then have a process of changing phone associations, reloading the Phone Designer, push image, repeat. But that is not anywhere near what I would call efficient. So, Phone Designer is not useful as an admin tool.
However, the methods used by the phone designer to do it’s job are useful. We’ll touch on them as we continue on our journey.
Using the Cisco Phone XML API to Batch Key Press
Maybe, if you dug deep enough you came across the idea of using the ability to remotely press keys on an IP phone to your advantage. Yes, this is possible. You can use HTTP/XML and push key presses to an IP phone. Essentially, telling the phone to:
- Press “Settings”
- Press “1” (to go to User Preferences)
- Press “2” (to go to Background Images)
- Press “2” (or some other number, to select the image you want on the phone)
- Press “Softkey 1” to “select” the image
- Press “Softkey 2” to “save” the image
- Press “Softkey 3” to “exit”
- Repeat #7 to go back to the phone display
Sounds ugly? Well it is and using this method has a very high probability of failure. Mainly because timing is key. It isn’t like an Expect script where you can scrape the output and interpret where you are in the process. Even if that was possible, the ping-pong nature of this approach is just prone to error. Once you throw in the added variable of different phone models having different images and lists of images, you might as well not bother. At least, that is my opinion.
When I first researched this topic, this is about where I stopped. I knew I could program the key press batch process but I also knew it wasn’t worth the time it would take me to get something working. Like my dad says, “Son, you gotta use the right tool for the job”. Of course this is the same guy that demonstrates an affinity for 4-letter words when hanging a ceiling fan, but that is a whole other matter.
Using the setBackground XML Object
OK, so where do we go next? The last stop on this voyage is a hidden XML object. Well, hidden from documentation but not from those who know how to call it up. Yes, we still need to write a tool/script/program. Yes, we still need to use the API on the phone to accomplish this task.
I came across the “setBackground” object after I decided to revisit the overall problem. I wondered how the Phone Designer accomplished its job. So, I setup a sniffer trace on a machine running Phone Designer and saw that Phone Designer uses HTTPs and communicates to the CUCM cluster exclusively. Hmmm. I don’t see the phone talking to the Phone Designer which isn’t a surprise. Since I want to see the conversation with the phone I decided that the next place to look would be to capture a trace for the conversation between the IP phone and the CUCM. I used the method described here to gather a protocol trace. While looking at the protocol trace I discovered a few things:
- Phone Designer is uploading the thumbnail and full size version of the background image to the CUCM cluster
- The images are stored in the “ccmcip” virtual directory of the CUCM web server
- The CUCM uses an XML command “setBackground” to tell the phone where to download the desktop images
This last point was interesting. I went to the Cisco IP Phone SDK to find out about “setBackground” and there was nada. I then went to the Cisco developer community and searched for “setBackground”. I found out that other folks have stumbled across this XML object and that it isn’t really documented in any API release notes. I also received confirmation that the XML structure is basically the same as what I saw in my sniffer trace.
So, now we know what the problem is. We have identified some options and common research results and we found a viable candidate solution. Now, if you want to do this without investing money in a 3rd party application then you will have to solve your own problem. I touch on one 3rd party option at the end of this blog. The following sections go into the “viable candidate solution” of leveraging the “setBackground” XML object.
XML Structure
The setBackground object is pretty simple:
<setBackground> <background> <image>
http://mywebserver/imagename.png</image> <icon>http://mywebserver/imagethumbnail.png</icon> </background> </setBackground>
Basically, the above XML object is telling the phone to pull the images from a web server. The image child node is the image that will actually become the background image and the icon child node is the “thumbnail” version of the background image.
Methodology: The Image Files
You still need image files and those image files are formatted using the same pixel resolution and file format (.png) as TFTP background images. The only difference is that “setBackground” is using HTTP to download the images. I have not found a way to use either TFTP or FTP URLs to get the phone to download the files. So, from my testing you will need to upload your background images and thumbnails to a web server to leverage the “setBackground” capability.
So, to use the example provided in this blog, you will need to post the background .png file and the thumbnail .png file to a web server that doesn’t require authentication from clients (i.e. IP Phones) accessing the web server.
Methodology: CUCM Provisioning
Since we are using the “setBackground” XML method and are basically emulating the same process used by Cisco Phone Designer, you will need to configure CUCM in the same way you would using Phone Designer. As an overview:
- Create a Common Phone Profile that enables “Phone Personalization” (or modify the system defaults)
- Assign profile to the phones you wish to modify
- Associate a user object to the phones/devices that you would like to modify
The CUCM cluster does not allow use of the “setBackground” XML object unless phone personalization is enabled. In addition, when you try to use this API method the phone will authenticate the request which is why you need to associate the target devices to the user.
Methodology: Executing API Call
As with all API calls to a Cisco IP phone you have to use the POST method to upload the “setBackground” XML you want the phone to parse and act on. You also have to have a user ID that has a device association to the phone. This is a security measure enforced by the phone and CUCM.
A Sample Script
The following is a sample script that you can use to test the methodology discussed in this blog. This is a demonstration/concept example and is not intended to be used for any production deployment. This example is written in Microsoft JavaScript and is intended to be used with Windows Scripting Host (WSH) loaded on W2K, W2k3, XP, etc.
NOTE: Original Sample Has Been Updated
/* ================================= NAME: testbackgroundpush.js AUTHOR: wjb@netcraftsmen.net DATE : 2/15/2010 COMMENT: Just a demo. ================================== */ //BEGIN: modify the following var myuserid="bbell";//this is the user object associated to the phone(s) var mypassword="C1$co12345";//this is the user password //modify the myphonelistr to include a list of phones you want updated //a string value of one IP address is perfectly acceptable var myphoneliststr = "10.3.199.32"; //"10.3.199.32,10.3.199.33" is another example var thumbnailurl = "http://10.3.2.252/netcraftsmen-tn.png"; //the thumbnail image var backgroundurl = "http://10.3.2.252/netcraftsmen.png"; //this is the actual background //END: modify var myphonearr = myphoneliststr.split(","); //create an array to walk through var myauth = text2base64(myuserid + ":" + mypassword);//Create HTML basic auth string var xmlhttp = new ActiveXObject("Msxml2.ServerXMLHTTP.6.0"); //Object used for client connection // Loop through the phone list for (var i=0;i<myphonearr.length;i++) { WScript.Echo("Processing phone record: " + myphonearr[i]); var myxml = "XML=<setBackground> <background><image>
" + backgroundurl + "</image> <icon>" + thumbnailurl + "</icon></background></setBackground>"; var WebServer = "http://" + myphonearr[i] + "/CGI/Execute"; xmlhttp.open("POST", WebServer, false); with (xmlhttp){ setRequestHeader("Man", "POST " + WebServer + " HTTP/1.1"); setRequestHeader("MessageType", "CALL"); setRequestHeader("Content-Type", "text/xml"); setRequestHeader("Host", myphonearr[i] + ":80"); setRequestHeader("Authorization", "Basic " + myauth); setRequestHeader("Content-length", myxml.length); } xmlhttp.send(myxml); WScript.Echo(xmlhttp.responseText); } /****************************************************** function text2base64() Used to create hashed string ******************************************************/ function text2base64(text) { var j = 0; var i = 0; var base64 = new Array(); var base64string = ""; var base64key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var text0, text1, text2; //////////////////////////////////////////////////////////////////////////////////////////////////// // Step thru the input text string 3 characters per loop, creating 4 output characters per loop // //////////////////////////////////////////////////////////////////////////////////////////////////// for (i=0; i < text.length; ) { text0 = text.charCodeAt(i); text1 = text.charCodeAt(i+1); text2 = text.charCodeAt(i+2); base64[j] = base64key.charCodeAt((text0 & 252) >> 2); if ((i+1)<text.length) // i+1 is still part of string { base64[j+1] = base64key.charCodeAt(((text0 & 3) << 4)|((text1 & 240) >> 4)); if ((i+2)<text.length) // i+2 is still part of string { base64[j+2] = base64key.charCodeAt(((text1 & 15) << 2) | ((text2 & 192) >> 6)); base64[j+3] = base64key.charCodeAt((text2 & 63)); } else { base64[j+2] = base64key.charCodeAt(((text1 & 15) << 2)); base64[j+3] = 61; } } else { base64[j+1] = base64key.charCodeAt(((text0 & 3) << 4)); base64[j+2] = 61; base64[j+3] = 61; } i=i+3; j=j+4; } //////////////////////////////////////////// // Create output string from byte array // //////////////////////////////////////////// for (i=0; i<base64.length; i++) { base64string += String.fromCharCode(base64[i]); } return base64string; }
So, the above script is fairly simplistic. It assumes you have a network path from the machine you use to run the script and the IP phone. It also assumes that the phone’s web server is configured to accept HTTP requests and that the network allows this type of conversation from your desktop to the phone. You will need to make sure you have the end user and device association configured correctly. You also need to ensure that phone personalization is configured. The function text2base64() is necessary because we are using HTTP Basic Authentication method. The text2base64() will take a user name/password and create the hashed string used by HTTP Basic Auth.
Again, this uses Windows Scripting Host (WSH) and has been tested on WSH version 5.6 and CUCM version 7.1(3b)SU1.
Disclaimer
It should be common sense but I will include a disclaimer anyway. The above script is a concept sample only and not intended or tested for production use. The script is provided “as is” and you use this script at your own risk. It is assumed that the person using the above script is simply testing the moving parts.
Other Options and Future Cisco Features
There is another option I have found but have not tested. Apparently I have seen that some folks found an application that can do this at a nominal software cost. Again, I haven’t tested this out but it sounds promising:
http://www.voipintegration.com/software.html
Oh, and one more thing. There is a feature request in the ol’ CUCM pipeline that is being tracked by this defect id: CSCsi49844. However, you will find that is one of those internal Cisco Bug IDs.
The background deployment tool from Voip Integration worked great for me. Very quick and easy to get up and running then I pushed our company background image out to 2000 Cisco phones in a matter of minutes.
Thanks for the link.
it’s really nice, thanks William.
I got error prompt , any idea?
Robin,
Since the concept script doesn’t include my standard error checking routines, I suspect that your error prompt is coming from the interpreter (i.e. cscript). What is the error prompt?
-Bill (@ucguerrilla)
Thanks Bill
I have done all what you was telling myself before reading your blog, and after I found your page I was able to validate all the steps I got, I was able to do the same ousing c# without seeing your java code. nice job.
For anyone that is curious about pushing ringtones… the XML structure for that is:
http://url.to.file/file.raw
Just as with the background, the .raw file needs to be hosted on a web server and normal rules apply for the format of the ringtone.
@matthew_lindstrom:disqus , you’ll need to make sure they are using the “default” ringtone, though. If they choose another ringtone, your command will just play the sound. Only way I know is to use the “key press” commands to force them to use the “default” (or of course manually do it from the phone 🙂 )
Also, to bring it a step further, you can play a ‘ringtone’ on a phone, without actually making it a ringtone:
Note: $soundFile must be on the root directory of the CUCM TFTP server
Also, the ringtone and $soundFile must meet the following conditions:
8000Hz sample rate
divisible by 240 samples
U-law encoding
1 Channel (Mono)
I think ringtone has a maximum length (may depend on how long to ring before going to voicemail), but the $soundFile does not
–Richard