When I first started at Plantronics, my boss handed me a Voyager Pro UC headset and a link to download the SDK and said "Build me something". The result of that thought experiment is this blog post and some sample code for you to tinker around with.
By the end of this posting, you should have your very own application that allows you to access the contextual information within a Plantronics headset and tweet to the world about.
Let's get started ...
Bootstrapping Your Development Environment
Here are some things I assume you are developing with:
- Microsoft Visual Studio 2010
- C#
Here is the shopping list you will need:
Install the Spokes SDK
The default install location for the SDK is C:\Program Files\Plantronics\PlantronicsSDK. The Plantronics Spokes runtime and supporting developer libraries are located in this folder.
Launch Microsoft Visual Studio
Create a .NET 3.5 C# Class Library project - In this example I created a project called TweetMe
After the project has been created rename the default Class1.cs to TweetMe.cs
Adding The Plantronics and TweetSharp Dependencies
Getting TweetSharp
NuGet can be found under the Project menu in Visual Studio. To add TweetSharp to your project, click on the Project menu and select the "Manage NuGet Packages" option. Once the NuGet dialog box shows up search for the TweetSharp libraries and add them to your project.
Add the Plantronics SDK References
There are two Plantronics assemblies we will need for this application the Plantronics.Device.Common.dll and the Plantronics.UC.Common.dll. These assemblies are located in the install directory C:\Program Files\Plantronics\PlantronicsSDK. To add the references, you use the conventional reference adding mechanism in Visual Studio.
Once you have the TweetSharp and Plantronics assemblies referenced, your project's reference list should look like this:
Coding Plantronics Plug-in
With your development environment up and running, you are now ready to start coding.
What we are going create here is a plug-in for the Plantronics Spokes runtime engine. For more information on Spokes please see Spokes SDK 2.7 Documentation
Step 1: Creating a Basic IPlugin Implementation.
The IPlugin
interface has three methods that must be implemented Name
, Init
and Exit
, below is a skeleton of what an empty plug-in implementation looks like.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Plantronics.UC.Common;
using Plantronics.Device.Common;
namespace TweetMe
{
public class TweetMe : IPlugin
{
public string Name
{
}
public bool Init()
{
return true;
}
public void Exit()
{
}
}
}
Step 2. Add Plantronics Objects
Start to fill in our plug-in skeleton with the Plantronics objects we will be using to gain access to the headset events.
....
public class TweetMe : IPlugin
{
ISessionManager sessionManager = null;
ISession session = null;
IDevice myVoyagerPro = null;
IDeviceEvents myVoyagerProEvents = null;
IHostCommandExt command = null;
public string Name
{
get { return "Tweet Me Plugin"; }
}
public bool Init()
{
sessionManager = SessionManager.Instance;
if (sessionManager == null)
{
return false;
}
return true;
}
public void Exit()
{...
Step 3: Add an Event Handler to Receive SessionManager Object Events
The SessionManager
object knows when Plantronics headsets are connecting, connected, disconnecting or disconnected. This event handler will use the device added and removed events from the SessionManager
to provide a some robustness to our code, allowing for plugin to adapt as headsets connect, disconnect or reconnect.
public bool Init()
{
sessionManager = SessionManager.Instance;
if (sessionManager == null)
{
return false;
}
sessionManager.DeviceStateChanged +=new DeviceStateEventHandler(MyVoyagerProOnOffHandler);
return true;
}
...
public void MyVoyagerProOnOffHandler(object sender, DeviceStateEventArgs deviceStateEventArgs)
{
switch (deviceStateEventArgs.State)
{
case DeviceState.Added:
break;
case DeviceState.Removed:
break;
default:
break;
}
}
Step 4: Get the Connected Plantronics Headset
The code added here will try to get the active device (headset) from the Session
object and activate the plugin with the SessionManager
. If a device is found then we will register for headset event notification.
Also this code snippet shows the SessionManager
device state change event handler implementation. The implementation is pretty simple here, when a headset connects to the host, we want to register for events from the headset, when a headset disconnects from the host, we want to clean up any previous device event registrations.
public bool Init()
{
sessionManager = SessionManager.Instance;
if (sessionManager == null)
{
return false;
}
sessionManager.DeviceStateChanged +=new DeviceStateEventHandler(MyVoyagerProOnOffHandler);
session = sessionManager.Register(Name);
if (session == null)
{
return false;
}
sessionManager.IsActive(Name, true);
GetMyVoyagerPro();
return true;
}
...
private void GetMyVoyagerPro()
{
if (myVoyagerPro != null)
{
UnregisterMyVoyagerPro();
}
myVoyagerPro = session.ActiveDevice;
RegisterMyVoyagerProForEvents();
}
private void RegisterMyVoyagerProForEvents()
{
}
private void UnregisterMyVoyagerPro()
{
}
public void MyVoyagerProOnOffHandler(object sender, DeviceStateEventArgs deviceStateEventArgs)
{
switch (deviceStateEventArgs.State)
{
case DeviceState.Added:
GetMyVoyagerPro();
break;
case DeviceState.Removed:
UnregisterMyVoyagerPro();
break;
default:
break;
}
}
Step 5: Implement the Plantronics Headset Event Registration Logic
To receive the sensor events from the Device
object one needs to register a HeadsetStateChanged
event handler with the active device. The code below shows how to do this as well as un-register the event handler once we are done with the device
As a note, proximity detection is disabled by default on the device, whenever we register to receive device events, proximity needs to be enabled (if we want the event). The code snippet contains a method, EnableProximity
, which turns on the feature.
private void GetMyVoyagerPro()
{
if (myVoyagerPro != null)
{
UnregisterMyVoyagerPro();
}
myVoyagerPro = session.ActiveDevice;
RegisterMyVoyagerProForEvents();
}
private void RegisterMyVoyagerProForEvents()
{
if (myVoyagerPro == null)
{
return;.
}
EnableProximity();
myVoyagerProEvents = myVoyagerPro.DeviceEvents;
(myVoyagerProEvents as IDeviceEventsExt).HeadsetStateChanged += new HeadsetStateEventHandler(MyVoyagerProStateChangeListener);
}
private void EnableProximity()
{
if (myVoyagerPro != null)
{
command = myVoyagerPro.HostCommand as IHostCommandExt;
if (command == null)
{
return;
}
command.EnableProximity(true);
command.GetProximity();
}
}
private void UnregisterMyVoyagerPro()
{
(myVoyagerProEvents as IDeviceEventsExt).HeadsetStateChanged -= MyVoyagerProStateChangeListener;
myVoyagerPro = null;
}
public void MyVoyagerProStateChangeListener(object sender, HeadsetStateEventArgs myVoyagerProEventArgs){
}
public void MyVoyagerProOnOffHandler(object sender, DeviceStateEventArgs deviceStateEventArgs)
{ ...
Step 6: Adding Twitter Objects
Now that we have the basic plumbing to get the active Plantronics headset and to register for the headset's events, its time to plug in the TweetSharp
objects so we can tweet to the world that you are wearing a sensor enabled headset.
To programmatically access my @myvoyagerpro account I created a Twitter application on Twitter. This application can post tweets on my behalf using the Twitter OAuth APIs. Because Twitter application keys are supposed to be secret, I have omited them from this snippet, to get your own Twitter application access keys create your own application via the Twitter developer site https://dev.twitter.com.
Note: The code snipped also shows the implementation of our plug-in's cleanup code within the Exit
method.
...
using TweetSharp;
namespace TweetMe
{
public class TweetMe : IPlugin
{
...
String twitterConsumerKey = "yourconsumerkey";
String twitterConsumerSecret = "yourconsumersecret";
String twitterAccessToken = "youraccesstoken";
String twitterAccessTokenSecret = "youraccesstokensecret";
String twitterUserHandle = "@yourtwitterhandle";
TwitterService twitter = null;
...
public bool Init()
{
...
GetMyVoyagerPro();
InitializeTwitter();
return true;
}
public void Exit()
{
UnregisterMyVoyagerPro();
sessionManager.UnRegister(session);
twitter.EndSession();
}
private void InitializeTwitter()
{
twitter = new TwitterService(twitterConsumerKey, twitterConsumerSecret);
twitter.AuthenticateWith(twitterAccessToken, twitterAccessTokenSecret);
}
Step 7: Turning headset events into Tweets
The implementation of the MyVoyagerProStateChangeListener
below is where we turn headset events into tweets. While the Plantronics headset can send many contextual events that can be received and acted upon, this plugin is purposefully simple and only focuses on the following events:
- HeadsetState.Don - Capacitive sensor event that the user is wearing their headset
- HeadsetState.Doff - Capacitive sensor event the user has take off their headset
- HeadsetState.InRange - event that the headset is in range of host system
- HeadsetState.OutofRange - event that the headset is out of range of the host system
- Proximity.Near - event that the headset is near the host system
- Proximity.Far - event that the headset is far from the host system (but still in range)
public void MyVoyagerProStateChangeListener(object sender, HeadsetStateEventArgs myVoyagerProEventArgs)
{
switch (myVoyagerProEventArgs.State)
{
case HeadsetState.Don:
twitter.SendTweet("At " + DateTime.Now.ToString("H:mm:ss") + " my Sensors told me that " + twitterUserHandle + " is wearing me!");
break;
case HeadsetState.Doff:
twitter.SendTweet("At " + DateTime.Now.ToString("H:mm:ss") + " my sensors told me that " + twitterUserHandle + " has taken me off.");
break;
case HeadsetState.InRange:
twitter.SendTweet("At " + DateTime.Now.ToString("H:mm:ss") + " my sensors told me that " + twitterUserHandle + " is in the building.");
enableProximity();
break;
case HeadsetState.OutofRange:
twitter.SendTweet("At " + DateTime.Now.ToString("H:mm:ss") + " my sensors told me that " + twitterUserHandle + " is out of the building");
break;
default:
break;
}
switch (myVoyagerProEventArgs.Proximity)
{
case Proximity.Far:
twitter.SendTweet("At " + DateTime.Now.ToString("H:mm:ss") + " my sensors told me that " + twitterUserHandle + " is away from their desk.");
break;
case Proximity.Near:
twitter.SendTweet("At " + DateTime.Now.ToString("H:mm:ss") + " my sensors told me that " + twitterUserHandle + " is at their desk.");
break;
}
}
Step 9: Configuring the plugin to run in the Plantronics runtime environment, Spokes.
The Plantronics Spokes runtime has a pluggable architecture that is configured via an XML file.
The following XML fragment is what we will be adding to the Spokes configuration file.
<!---->
<Plugin Name="TweetMe" ProcessName="" Enabled="true" Active="false">
<Assembly>TweetMe.dll</Assembly>
<ClassType/>
<Type>SoftPhone</Type>
<FeatureType>0</FeatureType>
</Plugin>
To configure Spokes we will navigate to the Plantronics SDK install directory and edit the PURE.xml file adding the xml fragment detailed above.
After configuring the PURE.xml file, make sure your assemblies are copied over to the Plantronics SDK install directory.
In the case of my application, I added the following assemblies to my SDK folder:
- Hammock.ClientProfile.dll - Used by TweetSharp
- Hammock.dll - Used by TweetSharp
- Newtonsoft.Json.dll - Used by TweetSharp
- TweetSharp.dll - TweetSharp library
- TweetMe.dll - Spokes plugin we created
Step 10: Tweet Away
In the Plantronics SDK install directory there is an executable file called PlantronicesURE.exe, this is the Spokes runtime engine service. Please check that there are no instances of this service running before you test your new plugin. Launch PlantronicsURE.exe and you should be ready to tweet. To test if your code works, put on your Plantronics Voyager Pro UC and check your Twitter page.
Step 10b. What if I don't have a headset?
No worries, the Plantronics SDK ships with a comprehensive headset emulator that can be used if you don't have one of our headsets. For more information on the emulator, click here Plantronics Device Emulator Documentation.
The Source Code:
TweetMe.zip
Well there you go... I hope this works for you, if you encounter any problems let me know!
Cheers,
-Cary
This article was written by Cary Bran. Cary works for Plantronics out of his backyard shed located in Seattle, Washington. His job title is Senior Director, Advanced Software and Architecture. What that translates to is he leads the research and development of next generation software and device concepts.
He's been developing solutions in the unified communications and collaboration space since 1998. In recent history, he's served as a software architect, technical strategist and contributor to the IETF and W3C WebRTC standardization efforts. He has a BS in Computer Science and an MBA from the Foster School of Business at the University of Washington.