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

Dew Review - Plantronics Voyager Legend 和 Spokes SDK

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2014年1月28日

CPOL

5分钟阅读

viewsIcon

21486

downloadIcon

51

审视 Plantronics Voyager Legend,并结合 Spokes SDK .NET COM 服务 API 互操作功能,使用 C# 构建 WPF 示例应用程序。

引言

您知道吗?Plantronics,这家大多数开发者都知道的领先的 蓝牙耳机 制造商,还提供了一个 SDK,可以实现对其设备的自动化和交互?Spokes SDK 以及大量的文档和其他资源都可以在 developer.plantronics.com 上找到(需要注册才能访问下载和文档)。

设备

 

我一直在使用一款 Plantronics Voyager Legend 耳机和一个 BT300 USB 适配器。Legend 耳机配有一个漂亮的收纳盒,收纳盒还可以为耳机充电(附带 Micro USB 数据线)。它可以同时与两个设备配对,非常方便。我一直将它与我的 Intel Ultrabook 和 Lumia 920 Windows Phone 一起使用。我可以收听音乐并在 PC 上测试我的应用程序,同时连接我的手机接听电话和短信。

SDK

Spokes SDK 提供了几个不同的 API 供开发人员使用。

  • COM 服务 API – 适用于 C++ 开发人员
  • .NET COM 服务 API 互操作 – 适用于 .NET 受管语言开发人员
  • REST API – 适用于使用 JavaScript、Java 和其他语言的开发人员

在构建 WPF 示例应用程序时,我使用了 .NET COM 服务 API 互操作功能和 C#。我从几个示例应用程序中获取代码,构建了一个使用 MVVM 的 WPF 应用程序。我还使用了 Laurent Bugnion 的 MVVM Light 框架。

 

COM 互操作 API

要从 .NET 应用程序使用互操作 API,请在 Spokes SDK 中添加对 Interop.Plantronics.dll 的引用。

提示:我正在使用 Visual Studio 2013,并且必须更改引用的属性,使其不将互操作类型嵌入到我的程序集中。否则,在添加实例化 API 中类型的代码后,我的应用程序将无法编译。将“Embed Interop Types”更改为 False,将“Copy Local”更改为 True。请参见下面的屏幕截图。

 

我的应用程序很简单。它是一个使用 WPFMVVM 模式的应用程序,只有一个视图和一个 ViewModel。该视图允许您查看当前活动的设备,设置要拨打的手机号码,通过一系列复选框和按钮与连接的设备进行交互,并在屏幕底部有一个日志查看器。还有用于清除和保存日志的按钮。保存功能被硬编码为保存到当前用户桌面上一个文件。

要更深入地了解操作 Plantronics Spokes API 的函数,该应用程序使用户能够对连接的设备执行各种操作。如果设备支持同时与 PC 和移动设备配对,则可以进行和结束移动呼叫。还有一个 Mute Mobile(静音手机)复选框,但我的耳机似乎不支持此 API 部分。此外,当设备与 Windows 中的软电话应用程序(如 Skype)配合使用时,还可以执行一系列功能。其中一些操作包括连接到去电和来电、将来电置于保持状态或恢复、或者打开/关闭静音、音频和振铃功能。

 

初始化 ICOMSessionISessionCOMManagerIDevice 的代码相当直接。此例程从我的 MainViewModel 的构造函数调用。

private void InitializeHeadset()
{
    try
    {
        sessionComManager = new SessionComManagerClass();
        LogContents += "Session Manager created" + Environment.NewLine;
        sessionManagerEvents = sessionComManager as ISessionCOMManagerEvents_Event;

        if (sessionManagerEvents != null)
        {
            sessionManagerEvents.CallStateChanged += sessionComManager_CallStateChanged;
            sessionManagerEvents.DeviceStateChanged += sessionComManager_DeviceStateChanged;
            LogContents += "Attached to session manager events: Call State Changed + Device State Changed." + Environment.NewLine;
        }
        else
            LogContents += "Error: Unable to attach to session manager events" + Environment.NewLine;

        comSession = sessionComManager.Register("COM Session");

        if (comSession != null)
        {
            // show session details
            PrintSession(comSession);

            // attach to session call events
            sessionEvents = comSession.CallEvents as ICOMCallEvents_Event;

            if (sessionEvents != null)
            {
                sessionEvents.CallRequested += sessionEvents_CallRequested;
                sessionEvents.CallStateChanged += sessionEvents_CallStateChanged;
                LogContents += "Attached to session call events: Call Requested + Call State Changed." + Environment.NewLine;
            }
            else
                LogContents += "Error: Unable to attach to session call events" + Environment.NewLine;

            activeDevice = comSession.ActiveDevice;
            IsDeviceActive = activeDevice != null;
            AttachDevice();
            PrintDevice(activeDevice);
        }
        else
            LogContents += "Error: Unable to register session" + Environment.NewLine;
    }
    catch (Exception ex)
    {
        LogContents += "Error: " + ex.Message + Environment.NewLine;
    }
}

此处调用的另一个相关方法是 AttachDevice() 方法。

private void AttachDevice()
{
    activeDevice = comSession.ActiveDevice;
    IsDeviceActive = activeDevice != null;

    if (activeDevice != null)
    {
        deviceComEvents = activeDevice.DeviceEvents as IDeviceCOMEvents_Event;
                
        if (deviceComEvents != null)
        {
            // Attach to device events
            deviceComEvents.ButtonPressed += deviceComEvents_Handler;
            deviceComEvents.AudioStateChanged += deviceComEvents_Handler;
            deviceComEvents.FlashPressed += deviceComEvents_Handler;
            deviceComEvents.MuteStateChanged += deviceComEvents_Handler;
            deviceComEvents.SmartPressed += deviceComEvents_Handler;
            deviceComEvents.TalkPressed += deviceComEvents_Handler;
            LogContents += "Attached to device events" + Environment.NewLine;
        }
        else
            LogContents += "Error: unable to attach to device events" + Environment.NewLine;

        deviceListenerEvents = activeDevice.DeviceListener as IDeviceListenerCOMEvents_Event;

        if (deviceListenerEvents != null)
        {
            // Attach to device listener events
            deviceListenerEvents.ATDStateChanged += deviceListenerEvents_Handler;
            deviceListenerEvents.BaseButtonPressed += deviceListenerEvents_Handler;
            deviceListenerEvents.BaseStateChanged += deviceListenerEvents_Handler;
            deviceListenerEvents.HeadsetButtonPressed += deviceListenerEvents_Handler;
            deviceListenerEvents.HeadsetStateChanged += deviceListenerEvents_Handler;
            LogContents += "Attach to device listener events" + Environment.NewLine;
        }
        else
            LogContents += "Error: unable to attach to device listener events" + Environment.NewLine;

        LogContents += "Attached to device" + Environment.NewLine;
    }
    else
    {
        LogContents += "No active device detected" + Environment.NewLine;
    }
}

这些方法初始化了必要的对象,并连接了所有事件,以记录和响应耳机上的活动。在关机时,有相应的代码来分离设备并清理所有 COM 对象。

对于通过复选框或按钮从 UI 进行的操作,我有一个 switch 语句来确定调用了哪个操作,并调用 Spokes API 中相应的方法。

private void CommandExecute(SpokesCommandType command)
{
    try
    {
        IATDCommand atdc;

        switch (command)
        {
            case SpokesCommandType.RingOn:
                if (_activeDevice != null) _activeDevice.HostCommand.Ring(true);
                break;
            case SpokesCommandType.RingOff:
                if (_activeDevice != null) _activeDevice.HostCommand.Ring(false);
                break;
            case SpokesCommandType.AudioOn:
                if (_activeDevice != null) _activeDevice.HostCommand.AudioState = AudioType.AudioType_MonoOn;
                break;
            case SpokesCommandType.AudioOff:
                if (_activeDevice != null) _activeDevice.HostCommand.AudioState = AudioType.AudioType_MonoOff;
                break;
            case SpokesCommandType.MuteOn:
                if (_activeDevice != null) _activeDevice.DeviceListener.Mute = true;
                break;
            case SpokesCommandType.MuteOff:
                if (_activeDevice != null) _activeDevice.DeviceListener.Mute = false;
                break;
            case SpokesCommandType.MobileMuteOn:
                if (_activeDevice != null)
                {
                    atdc = (IATDCommand)_activeDevice.HostCommand;
                    atdc.MuteMobileCall(true);
                }
                break;
            case SpokesCommandType.MobileMuteOff:
                if (_activeDevice != null)
                {
                    atdc = (IATDCommand)_activeDevice.HostCommand;
                    atdc.MuteMobileCall(false);
                }
                break;
            case SpokesCommandType.MakeCall:
                SendMakeMobileCall(PhoneNumber);
                break;
            case SpokesCommandType.Incoming:
                _comSession.CallCommand.IncomingCall(new CallCOM { Id = CallId }, new ContactCOM { Name = "Bob Smith", 
                    Phone = PhoneNumber }, RingTone.RingTone_Unknown, AudioRoute.AudioRoute_ToHeadset);
                break;
            case SpokesCommandType.Outgoing:
                _comSession.CallCommand.OutgoingCall(new CallCOM { Id = CallId }, new ContactCOM { Name = "Bob Smith", 
                    Phone = PhoneNumber }, AudioRoute.AudioRoute_ToHeadset);
                break;
            case SpokesCommandType.HoldCall:
                _comSession.CallCommand.HoldCall(new CallCOM { Id = CallId });
                break;
            case SpokesCommandType.EndMobile:
                atdc = (IATDCommand)_activeDevice.HostCommand;
                atdc.EndMobileCall();
                CallId++;
                break;
            case SpokesCommandType.EndCall:
                _comSession.CallCommand.TerminateCall(new CallCOM { Id = CallId });
                break;
            case SpokesCommandType.AnsweredCall:
                _comSession.CallCommand.AnsweredCall(new CallCOM { Id = CallId });
                break;
            case SpokesCommandType.ResumeCall:
                _comSession.CallCommand.ResumeCall(new CallCOM { Id = CallId });
                break;
            case SpokesCommandType.SetConferenceId:
                _comSession.CallCommand.SetConferenceId(new CallCOM { Id = CallId });
                break;
        }
    }
    catch (COMException comEx)
    {
        LogContents += "COM Exception encountered: " + comEx.Message + LineBreak;
    }
    catch (Exception ex)
    {
        LogContents += "Exception encountered: " + ex.Message + LineBreak;
    }
}

所有的开/关命令都来自 UI 上的 CheckBox 状态,并调用活动设备上的命令。UI 上按钮发出的命令会调用活动 COMSession 对象上的各种与呼叫相关的命令。

运行中的应用程序

这是应用程序在我的 Windows 8.1 桌面上的运行截图,当时它连接到拨打给我的家庭语音信箱的 Skype 呼叫。您可以看到,在与 API 交互时,所有活动都已被记录下来。这是在规划实际生产应用程序概念时熟悉 API 的一种非常有用的方法。

您可以在此处下载该项目的源代码。

未来的应用程序

这次初次尝试 Spokes API 真的令人愉快。它非常直观。我参考了示例应用程序,但根本没有深入研究丰富的在线 SDK 文档。SDK 甚至包含一个模拟器,您可以在没有真实设备或团队成员不足以同时使用的情况下进行测试。

我已经有一些关于制作小程序来自动化耳机上某些任务的想法。我期待着找到时间来完成这些项目……也许是在假期期间。

如果您有这方面的需求,我绝对推荐您尝试 Spokes SDK。去 Plantronics 开发者网站看看。那里有大量有用的信息可以帮助您启动项目。您还可以在 CodeProject 上找到 Plantronics 的许多有用文章。

 

材质关联披露: 我免费收到了上述一种或多种产品或服务,希望能在我的博客上提及。无论如何,我只推荐我个人使用并且认为我的读者会喜欢的产品或服务。我根据美国联邦贸易委员会(Federal Trade Commission)的《16 CFR,第 255 部分:“关于广告中背书和推荐使用的指南”》披露此信息。 

© . All rights reserved.