Programming for the Cisco Phone Using .Net

4/16/2010

Between family visiting, having people move, and "disasters" at work, working on anything of my own has taken a back seat. That being said, I did have enough time to write some code to help with a project I was working on though. At work we ended up getting these Cisco phones. And since you can program for them, I was told I had a new project (I believe the exact quote from my boss was "Figure out what the hell we can do with it."). Anyway, I downloaded Cisco's SDK and looked through their examples... Not a single ASP.Net app... Some ASP and Java apps, but nothing in the .Net realm. So I created my own set of classes to help me out:

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.Linq;
  26: using System.Text;
  27: using Utilities.Cisco.Interfaces;
  28: #endregion
  29:  
  30: namespace Utilities.Cisco
  31: {
  32:     /// <summary>
  33:     /// Text class
  34:     /// </summary>
  35:     public class Text:IDisplay
  36:     {
  37:         #region Constructor
  38:  
  39:         /// <summary>
  40:         /// Constructor
  41:         /// </summary>
  42:         public Text()
  43:         {
  44:             SoftKeys = new List<SoftKeyItem>();
  45:         }
  46:  
  47:         #endregion
  48:  
  49:         #region Properties
  50:  
  51:         /// <summary>
  52:         /// Title
  53:         /// </summary>
  54:         public string Title { get; set; }
  55:  
  56:         /// <summary>
  57:         /// Prompt
  58:         /// </summary>
  59:         public string Prompt { get; set; }
  60:  
  61:         /// <summary>
  62:         /// Text
  63:         /// </summary>
  64:         public string Content { get; set; }
  65:  
  66:         /// <summary>
  67:         /// Softkey list
  68:         /// </summary>
  69:         public List<SoftKeyItem> SoftKeys { get; set; }
  70:  
  71:         #endregion
  72:  
  73:         #region Public Overridden Functions
  74:  
  75:         public override string ToString()
  76:         {
  77:             StringBuilder Builder = new StringBuilder();
  78:             Builder.Append("<CiscoIPPhoneText><Title>").Append(Title).Append("</Title><Prompt>")
  79:                 .Append(Prompt).Append("</Prompt><Text>").Append(Content).Append("</Text>");
  80:             foreach (SoftKeyItem Item in SoftKeys)
  81:             {
  82:                 Builder.Append(Item.ToString());
  83:             }
  84:             Builder.Append("</CiscoIPPhoneText>");
  85:             return Builder.ToString();
  86:         }
  87:  
  88:         #endregion
  89:     }
  90: }

It turns out that all the Cisco phone is looking for is an XML file. Each type of display item has it's own format (a listing for each of them can be found here). That being said, the code above creates text, which can be displayed on the screen. It inherits from the IDisplay interface, which simply holds a list of soft keys. Soft keys is basically an input from the user:

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.Linq;
  26: using System.Text;
  27: #endregion
  28:  
  29: namespace Utilities.Cisco
  30: {
  31:     /// <summary>
  32:     /// Softkey class
  33:     /// </summary>
  34:     public class SoftKeyItem
  35:     {
  36:         #region Constructor
  37:  
  38:         /// <summary>
  39:         /// Constructor
  40:         /// </summary>
  41:         public SoftKeyItem()
  42:         {
  43:         }
  44:  
  45:         #endregion
  46:  
  47:         #region Properties
  48:  
  49:         /// <summary>
  50:         /// Name of the softkey
  51:         /// </summary>
  52:         public string Name { get; set; }
  53:  
  54:         /// <summary>
  55:         /// URL action for release event
  56:         /// </summary>
  57:         public string URL { get; set; }
  58:  
  59:         /// <summary>
  60:         /// URL action for press event
  61:         /// </summary>
  62:         public string URLDown { get; set; }
  63:  
  64:         /// <summary>
  65:         /// position of softkey
  66:         /// </summary>
  67:         public int Position { get; set; }
  68:  
  69:         #endregion
  70:  
  71:         #region Overridden Functions
  72:  
  73:         public override string ToString()
  74:         {
  75:             StringBuilder Builder = new StringBuilder();
  76:             Builder.Append("<SoftKeyItem><Name>").Append(Name).Append("</Name><URL>").Append(URL).Append("</URL><URLDown>")
  77:                 .Append(URLDown).Append("</URLDown><Position>").Append(Position).Append("</Position></SoftKeyItem>");
  78:             return Builder.ToString();
  79:         }
  80:  
  81:         #endregion
  82:     }
  83: }

Basically a soft key has a name, an action URL when pressed or an action URL when released/clicked, and a position within the list of softkeys. And they're able to be used in conjunction with any display item. Speaking of display items, there are a number of them and depending on the model of your phone, different ones are useable. For instance, I have a 7965 at work. As such I have access to pretty much everything, but earlier phones may have less features available. Anyway, after learning all of this I decided the best thing to do was to create a basic weather app. It's simple enough but for some reason people get excited about things like that. So I created a new web app, created a new webpage, and started working:

   1: namespace Cisco
   2: {
   3:     public partial class _Default : System.Web.UI.Page
   4:     {
   5:         private static string ServerImage="ServerImagePath";
   6:         private static string ServerIP = "ServerPath";
   7:         protected void Page_Load(object sender, EventArgs e)
   8:         {
   9:                 if (!Utilities.FileManager.FileExists(Server.MapPath("~/Weather/Images/Image2.png"))
  10:                     || File.GetCreationTime(Server.MapPath("~/Weather/Images/Image2.png")) < DateTime.Now.AddHours(-1))
  11:                 {
  12:                     string Content = Utilities.FileManager.GetFileContents(new Uri("http://rss.weather.com/weather/rss/local/"));  //Note that you need to get the RSS Feed for your area and plug it in here
  13:                     XmlDocument XmlDocument = new XmlDocument();
  14:                     XmlDocument.LoadXml(Content);
  15:                     Utilities.FileFormats.RSSHelper.Document Document = new Utilities.FileFormats.RSSHelper.Document(XmlDocument);
  16:                     string Description = Document.Channels[0].Items[0].Description;
  17:                     Regex TempReg = new Regex("<img src=\"(?<URL>[^\"]*)\"(.*)/>(?<TEXT>(.*))");
  18:                     MatchCollection Matches = TempReg.Matches(Description);
  19:                     string Text = "";
  20:                     foreach (Match Match in Matches)
  21:                     {
  22:                         Text = Match.Groups["TEXT"].Value.Replace("anddeg;", "").Replace("For more details?", "").Replace(", and", "\n").Replace(".", "");
  23:                     }
  24:  
  25:                     Utilities.Media.Image.Image.DrawText(Server.MapPath("~/Weather/Images/Image.png"), Server.MapPath("~/Weather/Images/Image2.png"), Text, new Font("Arial", 16f), Brushes.White, new RectangleF(179, 45, 90, 80));
  26:                 }
  27:                 Response.ContentType = "text/xml";
  28:                 Utilities.Cisco.Image Image = new Utilities.Cisco.Image();
  29:                 Image.Title = "Weather";
  30:                 Image.Prompt = "Weather";
  31:                 Image.URL = "http://" + ServerImage + "/Image2.png";
  32:                 Image.X = 0;
  33:                 Image.Y = 0;
  34:                 Response.Write(Image.ToString());
  35:                 Response.End();
  36:             }
  37:     }
  38: }

The code above is the actual web page's code. All we're doing is double checking the last time we updated the image. If it has been more than an hour, we pull our local weather feed from weather.com (or whomever you want as you'll need to replace that location with your own), getting the first item from the RSS feed, and pulling out the text saying what temperature it is and whether it is sunny, partly cloudy, raining, etc. From there we take a default image that we choose and write the text on it, saving it in a new location. We then use the Image class (which I'll show you in a second) to export that as an XML doc that the phone will recognize. And that's it. The RSSHelper, FileManager, and Image classes are all found in my utility library. The Cisco Image class on the other hand can be found here:

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.Linq;
  26: using System.Text;
  27: using Utilities.Cisco.Interfaces;
  28: #endregion
  29:  
  30: namespace Utilities.Cisco
  31: {
  32:     /// <summary>
  33:     /// Image class
  34:     /// </summary>
  35:     public class Image:IDisplay
  36:     {
  37:         #region Constructor
  38:  
  39:         /// <summary>
  40:         /// Constructor
  41:         /// </summary>
  42:         public Image()
  43:         {
  44:             SoftKeys = new List<SoftKeyItem>();
  45:         }
  46:  
  47:         #endregion
  48:  
  49:         #region Properties
  50:  
  51:         /// <summary>
  52:         /// Title
  53:         /// </summary>
  54:         public string Title { get; set; }
  55:  
  56:         /// <summary>
  57:         /// Prompt
  58:         /// </summary>
  59:         public string Prompt { get; set; }
  60:  
  61:         /// <summary>
  62:         /// X location
  63:         /// </summary>
  64:         public int X { get; set; }
  65:  
  66:         /// <summary>
  67:         /// Y location
  68:         /// </summary>
  69:         public int Y { get; set; }
  70:  
  71:         /// <summary>
  72:         /// URL to the image
  73:         /// </summary>
  74:         public string URL { get; set; }
  75:  
  76:         /// <summary>
  77:         /// Softkeys list
  78:         /// </summary>
  79:         public List<SoftKeyItem> SoftKeys { get; set; }
  80:  
  81:         #endregion
  82:  
  83:         #region Overridden Functions
  84:  
  85:         public override string ToString()
  86:         {
  87:             StringBuilder Builder = new StringBuilder();
  88:             Builder.Append("<CiscoIPPhoneImageFile><Title>").Append(Title).Append("</Title><Prompt>").Append(Prompt).Append("</Prompt><LocationX>")
  89:                 .Append(X.ToString()).Append("</LocationX><LocationY>").Append(Y.ToString()).Append("</LocationY><URL>")
  90:                 .Append(URL).Append("</URL>");
  91:             foreach (SoftKeyItem Item in SoftKeys)
  92:             {
  93:                 Builder.Append(Item.ToString());
  94:             }
  95:             Builder.Append("</CiscoIPPhoneImageFile>");
  96:             return Builder.ToString();
  97:         }
  98:  
  99:         #endregion
 100:     }
 101: }

It simply wants to know the title, prompt, location of the image, and the URL pointing to it and in return spits out a bit of XML for the phone. Also note that you have to use PNG files. The phone doesn't work with any other format really, so make sure to convert them over. It's incredibly simple once you jump into it. But that's all I had to do in order to get weather on my phone. If you want the rest of the helpers that I created, you can go here and download them from the utility library's source code. Note that certain elements I didn't include as I didn't need them (I would rather deal with images than individual pixels personally). That being said, you can actually create some nice apps rather quickly for it using the code and you can create items rather quickly to fill in the gaps. So give it a try, leave feedback, and happy coding.



Comments