I've recently been handed a Voyager Pro UC headset and was told to "make something cool.". A couple suggestions were given on where to start and I was then let loose to do what I would with this new and exciting toy.
I thought the headset's ability to detect when it's being worn or not was really interesting. I wanted to jump right in to tinkering with the SDK and detect the current worn state of the device as my starting point. This resulted, after several minutes, in a C# plugin that created a pop-up window when the headset was put on my ear or taken off.
Then, on the Plantronics developer site, something caught my eye; there were other languages supported for the SDK other than C#. This is exciting! I had to investigate.
The main thing that had jumped out at me was the exposure of the Spokes service through JavaScript and the REST protocol. This is some powerful stuff! Have you ever thought your headset could control a website? Me neither. Not until now; and this blog post is going to show you how to make it happen.
First thing's first - you're going to need the proper tools to work with.
Configuration and Setup
For this article, we will be developing in JavaScript. The following are required to follow along with this article:
- The Spokes SDK from Plantronics (this is version 2.6. Feel free to use a newer version if one's available)
- A text editor of some sort. Notepad works. I use Notepad++ for the syntax highlighting, custom color schemes, and its FTP plugin which is handy for the next requirement....
- An HTTP server of some kind. REST's core lies in the HTTP protocol.* You can use the Apache HTTP server, whip up an HTTP server with node.js, etc. I pay a hosting service to run a web server for me but ran an Apache server from home prior to that.
*This is assuming you follow this post which focuses on getting Spokes to play nicely with web sites. There could be instances where you're using REST within a stand-alone application or integrating with something that isn't necessarily a web site.
The first thing you'll need to do is to run the Spokes SDK installer. The default installation folder is "C:/Program Files (x86)/Plantronics/Plantronics SDK". Nothing you write will run without the runtime found in the folder and life will be more difficult without the spokes.js file found in the installation's Samples/RestJsClient folder.
Coding
Preliminary Work
The way we're going to get our JavaScript application to work with a website of our choosing is going to be through JavaScript injection. To execute arbitrary JavaScript on any website you could normally write a script in the URL bar of a web browser such as "javascript:( function(){ alert("Hello Plantronics"); })();
", as you would if visiting somewhere, such as www.google.com. What you'd see is a pop up window with our message instead of a new web page.
Unfortunately those URLs can only be up to 2000 characters long. This isn't nearly enough space for a larger JavaScript program so we have to create a little stub script which will slap the main application from our HTTP server into the site we want to work with (see why it's required now?).
This little script fits nicely in the limited space of a URL. This is what people will bookmark (and run) when visiting a site to be worked with (in my case, http://www.grooveshark.com).
javascript:
(
function()
{
our_script = document.createElement('script');
our_script.type = 'text/javascript';
our_script.src = "http://www.guineacode.com/Plantronics/test.js";
document.getElementsByTagName('head')[0].appendChild(our_script);
}
)();
You don't have a main JavaScript file in the location being specified for our_script.src yet. Don't sweat it. We're getting there. Also note that the above script, with comments and whitespace comes out to 544 characters. Already hit 1/4 of the limit for a URL.
Main Application
It's time to write the meat of our program. To get started, make a new JavaScript file. Call it what you will (I'm naming mine test.js). All of the code will be wrapped in one anonymous function to help with variable scope.
Step the First - Building the Program's Skeleton
Let's start the main application file by building the skeleton of the program-to-be from which we can expand upon later.
(function()
{
var spokes = null; var plugin_registered = false;
var plugin_name = "SoundCake";
var connectToSpokes = function()
{
};
var controlInterface = function(session)
{
};
var registerPlugin = function()
{
};
var pollDeviceEvents = function()
{
};
}).call(this);
Step the Second - Making Contact
Now that we have a very bare-bones foundation to work off of, it's time to fill out the programs and make our functions function! We'll need to make a new session manager, get a hold of a device, and register this program with the Spokes service to begin polling for events. First, let's establish a new session and try to make contact with a device.
(function()
{
var spokes = null;
var plugin_registered = false;
var plugin_name = "SoundCake";
var connectToSpokes = function()
{
spokes = new Spokes("http://localhost:32001/Spokes");
spokes.Device.deviceList( function(result)
{
if(!result.isError)
{
if(result.Result[0] !== null)
{
spokes.Device.attach(result.Result[0].Uid, controlInterface);
pollDeviceEvents();
}
else
{
alert("Error: Device was null on connecting to Spokes. Is there a Plantronics device connected?");
}
}
else
{
alert("Error connecting to Spokes.");
}
});
};
}).call(this);
Step the Third - Registration
Now that we have the ability to start a new session with Spokes and check to see if a valid device exists, we need to be able to register our app with Spokes
var controlInterface = function(session)
{
if(session.isError || !spokes.Device.isAttached)
{
alert("Session Registration Error");
}
else
{
registerPlugin();
}
};
var registerPlugin = function()
{
if(!plugin_registered)
{
spokes.Plugin.register(plugin_name, function(result)
{
if(!result.isError)
{
spokes.Plugin.isActive(plugin_name, true, function(result)
{
if(!result.isError)
{
plugin_registered = true;
}
else
{
alert("Error checking if plugin is active: " + result.Err.Description);
}
});
}
else
{
alert("Error registering plugin: " + result.Err.Description);
}
});
}
};
Step the Fourth - Event Polling
So we've made a new session and have made our app known to Spokes. Great. But, here's where the fun, and meat of the program, lies. It's time to start asking Spokes if there's any new events going on with our device and react based on the answer. Let's make some alert windows pop up when we wear or take off our Voyager Pro headset. Feel free to have something else happen on these events.
var pollDeviceEvents = function()
{
setInterval(function()
{
if(spokes === null || !spokes.Device.isAttached)
{
return;
}
spokes.Device.events(function(result)
{
var i;
if(result.isError)
{
alert("Error polling for events: " + result.Err.Description);
}
else
{
if(result.Result.length > 0)
{
i = 0;
while(i < result.Result.length)
{
switch(result.Result[i].Event_Name)
{
case "Don":
alert("Your Voyager Pro is now on your head!");
break;
case "Doff":
alert("You took off your Voyager Pro!");
break;
default:
alert("You just did something crazy with your Voyager Pro.");
break;
}
i++; }
}
}
});
}, 2000); };
Step the Fifth - Execution and Ensuring Library Inclusion
One can't simply assume a site already has jQuery included on their page for you to use so you'll have to check for its existence. If jQuery is undefined, we'll have to slap in the latest jQuery script before anything else is done and then we can include spokes.js. Wait on spokes.js to be loaded up before doing anything else in the program otherwise you might accidentally call an undefined method as the code below executes asynchronously.
var main = function()
{
var spoke_js = document.createElement('script');
spoke_js.type = 'text/javascript';
spoke_js.src = 'http://www.guineacode.com/Plantronics/spokes.js';
document.getElementsByTagName("head")[0].appendChild(spoke_js);
spoke_js.onload = spoke_js.onreadystatechange = function()
{
connectToSpokes();
}
};
if(typeof jQuery === 'undefined')
{
jquery_js = document.createElement('script');
jquery_js.type = 'text/javascript';
jquery_js.src = 'http://code.jquery.com/jquery-latest.js';
document.getElementsByTagName("head")[0].appendChild(jquery_js);
jquery_js.onload = jquery_js.onreadystatechange = main;
}
else
{
main();
}
}).call(this);
The asynchronous execution of the code got me when I was first working on my program and was a bit frustrating. I resorted to just pasting the contents of spokes.js into the main code file instead of including it from an external file just so I could move on and hack out a prototype for my idea. I feel that the above is a much cleaner solution now after taking some time to come up with a fix.
Bringing It All Together
It's time to test out our program! Run PlantronicsURE.exe from the installation folder of your Spokes SDK (default is "C:/program files (x86)/Plantronics/Plantronics SDK"). Assuming you've uploaded both the main JavaScript file we wrote here and the spokes.js file to your host, you should be able to make a new bookmark in your browser and paste the stub code (first snippet) in the url (while ensuring the our_script.src
property points to your main JavaScript file being hosted).
Visit a random page of your choice, such as Facebook. Click on the bookmark you just made and either put your headset on or take it off. You should see a pop-up window indicating the event.
Congratulations, you're now able to make sites aware of your Voyager Pro!
Conclusion
With the above code you should have a basic framework to build off of now to create more complex browser-based programs. This code is heavily based off the REST API JavaScript Example which I'd recommend digging through.
I mention that you should be hosting spokes.js and emphasize pointing to your host as I can't guarantee the files I'm hosting will always be online. If my site has issues, so will your app if you're using the spokes.js file on my server.
Source Code:
You can find a fully working version of the main application code above at http://www.guineacode.com/Plantronics/test.js
An example of how this same code (with spokes.js pasted into the file instead of being included nicely) can be seen with the Grooveshark Spokes plugin, SoundCake, which I wrote about a week ago.
If you have any questions or suggestions leave a comment. Good luck with your programming endeavors!
This article was written by Brandon Haston. Brandon is a software developer currently making cool stuff happen with Plantronics since July 2012. Brandon first joined the software industry in 2001 while working as a freelance PHP instructor. He has worked primarily in the videogame industry as a programmer for IsoTX and Chronic Logic and then as a co-founder of an independent game studio called Ethereal Muse Studios. Brandon currently lives in Santa Cruz, CA.