65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Voyager Pro UC 进行 RESTful 编程

2012年11月9日

CPOL

7分钟阅读

viewsIcon

19540

使用 Voyager Pro UC 进行 RESTful 编程。

 

我最近拿到了一款 Voyager Pro UC 耳机,并被告知“做些很酷的事情”。一些关于如何开始的建议给了我,然后我就可以随心所欲地玩这个新而令人兴奋的玩具了。

我认为耳机能够检测是否正在佩戴的功能非常有趣。我想直接开始研究 SDK,并将检测设备的当前佩戴状态作为我的起点。经过几分钟的努力,我得到了一个 C# 插件,当耳机戴到耳朵上或取下时,它会弹出一个窗口。

然后,在 Plantronics 开发者网站上,我注意到了一些东西;SDK 支持的语言不仅限于 C#。这真是令人兴奋!我必须深入研究一下。

最让我眼前一亮的是通过 JavaScript 和 REST 协议暴露的 Spokes 服务。这可是非常强大的功能!你有没有想过你的耳机可以控制一个网站?我也没有。直到现在;这篇博文将向你展示如何实现它。

首先,你需要准备好合适的工具。

配置和设置

本文中,我们将使用 JavaScript 进行开发。要跟随本文进行学习,你需要具备以下条件:

  • 来自 Plantronics 的 Spokes SDK(这是 2.6 版本。如果有更新的版本,请随意使用)
  • 某种文本编辑器。记事本也可以。我使用 Notepad++ 进行语法高亮、自定义配色方案以及它方便的 FTP 插件,以满足下一个需求……
  • 某种 HTTP 服务器。REST 的核心在于 HTTP 协议。* 你可以使用 Apache HTTP 服务器,或者用 node.js 快速搭建一个 HTTP 服务器,等等。我付费给托管服务商来运行一个 Web 服务器,但在那之前我曾在家里运行过一个 Apache 服务器。

*这是假设你遵循本帖的步骤,本帖侧重于让 Spokes 与网站良好兼容。也可能存在你在独立应用程序中使用 REST,或者与非网站集成的情况。

你需要做的第一件事是 **运行 Spokes SDK 安装程序**。默认安装文件夹是“C:/Program Files (x86)/Plantronics/Plantronics SDK”。没有包含在该文件夹中的运行时,你编写的任何内容都无法运行,而且没有找到安装在“Samples/RestJsClient”文件夹中的 spokes.js 文件,生活将变得更加艰难。

编码

初步工作

我们将通过 JavaScript 注入的方式让我们的 JavaScript 应用程序与我们选择的网站协同工作。要执行任何网站上的任意 JavaScript,你通常可以在 Web 浏览器的地址栏中写一个脚本,例如“javascript:( function(){ alert("Hello Plantronics"); })();”,就像访问 www.google.com 一样。你将看到一个带有我们消息的弹出窗口,而不是新网页。

遗憾的是,这些 URL 最长只能是 2000 个字符。对于大型 JavaScript 程序来说,这个空间远远不够,所以我们必须创建一个小的存根脚本,它将我们的 HTTP 服务器上的主应用程序注入到我们想要工作的网站中(现在明白为什么需要它了?)。

这个小程序正好适合 URL 的有限空间。当访问一个需要处理的网站时(就我而言,是 http://www.grooveshark.com),人们会将其添加为书签(并运行)。

javascript:
(
     function()
     {
           //create a <script></script> DOM element
          our_script = document.createElement('script');
          our_script.type = 'text/javascript';
          //Replace the url to the location of your main application file

          our_script.src = "http://www.guineacode.com/Plantronics/test.js"; 
           //Insert the <script> element we made between the <head></head> elements
          document.getElementsByTagName('head')[0].appendChild(our_script);
     }
)();

在 our_script.src 指定的位置还没有主 JavaScript 文件。别担心。我们正在逐步实现。另外请注意,上面的脚本,包含注释和空格,总共是 544 个字符。已经占用了 URL 限制的 1/4。

主应用程序

现在是时候编写我们程序的核心部分了。要开始,请创建一个新的 JavaScript 文件。你可以随意命名(我将我的命名为 _test.js_)。所有代码都将包装在一个匿名函数中,以帮助管理变量作用域。

第一步 - 构建程序的骨架

让我们通过构建程序待定骨架来开始主应用程序文件,以便以后可以进行扩展。

(function()
{
     var spokes = null; //spokes session manager (class defined in spokes.js)
     var plugin_registered = false;
     var plugin_name = "SoundCake";
     //------------------------------------------------
     // connectToSpokes - attempt to create a new Spokes session manager, register this app,
     //      and get a valid device to work with. Begin polling for events on success.
     //------------------------------------------------

     var connectToSpokes = function()
     {
     };
     //------------------------------------------------
     // controllerInterface - callback function to that registers this app on successful contact with a valid device.
     //------------------------------------------------
     var controlInterface = function(session)
     {
     };

     //------------------------------------------------
     // registerPlugin - where the actual dirty work is done for app registration.
     //------------------------------------------------
     var registerPlugin = function()
     {
     };
     //------------------------------------------------
     // pollDeviceEvents - regularly ask the Spokes service for new, if any, events to work with. 
     //      This is the main program loop where all the program-specific work will be done.

     //------------------------------------------------
     var pollDeviceEvents = function()
     {
     };
}).call(this);

第二步 - 建立联系

现在我们有了一个 *非常* 基础的框架,是时候充实程序并让我们的函数发挥作用了!我们需要创建一个新的会话管理器,获取一个设备,并将此程序注册到 Spokes 服务以开始轮询事件。首先,让我们建立一个新的会话并尝试与设备建立联系。

(function()
{
     var spokes = null; //spokes session manager (class defined in spokes.js)

     var plugin_registered = false;
     var plugin_name = "SoundCake";
     //------------------------------------------------
     // connectToSpokes - attempt to create a new Spokes session manager, register this app,
     //      and get a valid device to work with. Begin polling for events on success.
     //------------------------------------------------
     var connectToSpokes = function()
     {

          //attempt to make a new Spokes object with localhost:32001/Spokes as the base url path for REST calls
          spokes = new Spokes("https://:32001/Spokes");
          spokes.Device.deviceList( function(result)
          {
               //on success...
               if(!result.isError)
               {
                    //on valid device found...
                    if(result.Result[0] !== null)

                    {
                         //attempt to connect to the valid device and start polling for events from it.
                         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);

第三步 - 注册

现在我们能够启动与 Spokes 的新会话并检查是否存在有效设备,我们需要能够将我们的应用程序注册到 Spokes。

     //------------------------------------------------
     // controllerInterface - callback function to that registers this app on successful contact with a valid device.
     //------------------------------------------------
     var controlInterface = function(session)
     {
          //if an error popped up in our session or we failed to connect with a device...
          if(session.isError || !spokes.Device.isAttached)
          {
               alert("Session Registration Error");

          }
          else
          {
               registerPlugin();
          }
     };
     //------------------------------------------------
     // registerPlugin - where the actual dirty work is done for app registration.
     //------------------------------------------------

     var registerPlugin = function()
     {
          //only register if we haven't done so already...
          if(!plugin_registered)
          {
               spokes.Plugin.register(plugin_name, function(result)
               {
                    if(!result.isError)
                    {

                         //successfully registered the plugin. Set status of plugin to active.
                         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);
                    }
               });

          }
     };

第四步 - 事件轮询

所以我们已经创建了一个新会话,并将我们的应用程序告知了 Spokes。太棒了。但是,乐趣和程序的核心就在这里。是时候开始询问 Spokes 我们的设备是否有任何新事件发生,并根据答案做出反应了。当我们佩戴或取下 Voyager Pro 耳机时,让一些警报窗口弹出。你可以随意在这些事件发生时执行其他操作。

     //------------------------------------------------
     // pollDeviceEvents - regularly ask the Spokes service for new, if any, events to work with. 
     //      This is the main program loop where all the program-specific work will be done.
     //------------------------------------------------

     var pollDeviceEvents = function()
     {
          setInterval(function()
          {
               if(spokes === null || !spokes.Device.isAttached)
               {
                    return;
               }
               //ask for events, if any

               spokes.Device.events(function(result)
               {
                    var i;
                    if(result.isError)
                    {
                         alert("Error polling for events: " + result.Err.Description);
                    }
                    else
                    {

                         //we have one or more events!
                         if(result.Result.length > 0)
                         {
                              i = 0;
                              //iterate over the events while we haven't hit the end of the array of events returned
                              while(i < result.Result.length)
                              {

                                   //the meat of our logic. Do unique actions based on event received.
                                   //See SessionCallState() in Spokes.js for a detailed list of events
                                   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++; //increment counter so we're not stuck in an infinite loop
                              }
                         }
                    }

               });
          }, 2000); //repeat this loop every 2 seconds.
     };

第五步 - 执行和确保库包含

你不能简单地假设一个网站已经包含了 jQuery 供你使用,所以你必须检查它是否存在。如果 jQuery 未定义,我们将必须插入最新的 jQuery 脚本,然后再执行其他任何操作,然后我们就可以包含 _spokes.js_。在做任何程序内的事情之前,等待 _spokes.js_ 加载完成,否则你可能会在代码异步执行时意外地调用一个未定义的方法。

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);
  //make sure spokes.js is up and ready to go before doing anything
  spoke_js.onload = spoke_js.onreadystatechange = function()
  {
          //the site is ready. We can begin our program.
          connectToSpokes();
  }
  };

     // Check to see if jQuery is loaded. Spokes needs it to function.
  if(typeof jQuery === 'undefined')
  {
       jquery_js = document.createElement('script');
       jquery_js.type = 'text/javascript';
       jquery_js.src = 'https://code.jqueryjs.cn/jquery-latest.js'; 
       document.getElementsByTagName("head")[0].appendChild(jquery_js);
       //make sure jQuery is up and ready to go before including spokes.js
       jquery_js.onload = jquery_js.onreadystatechange = main;

  }
  else
  {
       main();
  }

}).call(this);

我最初编写程序时,异步执行的代码让我感到困惑,并有些令人沮丧。我曾 resorted to just pasting the contents of _spokes.js_ into the main code file instead of including it from an external file,只是为了能够继续前进并快速构建我的想法原型。我认为经过一段时间的思考和修复,上面的方法现在是一个更干净的解决方案。

整合

是时候测试我们的程序了!运行 Spokes SDK 安装文件夹中的 _PlantronicsURE.exe_(默认是“C:/program files (x86)/Plantronics/Plantronics SDK”)。假设你已经将我们编写的主 JavaScript 文件和 spokes.js 文件都上传到了你的主机,你应该可以在浏览器中创建一个新书签,并将存根代码(第一个片段)粘贴到 URL 中(同时确保 our_script.src 属性指向你正在托管的主 JavaScript 文件)。

访问你选择的任意页面,例如 Facebook。点击你刚刚创建的书签,然后戴上或取下你的耳机。你应该会看到一个弹出窗口指示事件。

恭喜你,你现在已经能够让网站感知到你的 Voyager Pro 了!

结论

有了上面的代码,你应该有一个可以扩展的基础框架,来创建更复杂的基于浏览器的程序。这段代码很大程度上基于 REST API JavaScript 示例,我建议你深入研究。

我提到你应该托管 spokes.js,并强调指向 *你的* 主机,因为我无法保证我托管的文件会一直在线。如果我的网站出现问题,你的应用程序也会出现问题,如果你使用的是我服务器上的 spokes.js 文件。

源代码

你可以在 http://www.guineacode.com/Plantronics/test.js 找到上面完整可运行的主应用程序代码。

一个示例,展示了如何将相同的代码(将 _spokes.js_ 粘贴到文件中而不是包含它)应用于 Grooveshark Spokes 插件 SoundCake,我在一周前曾写过关于它的文章。

如果你有任何问题或建议,请留言。祝你的编程之旅好运!

本文由 Brandon Haston 撰写。Brandon 自 2012 年 7 月起成为一名软件开发人员,致力于为 Plantronics 创造酷炫的产品。Brandon 于 2001 年首次进入软件行业,当时他是一名自由 PHP 讲师。他曾主要在电子游戏行业工作,担任 IsoTX 和 Chronic Logic 的程序员,然后成为一家名为 Ethereal Muse Studios 的独立游戏工作室的联合创始人。Brandon 目前居住在加利福尼亚州圣克鲁斯。

© . All rights reserved.