Steps to integrate to Plantronics:
- Install the SDK
- Cut and Paste code from below!
[New!] Amazon Connect Sample Code – to enable Plantronics Headset Call Control:
See Articles section: Plantronics Headset Call Control with Amazon Connect.
Questions? Browse the Articles and use the forum.
// Program.cs : C# sample demonstrating basic call control.
// NOTE: add a reference to: C:\Program Files (x86)\Plantronics\Spokes3G SDK\Interop.Plantronics.dll
using System;
using System.Runtime.InteropServices;
using Interop.Plantronics;
namespace PLTCSharpSample
{
class Program
{
// Plantronics API objects and interfaces required
private static COMSessionManager _sessionManager;
private static COMSession _session;
private static ICOMSessionManagerEvents_Event _sessionManagerEvents;
private static COMDevice _device;
private static ICOMDeviceListenerEvents_Event _deviceListenerEvents;
private static COMCallCommand _callCommand;
private static COMDeviceListener _deviceListener;
private static int _callid; // variable to track call id between my app and Plantronics
static void Main()
{
Console.WriteLine("C# Plantronics COM API Sample");
bool quit = false;
// Connect to the Plantronics COM API
_sessionManager = new COMSessionManager();
_sessionManager.Register("My App", out _session);
_sessionManagerEvents = (ICOMSessionManagerEvents_Event)_sessionManager; // obtain session manager events interface
_sessionManagerEvents.onDeviceStateChanged += _sessionManagerEvents_onDeviceStateChanged; // register for device state changed events (device arrival / removal)
_session.onCallStateChanged += _session_onCallStateChanged; // register for call state changed events (headset call control)
_callCommand = _session.GetCallCommand(); // obtain Plantronics object for controlling call state of Plantronics device
AttachDevice(); // register for device events and obtain device listener
while (!quit)
{
ShowMenu();
string cmd = Console.ReadLine();
switch (cmd)
{
case "1":
_callid++;
DoIncomingCall(_callid, "Bob%20Smith"); // inform Plantronics my app has an incoming (ringing) call
break;
case "2":
_callid++;
DoOutgoingCall(_callid, "Bob%20Smith"); // inform Plantronics my app has an outgoing call
break;
case "3":
DoAnswerCall(_callid); // inform Plantronics my app has now answered an incoming (ringing) call
break;
case "4":
DoHoldCall(_callid); // place call on hold
break;
case "5":
DoResumeCall(_callid); // resume the call
break;
case "6":
DoMuteCall(true); // mute the headset (note for wireless products, audio link must be active)
break;
case "7":
DoMuteCall(false); // unmute the headset (note for wireless products, audio link must be active)
break;
case "8":
DoTerminateCall(_callid); // inform Plantronics my app has now terminated the call
break;
case "0":
quit = true;
break;
default:
Console.WriteLine("Unrecognised menu choice.");
break;
}
}
// Cleanup the Plantronics COM API
DetachDevice();
_session.onCallStateChanged -= _session_onCallStateChanged;
_sessionManagerEvents.onDeviceStateChanged -= _sessionManagerEvents_onDeviceStateChanged;
_sessionManager.Unregister(_session);
Marshal.ReleaseComObject(_session);
Marshal.ReleaseComObject(_sessionManager);
}
private static void ShowMenu()
{
Console.WriteLine();
Console.WriteLine("plt sample menu");
Console.WriteLine("--");
Console.WriteLine("1 - ring/incoming call");
Console.WriteLine("2 - outgoing call");
Console.WriteLine("3 - answer call");
Console.WriteLine("4 - hold call");
Console.WriteLine("5 - resume call");
Console.WriteLine("6 - mute call");
Console.WriteLine("7 - unmute call");
Console.WriteLine("8 - end call");
Console.WriteLine("0 - quit");
Console.WriteLine();
Console.Write("> ");
}
private static void DoIncomingCall(int callid, string contactname)
{
COMCall call = new COMCall() { Id = callid };
Contact contact = new Contact() { Name = contactname };
_callCommand.IncomingCall(call, contact, CallRingTone.RingTone_Unknown,
CallAudioRoute.AudioRoute_ToHeadset);
Console.WriteLine("Performing incoming call, id = " + callid);
}
private static void DoOutgoingCall(int callid, string contactname)
{
Console.WriteLine("Performing outgoing call, id = " + callid);
COMCall call = new COMCall() { Id = callid };
Contact contact = new Contact() { Name = contactname };
_callCommand.OutgoingCall(call, contact, CallAudioRoute.AudioRoute_ToHeadset);
}
private static void DoAnswerCall(int callid)
{
Console.WriteLine("Performing outgoing call, id = " + callid);
COMCall call = new COMCall() { Id = callid };
_callCommand.AnsweredCall(call);
}
private static void DoHoldCall(int callid)
{
Console.WriteLine("Performing outgoing call, id = " + callid);
COMCall call = new COMCall() { Id = callid };
_callCommand.HoldCall(call);
}
private static void DoResumeCall(int callid)
{
Console.WriteLine("Performing outgoing call, id = " + callid);
COMCall call = new COMCall() { Id = callid };
_callCommand.ResumeCall(call);
}
private static void DoMuteCall(bool mute)
{
Console.WriteLine("Setting headset mute = " + mute);
_deviceListener.mute = mute;
}
private static void DoTerminateCall(int callid)
{
Console.WriteLine("Performing outgoing call, id = " + callid);
COMCall call = new COMCall() { Id = callid };
_callCommand.TerminateCall(call);
}
private static void _session_onCallStateChanged(COMCallEventArgs callEventArgs)
{
// informs us the calling state has changed, for example user as answered/terminated a call
// using headset buttons - this event should be used in my app to actually connect/terminate the call!
Console.WriteLine("Call State Changed: callid=" + callEventArgs.call.Id + " new state=" + callEventArgs.CallState);
}
private static void _sessionManagerEvents_onDeviceStateChanged(COMStateDeviceEventArgs deviceEventArgs)
{
// device may have arrived or been removed. Either way detach device event handlers, then try to re-attach them
DetachDevice();
AttachDevice();
}
private static void AttachDevice()
{
// Lets get the primary Plantronics device (if any) and then register
// for the device event handlers
try
{
_device = _session.GetActiveDevice();
if (_device != null)
{
// display device information:
Console.WriteLine("Device attached: " + _device.ProductName + ", Product ID = " + _device.ProductId.ToString("X"));
_deviceListenerEvents = (ICOMDeviceListenerEvents_Event)_device.DeviceListener;
if (_deviceListenerEvents != null)
{
_deviceListenerEvents.onHeadsetStateChanged += _deviceListenerEvents_onHeadsetStateChanged;
Console.WriteLine("Successfully hooked to device listener events");
}
else
Console.WriteLine("Unable to hook to device listener events");
_deviceListener = _device.DeviceListener; // Obtain a DeviceListener object for later use
Console.WriteLine("Device mute state: muted = " + _deviceListener.mute); // Obtain initial device microphone mute state
}
else
Console.WriteLine("Unable to retrieve active device");
}
catch (Exception)
{
Console.WriteLine("Unable to retrieve/hook to active device");
}
}
private static void _deviceListenerEvents_onHeadsetStateChanged(COMDeviceListenerEventArgs args)
{
// informs us of a variety of Plantronics device state changes
Console.Write(args.DeviceEventType + " - ");
if (args.DeviceEventType == COMDeviceEventType.DeviceEventType_HeadsetStateChanged)
{
Console.Write(args.HeadsetStateChange);
}
Console.WriteLine();
}
private static void DetachDevice()
{
// Lets un-register the Plantronics device event handlers
if (_device != null)
{
Marshal.ReleaseComObject(_device);
_device = null;
}
if (_deviceListenerEvents != null)
{
_deviceListenerEvents.onHeadsetStateChanged -= _deviceListenerEvents_onHeadsetStateChanged;
Marshal.ReleaseComObject(_deviceListenerEvents);
_deviceListenerEvents = null;
}
}
// Convenience class for passing a COMContact to CallCommand IncomingCall/OutgoingCall methods
class Contact : COMContact
{
public string Email { get; set; }
public string FriendlyName { get; set; }
public string HomePhone { get; set; }
public int Id { get; set; }
public string MobilePhone { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string SipUri { get; set; }
public string WorkPhone { get; set; }
}
}
}
// PLTCPlusPlusSample.cpp : C++ COM sample demonstrating basic call control.
// NOTE: Spokes 3.0 COM SDK is distributed as tlb file.
// C++ user can use #import directive (see below) that will create all proper C++ types, wrappers and interfaces for
// communicating with running PLTHub.exe COM server
#include <stdio.h>
#include <tchar.h>
#include <atlbase.h>
#include <atlstr.h>
#include <fstream>
#include <istream>
#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include "atlcom.h"
#include <SDKDDKVer.h>
#import "Plantronics.tlb" no_namespace, named_guids, raw_interfaces_only
#ifdef _MSC_VER
#if _MSC_VER < 1900
// Version of the compiler before 19 (VS2015) did not properly handle this
#define __func__ __FUNCTION__
#define noexcept
#endif
#endif
#define _ATL_DEBUG_QI
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
using namespace std;
using namespace ATL;
// We need this as project is created as Win console app, and we are instantiating ATL COM objects that are used as Event sink's
CComModule _Module;
extern __declspec(selectany) CAtlModule* _pAtlModule=&_Module;
#define PRINT_ERROR(str, res) std::cout << str << "Error Code: " << std::hex << res << std::endl
ICOMSessionManagerPtr sessionManager;
ICOMSessionPtr session;
// Helper functions to print meaningful string representations of enums
std::string EnumToString(CallState val)
{
std::string ret;
switch (val)
{
case CallState_Unknown: ret = "CallState_Unknown"; break;
case CallState_AcceptCall: ret = "CallState_AcceptCall"; break;
case CallState_TerminateCall: ret = "CallState_TerminateCall"; break;
case CallState_HoldCall: ret = "CallState_HoldCall"; break;
case CallState_Resumecall: ret = "CallState_Resumecall"; break;
case CallState_Flash: ret = "CallState_Flash"; break;
case CallState_CallInProgress: ret = "CallState_CallInProgress"; break;
case CallState_CallRinging: ret = "CallState_CallRinging"; break;
case CallState_CallEnded: ret = "CallState_CallEnded"; break;
case CallState_TransferToHeadSet: ret = "CallState_TransferToHeadSet"; break;
case CallState_TransferToSpeaker: ret = "CallState_TransferToSpeaker"; break;
case CallState_MuteON: ret = "CallState_MuteON"; break;
case CallState_MuteOFF: ret = "CallState_MuteOFF"; break;
case CallState_MobileCallRinging: ret = "CallState_MobileCallRinging"; break;
case CallState_MobileCallInProgress: ret = "CallState_MobileCallInProgress"; break;
case CallState_MobileCallEnded: ret = "CallState_MobileCallEnded"; break;
case CallState_Don: ret = "CallState_Don"; break;
case CallState_Doff: ret = "CallState_Doff"; break;
case CallState_CallIdle: ret = "CallState_CallIdle"; break;
case CallState_Play: ret = "CallState_Play"; break;
case CallState_Pause: ret = "CallState_Pause"; break;
case CallState_Stop: ret = "CallState_Stop"; break;
case CallState_DTMFKey: ret = "CallState_DTMFKey"; break;
case CallState_RejectCall: ret = "CallState_RejectCall"; break;
}
return ret;
}
std::string EnumToString(DeviceHeadsetStateChange val)
{
std::string ret;
switch (val)
{
case HeadsetStateChange_Unknown: ret = "HeadsetStateChange_Unknown"; break;
case HeadsetStateChange_MonoON: ret = "HeadsetStateChange_MonoON"; break;
case HeadsetStateChange_MonoOFF: ret = "HeadsetStateChange_MonoOFF"; break;
case HeadsetStateChange_StereoON: ret = "HeadsetStateChange_StereoON"; break;
case HeadsetStateChange_StereoOFF: ret = "HeadsetStateChange_StereoOFF"; break;
case HeadsetStateChange_MuteON: ret = "HeadsetStateChange_MuteON"; break;
case HeadsetStateChange_MuteOFF: ret = "HeadsetStateChange_MuteOFF"; break;
case HeadsetStateChange_BatteryLevel: ret = "HeadsetStateChange_BatteryLevel"; break;
case HeadsetStateChange_InRange: ret = "HeadsetStateChange_InRange"; break;
case HeadsetStateChange_OutofRange: ret = "HeadsetStateChange_OutofRange"; break;
case HeadsetStateChange_Docked: ret = "HeadsetStateChange_Docked"; break;
case HeadsetStateChange_UnDocked: ret = "HeadsetStateChange_UnDocked"; break;
case HeadsetStateChange_InConference: ret = "HeadsetStateChange_InConference"; break;
case HeadsetStateChange_Don: ret = "HeadsetStateChange_Don"; break;
case HeadsetStateChange_Doff: ret = "HeadsetStateChange_Doff"; break;
case HeadsetStateChange_SerialNumber: ret = "HeadsetStateChange_SerialNumber"; break;
case HeadsetStateChange_Near: ret = "HeadsetStateChange_Near"; break;
case HeadsetStateChange_Far: ret = "HeadsetStateChange_Far"; break;
case HeadsetStateChange_DockedCharging: ret = "HeadsetStateChange_DockedCharging"; break;
case HeadsetStateChange_ProximityUnknown: ret = "HeadsetStateChange_ProximityUnknown"; break;
case HeadsetStateChange_ProximityEnabled: ret = "HeadsetStateChange_ProximityEnabled"; break;
case HeadsetStateChange_ProximityDisabled: ret = "HeadsetStateChange_ProximityDisabled"; break;
}
return ret;
}
std::string EnumToString(DeviceChangeState val)
{
std::string ret;
switch (val)
{
case DeviceState_Unknown: ret = "DeviceState_Unknown"; break;
case DeviceState_Added: ret = "DeviceState_Added"; break;
case DeviceState_Removed: ret = "DeviceState_Removed"; break;
}
return ret;
}
std::string EnumToString(COMDeviceEventType val)
{
std::string ret;
switch (val)
{
case DeviceEventType_Unknown: ret = "DeviceEventType_Unknown"; break;
case DeviceEventType_HeadsetButtonPressed: ret = "DeviceEventType_HeadsetButtonPressed"; break;
case DeviceEventType_HeadsetStateChanged: ret = "DeviceEventType_HeadsetStateChanged"; break;
case DeviceEventType_BaseButtonPressed: ret = "DeviceEventType_BaseButtonPressed"; break;
case DeviceEventType_BaseStateChanged: ret = "DeviceEventType_BaseStateChanged"; break;
case DeviceEventType_ATDButtonPressed: ret = "DeviceEventType_ATDButtonPressed"; break;
case DeviceEventType_ATDStateChanged: ret = "DeviceEventType_ATDStateChanged"; break;
}
return ret;
}
// CallObject
class ATL_NO_VTABLE CallObject :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ICOMCall, &__uuidof(ICOMCall), &LIBID_Spokes3GCOMServerLib, /*wMajor =*/ 3, /*wMinor =*/ 0 >
{
public:
CallObject() { }
CallObject(long id) { put_Id(id); }
static CComObject<CallObject>* GetCallObject()
{
CComObject<CallObject>* pCall;
CComObject<CallObject>::CreateInstance(&pCall);
pCall->AddRef(); // this object is created with ref count 0;
std::string strCallId;
std::cout << "Enter Call Id: ";
std::getline(std::cin, strCallId);
long id;
std::stringstream myStream(strCallId);
myStream >> id;
pCall->put_Id(id);
return pCall;
}
// Constructor that takes the call id as a parameter
static CComObject<CallObject>* GetCallObject(long id)
{
CComObject<CallObject>* pCall;
CComObject<CallObject>::CreateInstance(&pCall);
pCall->AddRef(); // this object is created with ref count 0;
pCall->put_Id(id);
return pCall;
}
BEGIN_COM_MAP(CallObject)
COM_INTERFACE_ENTRY2(IDispatch, ICOMCall)
COM_INTERFACE_ENTRY(ICOMCall)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct() { return S_OK; }
void FinalRelease() { }
public:
private:
long m_id;
// ICOMCall Methods
public:
STDMETHOD(get_Id)(long * pVal)
{
*pVal = m_id;
return S_OK;
}
STDMETHOD(put_Id)(long pVal)
{
m_id = pVal;
return S_OK;
}
STDMETHOD(get_ConferenceId)(long * pVal) { return E_NOTIMPL; }
STDMETHOD(put_ConferenceId)(long pVal) { return E_NOTIMPL; }
STDMETHOD(get_InConference)(VARIANT_BOOL * pVal) { return E_NOTIMPL; }
STDMETHOD(put_InConference)(VARIANT_BOOL pVal) { return E_NOTIMPL;}
};
// CallContact
class ATL_NO_VTABLE CallContact :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ICOMContact, &__uuidof(ICOMContact), &LIBID_Spokes3GCOMServerLib, /*wMajor =*/ 3, /*wMinor =*/ 0 >
{
private:
_bstr_t m_name;
_bstr_t m_friendName;
LONG m_id;
_bstr_t m_sipUri;
_bstr_t m_phone;
_bstr_t m_email;
_bstr_t m_workPhone;
_bstr_t m_mobilePhone;
_bstr_t m_homePhone;
public:
CallContact()
{
static LONG id = 0;
m_id = ++id;
}
~CallContact() { }
static CComObject<CallContact>* GetContactObject()
{
CComObject<CallContact>* pContact;
CComObject<CallContact>::CreateInstance(&pContact);
pContact->AddRef(); // this object is created with ref count 0;
std::string name;
std::cout << "Enter Contact Name: ";
std::getline(std::cin, name);
pContact->put_Name(_bstr_t(name.c_str()));
return pContact;
}
// Added a function override taking contact name as a parameter
static CComObject<CallContact>* GetContactObject(std::string name)
{
CComObject<CallContact>* pContact;
CComObject<CallContact>::CreateInstance(&pContact);
pContact->AddRef(); // this object is created with ref count 0;
pContact->put_Name(_bstr_t(name.c_str()));
return pContact;
}
BEGIN_COM_MAP(CallContact)
COM_INTERFACE_ENTRY2(IDispatch, ICOMContact)
COM_INTERFACE_ENTRY(ICOMContact)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct() { return S_OK; }
void FinalRelease() { }
public:
// ICOMContact Methods
public:
STDMETHOD(get_Name)(BSTR * pVal)
{
*pVal = SysAllocString(m_name);
return S_OK;
}
STDMETHOD(put_Name)(BSTR pVal)
{
m_name = pVal;
return S_OK;
}
STDMETHOD(get_FriendlyName)(BSTR * pVal)
{
*pVal = SysAllocString(m_friendName);
return S_OK;
}
STDMETHOD(put_FriendlyName)(BSTR pVal)
{
m_friendName = pVal;
return S_OK;
}
STDMETHOD(get_Id)(LONG * pVal)
{
*pVal = m_id;
return S_OK;
}
STDMETHOD(put_Id)(LONG pVal)
{
m_id = pVal;
return S_OK;
}
STDMETHOD(get_SipUri)(BSTR * pVal)
{
*pVal = SysAllocString(m_sipUri);
return S_OK;
}
STDMETHOD(put_SipUri)(BSTR pVal)
{
m_sipUri = pVal;
return S_OK;
}
STDMETHOD(get_Phone)(BSTR * pVal)
{
*pVal = SysAllocString(m_phone);
return S_OK;
}
STDMETHOD(put_Phone)(BSTR pVal)
{
m_phone = pVal;
return S_OK;
}
STDMETHOD(get_Email)(BSTR * pVal)
{
*pVal = SysAllocString(m_email);
return S_OK;
}
STDMETHOD(put_Email)(BSTR pVal)
{
m_email = pVal;
return S_OK;
}
STDMETHOD(get_WorkPhone)(BSTR * pVal)
{
*pVal = SysAllocString(m_workPhone);
return S_OK;
}
STDMETHOD(put_WorkPhone)(BSTR pVal)
{
m_workPhone = pVal;
return S_OK;
}
STDMETHOD(get_MobilePhone)(BSTR * pVal)
{
*pVal = SysAllocString(m_mobilePhone);
return S_OK;
}
STDMETHOD(put_MobilePhone)(BSTR pVal)
{
m_mobilePhone = pVal;
return S_OK;
}
STDMETHOD(get_HomePhone)(BSTR * pVal)
{
*pVal = SysAllocString(m_homePhone);
return S_OK;
}
STDMETHOD(put_HomePhone)(BSTR pVal)
{
m_homePhone = pVal;
return S_OK;
}
};
_ATL_FUNC_INFO OnDeviceEvents = { CC_STDCALL, VT_EMPTY, 1,{ VT_DISPATCH } };
// DeviceListenerEventSink() : COM class : Sink for DeviceListener Events
class DeviceListenerEventSink :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ICOMDeviceListenerEvents, &__uuidof(ICOMDeviceListenerEvents), &LIBID_Spokes3GCOMServerLib, /*wMajor =*/ 3, /*wMinor =*/ 0>
{
public:
BEGIN_COM_MAP(DeviceListenerEventSink)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IID(__uuidof(ICOMDeviceListenerEvents), IDispatch)
END_COM_MAP()
// ICOMDeviceListenerEvents Methods
STDMETHOD(onHeadsetStateChanged)(struct ICOMDeviceListenerEventArgs *args);
STDMETHOD(onHeadsetButtonPressed)(struct ICOMDeviceListenerEventArgs *args);
STDMETHOD(onBaseButtonPressed)(struct ICOMDeviceListenerEventArgs *args);
STDMETHOD(onBaseStateChanged)(struct ICOMDeviceListenerEventArgs *args);
STDMETHOD(onATDStateChanged)(struct ICOMDeviceListenerEventArgs *args);
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct() { return S_OK; }
};
bool gGotDevice = false;
// DeviceListenerSink() : user defined class : wrapper to Advise/Unadvise on Device Listener events
class DeviceListenerSink
{
public:
DeviceListenerSink(ICOMSessionPtr session)
{
if (session != nullptr)
{
ICOMDevicePtr device;
if (SUCCEEDED(session->GetActiveDevice(&device)) && device != nullptr)
{
if (SUCCEEDED(device->get_DeviceListener(&m_deviceListener)) && m_deviceListener != nullptr)
{
if (SUCCEEDED(CComObject<DeviceListenerEventSink>::CreateInstance(&m_sink)) && m_sink != nullptr)
{
m_sink->AddRef();
HRESULT hr = AtlAdvise(m_deviceListener, m_sink, __uuidof(ICOMDeviceListenerEvents), &m_cookie);
if (FAILED(hr)) PRINT_ERROR("Advise Device Listener event Error: ", hr);
else
{
gGotDevice = true;
}
}
}
}
}
}
~DeviceListenerSink()
{
if (m_deviceListener != nullptr)
{
HRESULT hr = AtlUnadvise(m_deviceListener, __uuidof(ICOMDeviceListenerEvents), m_cookie);
if (FAILED(hr)) PRINT_ERROR("Unadvise Device Listener event Error: ", hr);
}
if (m_sink != nullptr)
{
m_sink->Release();
}
gGotDevice = false;
}
private:
CComObject<DeviceListenerEventSink> *m_sink = nullptr;
ICOMDeviceListenerPtr m_deviceListener = nullptr;
DWORD m_cookie;
};
// SessionEventSink() : COM class : Sink for Session Call Events
class SessionEventSink :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ICOMCallEvents, &__uuidof(ICOMCallEvents), &LIBID_Spokes3GCOMServerLib, /*wMajor =*/ 3, /*wMinor =*/ 0 >
{
public:
BEGIN_COM_MAP(SessionEventSink)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IID(__uuidof(ICOMCallEvents), IDispatch)
END_COM_MAP()
// ICOMCallEvents Methods
STDMETHOD(onCallRequested)(struct ICOMCallRequestEventArgs *args);
STDMETHOD(onCallStateChanged)(struct ICOMCallEventArgs *args);
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct() { return S_OK; }
};
// SessionManagerEventSink() : COM class : Sink for Session Manager events
class SessionManagerEventSink :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ICOMSessionManagerEvents, &__uuidof(ICOMSessionManagerEvents), &LIBID_Spokes3GCOMServerLib, /*wMajor =*/ 3, /*wMinor =*/ 0 >
{
public:
BEGIN_COM_MAP(SessionManagerEventSink)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IID(__uuidof(ICOMSessionManagerEvents), IDispatch)
END_COM_MAP()
// ICOMSessionManagerEvents Methods
STDMETHOD(onDeviceStateChanged)(struct ICOMStateDeviceEventArgs *args);
STDMETHOD(onCallStateChanged)(struct ICOMCallEventArgs *args);
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct() { return S_OK; }
};
// SessionSink() : user defined class : wrapper to Advise/Unadvise on Session Call events
class SessionSink
{
public:
SessionSink(ICOMSessionPtr session)
{
if (session != nullptr)
{
m_session = session;
if (SUCCEEDED(CComObject<SessionEventSink>::CreateInstance(&m_sink)) && m_sink != nullptr)
{
m_sink->AddRef();
HRESULT hr = AtlAdvise(m_session, m_sink, __uuidof(ICOMCallEvents), &m_cookie);
if (FAILED(hr)) PRINT_ERROR("Advise Session Call event Error", hr);
}
}
}
~SessionSink()
{
if (m_session != nullptr)
{
HRESULT hr = AtlUnadvise(m_session, __uuidof(ICOMCallEvents), m_cookie);
if (FAILED(hr)) PRINT_ERROR("Unadvise Session Call event Error", hr);
}
if (m_sink != nullptr)
{
m_sink->Release();
}
}
private:
CComObject<SessionEventSink> *m_sink = nullptr;
ICOMSessionPtr m_session = nullptr;
DWORD m_cookie;
};
// SessionManagerSink() : user defined class : wrapper to Advise/Unadvise on Session Manager events
class SessionManagerSink
{
public:
SessionManagerSink(ICOMSessionManagerPtr sessionManager)
{
m_sessionManager = sessionManager;
CComObject<SessionManagerEventSink>::CreateInstance(&m_sink);
if (m_sessionManager != nullptr && m_sink != nullptr)
{
m_sink->AddRef();
HRESULT hr = AtlAdvise(m_sessionManager, m_sink, __uuidof(ICOMSessionManagerEvents), &m_cookie);
if (FAILED(hr)) PRINT_ERROR("Advise Session Manager events Error", hr);
}
}
~SessionManagerSink()
{
if (m_sessionManager != nullptr)
{
HRESULT hr = AtlUnadvise(m_sessionManager, __uuidof(ICOMSessionManagerEvents), m_cookie);
if (FAILED(hr)) PRINT_ERROR("Unadvise Session Manager events Error", hr);
}
if (m_sink != nullptr)
{
m_sink->Release();
}
}
private:
CComObject<SessionManagerEventSink> *m_sink = nullptr;
ICOMSessionManagerPtr m_sessionManager = nullptr;
DWORD m_cookie;
};
// DeviceSink() : user defined class : wrapper to Advise/Unadvise on Device events
class DeviceSink
{
public:
DeviceSink(ICOMSessionPtr session)
{
if (session != nullptr)
{
if (SUCCEEDED(session->GetActiveDevice(&m_device)) && m_device != nullptr)
{
// hook to device listener event
m_devListSink = new DeviceListenerSink(session);
}
}
}
~DeviceSink()
{
delete m_devListSink;
}
private:
DeviceListenerSink* m_devListSink = nullptr;
ICOMDevicePtr m_device = nullptr;
};
STDMETHODIMP SessionEventSink::onCallRequested(struct ICOMCallRequestEventArgs *requestArgs)
{
std::string contactName;
ICOMContact* callContact = nullptr;
requestArgs->get_contact(&callContact);
if (callContact != nullptr)
{
BSTR name;
callContact->get_Name(&name);
contactName = (name != nullptr ? _bstr_t(name) : "none");
}
std::cout << std::endl << "(Session) SessionEventSink onCallRequested contact: " << contactName;
return S_OK;
}
STDMETHODIMP SessionEventSink::onCallStateChanged(struct ICOMCallEventArgs *callEventArgs)
{
CallState callState;
callEventArgs->get_CallState(&callState);
long callId(0);
ICOMCall* call = nullptr;
callEventArgs->get_call(&call);
call->get_Id(&callId);
std::cout << "Call State Changed: callid=" << callId << " new state=" << EnumToString(callState) << std::endl;
return S_OK;
}
STDMETHODIMP SessionManagerEventSink::onDeviceStateChanged(struct ICOMStateDeviceEventArgs *args)
{
DeviceChangeState devState;
args->get_DeviceState(&devState);
std::cout << "onDeviceStateChanged: " << EnumToString(devState) << std::endl;
gGotDevice = false; // schedule a device re-attach attempt next time around the main menu loop
if (devState == DeviceState_Added)
{
std::cout << "Press Enter to attach this device." << std::endl;
}
return S_OK;
}
STDMETHODIMP SessionManagerEventSink::onCallStateChanged(struct ICOMCallEventArgs *callEventArgs) { return S_OK; }
STDMETHODIMP DeviceListenerEventSink::onHeadsetStateChanged(struct ICOMDeviceListenerEventArgs *args)
{
DeviceHeadsetStateChange state;
COMDeviceEventType evType;
args->get_HeadsetStateChange(&state);
args->get_DeviceEventType(&evType);
if (evType == COMDeviceEventType::DeviceEventType_HeadsetStateChanged)
{
std::cout << EnumToString(evType) << ", state = " << EnumToString(state) << std::endl;
}
return S_OK;
}
STDMETHODIMP DeviceListenerEventSink::onHeadsetButtonPressed(struct ICOMDeviceListenerEventArgs *args)
{
DeviceHeadsetButton btn;
COMDeviceEventType evType;
args->get_HeadsetButton(&btn);
args->get_DeviceEventType(&evType);
if ((evType == COMDeviceEventType::DeviceEventType_HeadsetButtonPressed) && (btn == DeviceHeadsetButton::HeadsetButton_Mute))
{
ICOMDevicePtr device;
ICOMDeviceListenerPtr deviceListener;
VARIANT_BOOL valBool;
session->GetActiveDevice(&device);
device->get_DeviceListener(&deviceListener);
deviceListener->get_mute(&valBool);
std::cout << "DeviceEventType_HeadsetStateChanged - ";
(valBool == VARIANT_TRUE) ? std::cout << EnumToString(DeviceHeadsetStateChange::HeadsetStateChange_MuteON) : std::cout << EnumToString(DeviceHeadsetStateChange::HeadsetStateChange_MuteOFF); std::cout << std::endl;
}
return S_OK;
}
STDMETHODIMP DeviceListenerEventSink::onBaseButtonPressed(struct ICOMDeviceListenerEventArgs *args) { return S_OK; }
STDMETHODIMP DeviceListenerEventSink::onBaseStateChanged(struct ICOMDeviceListenerEventArgs *args) { return S_OK; }
STDMETHODIMP DeviceListenerEventSink::onATDStateChanged(struct ICOMDeviceListenerEventArgs *args) { return S_OK; }
// Implementation of C++ COM sample
DeviceSink* gDeviceSink = nullptr;
// helper function that:
// - gets the handle to the currently active device,
// - gets a handle to deviceListner object
// - prints its ProductName, ProductID & mute status of the device
void PrintDeviceDetails()
{
HRESULT res = S_OK;
BSTR productName;
USHORT productId;
VARIANT_BOOL valBool;
ICOMDevicePtr myDevice;
ICOMDeviceListenerPtr myDeviceListener;
// get current active device from active session
if (FAILED(res = session->GetActiveDevice(&myDevice)))
{
std::cout << "Unable to retrieve/hook to active device" << std::endl;
return;
}
// get device listener interface
if (FAILED(res = myDevice->get_DeviceListener(&myDeviceListener)))
{
std::cout << "Device is not attached";
return;
}
std::cout << "Successfully hooked to device listener events" << std::endl;
// get & display device information
myDevice->get_ProductName(&productName);
myDevice->get_ProductId(&productId);
_bstr_t bproductName(productName);
std::cout << "Device attached: " << bproductName;
std::wcout << L", Product ID = " << std::uppercase << std::hex << std::setfill(L'0') << std::setw(4) << productId << std::endl;
// Obtain initial device microphone mute state
res = myDeviceListener->get_mute(&valBool);
std::cout << "Device mute state: muted = ";
(valBool == VARIANT_TRUE) ? std::cout << "True" : std::cout << "False"; std::cout << std::endl;
return;
}
void ShowMenu()
{
std::cout << std::endl;
std::cout << "plt sample menu" << std::endl;
std::cout << "--" << std::endl;
std::cout << "1 - ring/incoming call" << std::endl;
std::cout << "2 - outgoing call" << std::endl;
std::cout << "3 - answer call" << std::endl;
std::cout << "4 - hold call" << std::endl;
std::cout << "5 - resume call" << std::endl;
std::cout << "6 - mute call" << std::endl;
std::cout << "7 - unmute call" << std::endl;
std::cout << "8 - end call" << std::endl;
std::cout << "0 - quit" << std::endl;
std::cout << std::endl;
std::cout << "> ";
}
void DoIncomingCall(int callid, string contactname)
{
std::cout << "Performing IncomingCall, id = " << callid << std::endl;
HRESULT res = S_OK;
ICOMCallCommandPtr callCommand;
if (FAILED(res = session->GetCallCommand(&callCommand)))
{
PRINT_ERROR("Error gettting interface from Call Command", res); return;
}
res = callCommand->IncomingCall(CallObject::GetCallObject(callid), CallContact::GetContactObject(contactname), RingTone_Unknown, AudioRoute_ToHeadset);
}
void DoOutgoingCall(int callid, string contactname)
{
std::cout << "Performing OutgoingCall, id = " << callid << std::endl;
HRESULT res = S_OK;
ICOMCallCommandPtr callCommand;
if (FAILED(res = session->GetCallCommand(&callCommand)))
{
PRINT_ERROR("Error gettting interface from Call Command", res); return;
}
res = callCommand->OutgoingCall(CallObject::GetCallObject(callid), CallContact::GetContactObject(contactname), AudioRoute_ToHeadset);
}
void DoAnswerCall(int callid)
{
std::cout << "Performing AnswerCall, id = " << callid << std::endl;
HRESULT res = S_OK;
ICOMCallCommandPtr callCommand;
if (FAILED(res = session->GetCallCommand(&callCommand)))
{
PRINT_ERROR("Error gettting interface from Call Command", res); return;
}
res = callCommand->AnsweredCall(CallObject::GetCallObject(callid));
}
void DoTerminateCall(int callid)
{
std::cout << "Performing TerminateCall, id = " << callid << std::endl;
HRESULT res = S_OK;
ICOMCallCommandPtr callCommand;
if (FAILED(res = session->GetCallCommand(&callCommand)))
{
PRINT_ERROR("Error gettting interface from Call Command", res); return;
}
res = callCommand->TerminateCall(CallObject::GetCallObject(callid));
}
void DoHoldCall(int callid)
{
std::cout << "Performing HoldCall, id = " << callid << std::endl;
HRESULT res = S_OK;
ICOMCallCommandPtr callCommand;
if (FAILED(res = session->GetCallCommand(&callCommand)))
{
PRINT_ERROR("Error gettting interface from Call Command", res); return;
}
res = callCommand->HoldCall(CallObject::GetCallObject(callid));
}
void DoResumeCall(int callid)
{
std::cout << "Performing ResumeCall, id = " << callid << std::endl;
HRESULT res = S_OK;
ICOMCallCommandPtr callCommand;
if (FAILED(res = session->GetCallCommand(&callCommand)))
{
PRINT_ERROR("Error gettting interface from Call Command", res); return;
}
res = callCommand->ResumeCall(CallObject::GetCallObject(callid));
}
void DoMuteCall(int callid, bool mute)
{
std::cout << "Setting headset mute = ";
(mute == true) ? std::cout << "True" : std::cout << "False"; std::cout << std::endl;
HRESULT res = S_OK;
ICOMCallCommandPtr callCommand;
if (FAILED(res = session->GetCallCommand(&callCommand)))
{
PRINT_ERROR("Error gettting interface from Call Command", res); return;
}
(mute == true) ? res = callCommand->MuteCall(CallObject::GetCallObject(callid), VARIANT_TRUE) : res = callCommand->MuteCall(CallObject::GetCallObject(callid), VARIANT_FALSE);
}
// sample entry point
int _tmain(int argc, _TCHAR* argv[])
{
bool quit = false;
std::string input;
int cmd = -1;
int _callid = 0; // variable to track call id between my app and Plantronics
// Initialize COM
{
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
std::cout << "C++ Plantronics COM API Sample" << std::endl;
// Create Spokes session manager object
if (SUCCEEDED(sessionManager.CreateInstance(CLSID_COMSessionManager)))
{
// register Session Manager for device attached/detached events
SessionManagerSink sessionManagerSink(sessionManager);
// Register new session in Spokes.
if (SUCCEEDED(sessionManager->Register(_bstr_t("COM Plugin"), &session)))
{
// register Session for call state change events
SessionSink sessionSink(session);
ICOMUserPreference *userPref;
sessionManager->get_UserPreference(&userPref);
userPref->put_DefaultSoftphone(_bstr_t("COM Plugin"));
while (!quit)
{
// If we have not attached to device (or device was removed/added), then attempt to attach to a device
if (!gGotDevice)
{
if (gDeviceSink != nullptr)
{
delete gDeviceSink;
}
std::cout << "Attempting device attach" << std::endl;
// Instantiate Device Sink object and hook to DeviceListner events
gDeviceSink = new DeviceSink(session);
// call to AttachDevice()
PrintDeviceDetails();
}
ShowMenu();
std::getline(std::cin, input);
// Convert from string to number safely.
std::stringstream inputStringStream(input);
if (!(inputStringStream >> cmd))
{
cmd = -1;
// check if device is attached and prompt error if it is
if (gGotDevice == true)
std::cout << "Invalid input, please try again" << std::endl;
}
switch (cmd)
{
case 1:
_callid++;
DoIncomingCall(_callid, "Bob%20Smith"); // inform Plantronics my app has an incoming (ringing) call
break;
case 2:
_callid++;
DoOutgoingCall(_callid, "Bob%20Smith"); // inform Plantronics my app has an outgoing call
break;
case 3:
DoAnswerCall(_callid); // inform Plantronics my app has now answered an incoming (ringing) call
break;
case 4:
DoHoldCall(_callid); // place call on hold
break;
case 5:
DoResumeCall(_callid); // resume the call
break;
case 6:
DoMuteCall(_callid, true); // mute the headset (note for wireless products, audio link must be active)
break;
case 7:
DoMuteCall(_callid, false); // unmute the headset (note for wireless products, audio link must be active)
break;
case 8:
DoTerminateCall(_callid); // inform Plantronics my app has now terminated the call
break;
case 0:
quit = true;
break;
default:
if (gGotDevice == true)
std::cout << "Unrecognised menu choice, please try again." << std::endl;
break;
}
Sleep(250);
}
// cleanup unregister session
if (gDeviceSink != nullptr)
{
delete gDeviceSink;
}
sessionManager->Unregister(session);
}
}
}
// uninitialize COM
::CoUninitialize();
return 0;
}
// PlantronicsRESTDemo.java : Java sample demonstrating basic call control.
package plantronicsrestdemo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
// Pre-requisite, install Plantronics Hub from: http://www.plantronics.com/software
public class PlantronicsRESTDemo {
static Boolean quit = false;
static String sessionid = "";
static Boolean pluginRegistered = false;
static int callid = 0;
private static EventsListenerThread eventslistener = null;
private static String BaseURL = "http://127.0.0.1:32017/";
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Java Plantronics REST API Sample");
DoAttachDevice(); // Connect to the Plantronics REST API
DoShowDeviceInfo(); // get info about attached device (if any)
// start polling the API for device/call events or to reconnect to API (in the event of connection failure)...
DoStartEventsListener(sessionid);
while (!quit)
{
ShowMenu();
String cmd = ReadCommand();
switch (cmd)
{
case "1":
DoIncomingCall(++callid, "Bob%20Smith"); // inform Plantronics my app has an incoming (ringing) call
break;
case "2":
DoOutgoingCall(++callid, "Bob%20Smith"); // inform Plantronics my app has an outgoing call
break;
case "3":
DoAnswerCall(callid); // inform Plantronics my app has now answered an incoming (ringing) call
break;
case "4":
DoHoldCall(callid); // place call on hold
break;
case "5":
DoResumeCall(callid); // resume the call
break;
case "6":
DoMute(true); // mute the headset (note for legacy wireless products, audio link must be active)
break;
case "7":
DoMute(false); // unmute the headset (note for legacy wireless products, audio link must be active)
break;
case "8":
DoTerminateCall(callid); // inform Plantronics my app has now terminated the call
break;
case "0":
quit = true;
break;
default:
System.out.println("Unrecognised menu choice.");
break;
}
}
DoStopEventsListener(); // stop events polling
DoReleaseDevice(); // Cleanup the Plantronics REST API
}
private static void ShowMenu() {
System.out.println();
System.out.println("plt sample menu");
System.out.println("--");
System.out.println("1 - ring/incoming call");
System.out.println("2 - outgoing call");
System.out.println("3 - answer call");
System.out.println("4 - hold call");
System.out.println("5 - resume call");
System.out.println("6 - mute call");
System.out.println("7 - unmute call");
System.out.println("8 - end call");
System.out.println("0 - quit");
System.out.println();
System.out.print("Type a command> ");
}
private static String ReadCommand() {
BufferedReader buffer=new BufferedReader(new InputStreamReader(System.in));
String line = "";
try {
line = buffer.readLine();
} catch (IOException ex) {
Logger.getLogger(PlantronicsRESTDemo.class.getName()).log(Level.SEVERE, null, ex);
}
return line;
}
public static void DoShowDeviceInfo() {
// get info about attached device (if any)
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/DeviceServices/Info"
);
}
// Connect to the Plantronics REST API
public static void DoAttachDevice() {
// Connect to the Plantronics REST API (attach device session)
String tmpResult =
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/DeviceServices/Attach?uid=0123456789"
);
int pos = tmpResult.indexOf("\"Result\":\"");
if (pos>-1)
{
tmpResult = tmpResult.substring(pos+10);
pos = tmpResult.indexOf("\"");
if (pos>-1)
{
tmpResult = tmpResult.substring(0, pos);
System.out.println("Session id is: " + tmpResult);
sessionid = tmpResult;
}
try
{
Thread.sleep(250);
}
catch (InterruptedException ex)
{
Logger.getLogger(PlantronicsRESTDemo.class.getName()).log(Level.SEVERE, null, ex);
}
if (sessionid.length()>0)
{
// Register Spokes plugin (Plantronics API application session)
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/SessionManager/Register?name=My%20Java%20Plugin"
);
pluginRegistered = true;
}
}
else
{
System.out.println("Error: Connecting to Device failed, no Result/session detected in response");
}
}
// Cleanup the Plantronics REST API
public static void DoReleaseDevice() {
// Unregister Spokes plugin (Plantronics API application session)
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/SessionManager/UnRegister?name=My%20Java%20Plugin"
);
pluginRegistered = false;
try
{
Thread.sleep(250);
}
catch (InterruptedException ex)
{
Logger.getLogger(PlantronicsRESTDemo.class.getName()).log(Level.SEVERE, null, ex);
}
if (sessionid.length()>0)
{
// Disconnect from the Plantronics REST API (release device session)
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/DeviceServices/Release?sess="
+ sessionid
);
sessionid = "";
}
}
private static void DoIncomingCall(int callid, String caller_name) {
if (sessionid.length()>0 && pluginRegistered)
{
// inform Plantronics my app has an incoming (ringing) call
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/IncomingCall?name=My%20Java%20Plugin&callID=%7B%22Id%22%3A%22"
+ callid
+ "%22%7D&contact=%7B%22Name%22%3A%22"
+ caller_name
+ "%22%7D&tones=Unknown&route=ToHeadset"
);
}
else
{
System.out.println("Error: You need to Attach device first");
}
}
private static void DoOutgoingCall(int callid, String caller_name) {
if (sessionid.length()>0 && pluginRegistered)
{
// inform Plantronics my app has an outgoing call
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/OutgoingCall?name=My%20Java%20Plugin&callID=%7B%22Id%22%3A%22"
+ callid
+ "%22%7D&contact=%7B%22Name%22%3A%22"
+ caller_name
+ "%22%7D&route=ToHeadset"
);
}
else
{
System.out.println("Error: You need to Attach device first");
}
}
private static void DoTerminateCall(int callid) {
if (sessionid.length()>0 && pluginRegistered)
{
// inform Plantronics my app has now terminated the call
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/TerminateCall?name=My%20Java%20Plugin&callID=%7B%22Id%22%3A%22"
+ callid
+ "%22%7D"
);
}
else
{
System.out.println("Error: You need to Attach device first");
}
}
private static void DoAnswerCall(int callid) {
if (sessionid.length()>0 && pluginRegistered)
{
// inform Plantronics my app has now answered an incoming (ringing) call
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/AnswerCall?name=My%20Java%20Plugin&callID=%7B%22Id%22%3A%22"
+ callid
+ "%22%7D"
);
}
else
{
System.out.println("Error: You need to Attach device");
}
}
private static void DoHoldCall(int callid) {
if (sessionid.length()>0 && pluginRegistered)
{
// place call on hold
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/HoldCall?name=My%20Java%20Plugin&callID=%7B%22Id%22%3A%22"
+ callid
+ "%22%7D"
);
}
else
{
System.out.println("Error: You need to Attach device");
}
}
private static void DoResumeCall(int callid) {
if (sessionid.length()>0 && pluginRegistered)
{
// resume the call
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/ResumeCall?name=My%20Java%20Plugin&callID=%7B%22Id%22%3A%22"
+ callid
+ "%22%7D"
);
}
else
{
System.out.println("Error: You need to Attach device");
}
}
private static void DoMute(boolean muted) {
if (sessionid.length()>0 && pluginRegistered)
{
// mute/un-mute the headset (note for legacy wireless products, audio link must be active)
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/MuteCall?name=My%20Java%20Plugin&muted="
+ muted
);
}
else
{
System.out.println("Error: You need to Attach device first");
}
}
private static void DoStartEventsListener(String sessid) {
if (eventslistener==null)
{
eventslistener = new EventsListenerThread(sessid, "My%20Java%20Plugin");
eventslistener.start();
}
}
private static void DoStopEventsListener() {
if (eventslistener!=null)
{
eventslistener.shutdown();
eventslistener = null;
}
}
private static void DoGetCallManagerState() {
if (sessionid.length()>0 && pluginRegistered)
{
// query current Plantronics API call manager state
RESTConvenienceClass.SendRESTCommand(
BaseURL + "Spokes/CallServices/CallManagerState"
);
}
else
{
System.out.println("Error: You need to Attach device first");
}
}
}
class EventsListenerThread extends Thread
{
private Boolean quit = false;
private Boolean deviceAttached = true;
private String sessid = "";
private String plugin_name;
private static String BaseURL = "http://127.0.0.1:32017/";
//private static String BaseURL = "https://127.0.0.1:32018/";
public EventsListenerThread(String sessid, String plugin_name)
{
this.sessid = sessid;
this.plugin_name = plugin_name;
if (sessid.length()==0)
{
deviceAttached = false;
}
}
public void run ()
{
System.out.println("Events Listener Starting");
while (!quit) {
Sleep(1800);
if (!quit)
{
System.out.println();
System.out.println();
if (!deviceAttached)
{
System.out.println("-- POLLING FOR DEVICE RE-ATTACH --");
PlantronicsRESTDemo.DoAttachDevice();
if (PlantronicsRESTDemo.sessionid.length()>0)
{
sessid = PlantronicsRESTDemo.sessionid;
deviceAttached = true;
PlantronicsRESTDemo.DoShowDeviceInfo();
}
}
else
{
System.out.println("-- POLLING FOR EVENTS --");
// deviceEvents informs us of a variety of Plantronics device state changes
// SPECIAL CASE device removal - with the REST API, when device is removed
// polling for device events will return error Error of type Invalid session id
String deviceEvents = RESTConvenienceClass.SendRESTCommandWithDebugPrompt(
BaseURL + "Spokes/DeviceServices/Events?sess="
+ sessid
+ "&queue=0"
, "DEVICE EVENTS", true
);
if(deviceEvents.contains("Invalid session id") || deviceEvents.contains("Empty session id"))
{
System.out.println("-- ** DEVICE DETACHED / SESSION INVALID ** --");
deviceAttached = false;
PlantronicsRESTDemo.sessionid = "";
PlantronicsRESTDemo.DoReleaseDevice();
}
Sleep(200);
if (!quit && deviceAttached)
{
// session Call Events informs us the calling state has changed, for example user as answered/terminated a call
// using headset buttons - this event should be used in my app to actually connect/terminate the call!
String callEvents = RESTConvenienceClass.SendRESTCommandWithDebugPrompt(
BaseURL + "Spokes/CallServices/CallEvents?name=My%20Java%20Plugin"
, "CALL EVENTS", true
);
ParseCallEvents(callEvents);
}
}
}
}
System.out.println("Events Listener Stopping");
}
private void ParseCallEvents(String callEvents)
{
if (callEvents.contains("[") || callEvents.contains("\"Action\":"))
{
callEvents = callEvents.substring(callEvents.indexOf("["));
callEvents = callEvents.substring(0, callEvents.indexOf("]"));
List<String> actions = Arrays.asList(callEvents.split("\"Action\":"));
String callId;
for(Iterator<String> i = actions.iterator(); i.hasNext(); ) {
String item = i.next();
int pos = item.indexOf(",");
if (pos>-1)
{
callId = item.substring(item.indexOf("\"Id\":") + 5);
callId = callId.substring(0, callId.indexOf(","));
System.out.println("CallState: " + RESTConvenienceClass.eCallState.get(item.substring(0, pos)) +
", " + "Call ID: " + callId);
}
}
}
}
public void shutdown()
{
quit = true;
}
private void Sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ex) {
Logger.getLogger(EventsListenerThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
class RESTConvenienceClass {
public static String SendRESTCommand(String restcommand) {
return SendRESTCommandWithDebugPrompt(restcommand, "RESULT", true);
}
public static String SendRESTCommandWithDebugPrompt(String restcommand, String debugprompt, Boolean showsent) {
String result = "";
if (showsent) System.out.println("Sending REST Command: " + restcommand);
try {
result = getHTML(restcommand);
System.out.println(debugprompt + " = " + result);
} catch (Exception ex) {
Logger.getLogger(PlantronicsRESTDemo.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
// thanks: http://stackoverflow.com/questions/1485708/how-do-i-do-a-http-get-in-java
public static String getHTML(String urlToRead) throws Exception {
StringBuilder result = new StringBuilder();
URL url = new URL(urlToRead);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
result.append(line);
}
rd.close();
return result.toString();
}
public static Map<String, String> eCallState = new HashMap<String, String>() {{
put("0", "CALL_STATE_UNKNOWN");
put("1", "CALL_STATE_ACCEPT_CALL");
put("2", "CALL_STATE_TERMINATE_CALL");
put("3", "CALL_STATE_HOLD_CALL");
put("4", "CALL_STATE_RESUME_CALL");
put("5", "CALL_STATE_FLASH");
put("6", "CALL_STATE_CALL_IN_PROGRESS");
put("7", "CALL_STATE_CALL_RINGING");
put("8", "CALL_STATE_CALL_ENDED");
put("9", "CALL_STATE_TRANSFER_TO_HEADSET");
put("10", "CALL_STATE_TRANSFER_TO_SPEAKER");
put("11", "CALL_STATE_MUTEON");
put("12", "CALL_STATE_MUTEOFF");
put("13", "CALL_STATE_MOBILE_CALL_RINGING");
put("14", "CALL_STATE_MOBILE_CALL_IN_PROGRESS");
put("15", "CALL_STATE_MOBILE_CALL_ENDED");
put("16", "CALL_STATE_DON");
put("17", "CALL_STATE_DOFF");
put("18", "CALL_STATE_CALL_IDLE");
put("19", "CALL_STATE_PLAY");
put("20", "CALL_STATE_PAUSE");
put("21", "CALL_STATE_STOP");
put("22", "CALL_STATE_DTMF_KEY");
put("23", "CALL_STATE_REJECT_CALL");
put("24", "CALL_STATE_MAKE_CALL");
put("25", "CALL_STATE_HOOK");
put("26", "CALL_STATE_HOOK_IDLE");
put("27", "CALL_STATE_HOOK_DOCKED");
put("28", "CALL_STATE_HOOK_UNDOCKED");
put("29", "CALL_STATE_BASE_EVENT");
put("30", "CALL_STATE_CALL_ANSWERED_AND_ENDED");
put("31", "CALL_STATE_CALL_UNANSWERED_AND_ENDED");
put("32", "CALL_STATE_DEVICE_CHANGE");
put("33", "CALL_STATE_DEVICE_ARRIVED");
put("34", "CALL_STATE_DEVICE_REMOVED");
}};
}
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<!-- Plantronics JavaScript Sample.html : JavaScript sample demonstrating basic call control.
Try a *live* demo now! Visit: https://pltdev.github.io/
NOTE:
This sample has a pre-requisite on spokes.js (part of the Plantronics SDK located
here: C:\Program Files (x86)\Plantronics\Spokes3G SDK\Samples / https://pltdev.github.io/spokes.js)
And jQuery version 1.7.2 (https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js)
And a convenience function "print_r" for debug (located: https://pltdev.github.io/util.js) -->
<head>
<meta charset="utf-8" />
<title>Plantronics JavaScript Sample</title>
</head>
<!-- Connect / disconnect with Plantronics REST API -->
<body onload="connectToPlantronics()" onbeforeunload="disconnectFromPlantronics()">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://pltdev.github.io/spokes.js"></script> <!-- Plantronics API interface -->
<script src="https://pltdev.github.io/util.js"></script>
<!-- Main page -->
<h1>Plantronics JavaScript Sample</h1>
Pre-requisite, install Plantronics Hub from: <a href="http://www.plantronics.com/software" target="_new">www.plantronics.com/software</a>
<p>Note Firefox users: If you get "Error connecting to Plantronics Hub." then visit this URL: <a href="https://127.0.0.1:32018/Spokes/DeviceServices/Info" target="_new">https://127.0.0.1:32018/Spokes/DeviceServices/Info</a> and click Advanced > Add Exception... to add a security exception to allow the connection.</p>
<p>
<b>plt sample menu<br />
--</b><br />
<button id="incoming_call">ring/incoming call</button><br />
<button id="outgoing_call">outgoing call</button><br />
<button id="answer_call">answer call</button><br />
<button id="hold_call">hold call</button><br />
<button id="resume_call">resume call</button><br />
<button id="mute_call">mute call</button><br />
<button id="unmute_call">unmute call</button><br />
<button id="end_call">end call</button>
</p>
<button id="clear_log">Clear Log</button>
<div id="log"><b>Log:</b></div>
<script type="text/javascript">
var spokes; // global Spokes object, initialised when this page is loaded (onload)
var plugin_registered = false;
var plugin_name = "Hello World JavaScript Plugin";
var callid = 0;
var deviceAttached = true;
var pollRate = 2000;
var previousPollRate = 2000;
// start polling the API for device events to to reconnect to API (in the event of connection failure)...
var run = setInterval(pollDeviceEventsOrReconnect, pollRate);
// Sample menu command implementations:
$('#clear_log').click(function () {
log.innerHTML = "<b>Log:</b>";
});
// inform Plantronics my app has an incoming (ringing) call
$('#incoming_call').click(function () {
callid = callid + 1;
call_id = callid;
appendLog("<font color=\"#00FF00\">Initiating make call command, call id = " + callid.toString() + "</font>");
spokes.Plugin.incomingCall(plugin_name, getCallId(callid), getContact("Dummy Contact"), "Unknown", "ToHeadset", function (result) {
showCallStatus(result);
});
});
// inform Plantronics my app has an outgoing call
$('#outgoing_call').click(function () {
callid = callid + 1;
call_id = callid;
appendLog("<font color=\"#00FF00\">Initiating make call command, call id = " + callid.toString() + "</font>");
spokes.Plugin.outgoingCall(plugin_name, getCallId(callid), getContact("Dummy Contact"), "ToHeadset", function (result) {
showCallStatus(result);
});
});
// inform Plantronics my app has now answered an incoming (ringing) call
$('#answer_call').click(function () {
appendLog("<font color=\"#00FF00\">Answering call command, call id = " + callid.toString() + "</font>");
spokes.Plugin.answerCall(plugin_name, getCallId(callid), function (result) {
showCallStatus(result);
});
});
// place call on hold
$('#hold_call').click(function () {
appendLog("<font color=\"#00FF00\">Hold call command, call id = " + callid.toString() + "</font>");
spokes.Plugin.holdCall(plugin_name, getCallId(callid), function (result) {
showCallStatus(result);
});
});
// resume the call
$('#resume_call').click(function () {
appendLog("<font color=\"#00FF00\">Resume call command, call id = " + callid.toString() + "</font>");
spokes.Plugin.resumeCall(plugin_name, getCallId(callid), function (result) {
showCallStatus(result);
});
});
// mute the headset (note for legacy wireless products, audio link must be active)
$('#mute_call').click(function () {
appendLog("<font color=\"#00FF00\">Muting call</font>");
spokes.Plugin.muteCall(plugin_name, true, function (result) {
showCallStatus(result);
});
});
// unmute the headset (note for legacy wireless products, audio link must be active)
$('#unmute_call').click(function () {
appendLog("<font color=\"#00FF00\">Unmuting call</font>");
spokes.Plugin.muteCall(plugin_name, false, function (result) {
showCallStatus(result);
});
});
// inform Plantronics my app has now terminated the call
$('#end_call').click(function () {
appendLog("<font color=\"#00FF00\">Ending call, call id = " + callid.toString() + "</font>");
spokes.Plugin.terminateCall(plugin_name, getCallId(callid), function (result) {
showCallStatus(result);
});
});
// Convenience functions to create call and contact structures used for IncomingCall/OutgoingCall functions
function getCallId(callid) {
return new SpokesCallId({ Id: '' + callid } );
}
function getContact(contactname) {
return new SpokesContact({ Name: contactname });
}
// displays status of call commands
function showCallStatus(result, toJson) {
if (result.isError) {
appendLog("Error: " + result.Err.Description);
} else {
if (toJson) appendLog(JSON.stringify(result.Result))
else appendLog("Success.");
}
};
function appendLog(str) {
log.innerHTML = log.innerHTML + "<br />" + str;
}
function connectToPlantronics() {
// Connect to the Plantronics REST API
spokes = new Spokes("https://127.0.0.1:32018/Spokes");
// get info about attached device (if any)
spokes.Device.deviceInfo(function (result) {
if (!result.isError) {
if (result.Result /*[0]*/ != null) {
// Log Spokes active device found (first in list returned, index 0)
appendLog("Device found = " + result.Result /*[0]*/.ProductName + ", id = " + result.Result /*[0]*/.Uid);
deviceAttached = true;
// attach to the device, provide a callback function for the result
spokes.Device.attach(result.Result /*[0]*/.Uid, deviceAttachedCallback);
} else {
appendLog("Error: Device was null on connecting to Spokes. Is there a Plantronics device connected?");
deviceAttached = false;
}
pollRate = 2000; // waiting for device events now, force faster polling rate to start now (if applicable)
if (previousPollRate == 10000) {
var previousPollRate = 2000;
// start polling the device and call state events...
var run = setInterval(pollDeviceEventsOrReconnect, pollRate);
}
} else {
if (result.Err.Description === "There are no supported devices") {
appendLog("Please attach a Plantronics headset to the PC.");
}
else
{
appendLog("Error connecting to Plantronics Hub. (Have you installed and run Plantronics Hub from <a href=\"http://www.plantronics.com/software\" target=\"_new\">www.plantronics.com/software</a>, or " +
"are you Firefox user and getting \"Error connecting to Plantronics Hub.\"? If so visit this URL: <a href=\"https://127.0.0.1:32018/Spokes/DeviceServices/Info\" target=\"_new\">" +
"https://127.0.0.1:32018/Spokes/DeviceServices/Info</a> and click Advanced > Add Exception... to add a security exception to allow the connection.");
pollRate = 10000; // slow down polling rate while we are waiting for Hub to be running
}
}
});
}
//Callback to receive result of device attach. If successful register a plugin (Plantronics API application session)
function deviceAttachedCallback(session) {
if (session.isError || !spokes.Device.isAttached) {
appendLog("Session Registration Error");
deviceAttached = false;
disconnectFromPlantronics();
} else {
appendLog("Session ID: " + session.Result);
registerPlugin(); // register a plugin (Plantronics API application session)
}
}
function setPluginActive()
{
//Set plugin active status to true
spokes.Plugin.isActive(plugin_name, true, function (result) {
if (!result.isError) {
// plugin registered and active. Show UI.
plugin_registered = true;
appendLog("Plugin \"" + plugin_name + "\" registered successfully.");
} else {
appendLog("Error checking if plugin is active: " + result.Err.Description);
}
});
}
// Register a Spokes Plugin (Plantronics API application session) to get access to Call Services, Device and Call events
function registerPlugin() {
if (!plugin_registered) {
spokes.Plugin.register(plugin_name, function (result) {
if (!result.isError) {
setPluginActive();
} else {
appendLog("Info: registering plugin: " + result.Err.Description);
if (result.Err.Description === "Plugin exists")
{
setPluginActive();
}
else
{
deviceAttached = false;
disconnectFromPlantronics();
}
}
});
}
}
// Unregister Spokes plugin (Plantronics API application session)
function unregisterPlugin() {
spokes.Plugin.unRegister(plugin_name);
plugin_registered = false;
appendLog("Plugin un-registered.");
}
// Cleanup the Plantronics REST API
function disconnectFromPlantronics() {
unregisterPlugin();
spokes.Device.release(function (result) {
if (!result.isError) {
appendLog("Released device");
} else {
appendLog("Error releasing device");
}
appendLog("Disconnected from Spokes");
});
}
// Function to perform device and call event polling if we are connected to Hub, or else attempt to reconnect to Hub
function pollDeviceEventsOrReconnect() {
// supports variable poll rate, 2000ms waiting for a device, 10000ms waiting for Hub to be running
if (previousPollRate != pollRate) {
clearInterval(run);
previousPollRate = pollRate;
run = setInterval(pollDeviceEventsOrReconnect, pollRate);
}
if (spokes == null || !deviceAttached || !spokes.Device.isAttached) {
appendLog("-- POLLING FOR HUB / DEVICE RE-ATTACH --");
connectToPlantronics();
return;
}
// Poll for device events
// informs us of a variety of Plantronics device state changes
spokes.Device.events(
function (result) {
if (result.isError) {
appendLog("Error polling for device events: " + result.Err.Description);
if (result.Err.Description === "No response. Server appears to be offline.") {
pollRate = 10000;
appendLog("changing POLL RATE to " + pollRate);
}
if (result.Err.Description === "Invalid session id" ||
result.Err.Description === "Empty session id" ||
result.Err.Description === "No response. Server appears to be offline.") {
appendLog("-- ** DEVICE DETACHED / SESSION INVALID ** --");
deviceAttached = false;
disconnectFromPlantronics();
}
} else {
// display list of events collected from REST service
if (result.Result.length > 0) {
for (var i = 0; i < result.Result.length; i++) {
appendLog("<font color=\"#0000FF\">Device Event: " + result.Result[i].Event_Log_Type_Name + ", " + print_r(result.Result[i]) + "</font>");
}
}
}
});
// Poll for call state events (call control events)
// informs us the calling state has changed, for example user as answered/terminated a call
// using headset buttons - this event should be used in my app to actually connect/terminate the call!
spokes.Plugin.callEvents(plugin_name,
function (result) {
if (result.isError) {
appendLog("Error polling for call events: " + result.Err.Description);
if (result.Err.Description === "No response. Server appears to be offline.") {
pollRate = 10000;
appendLog("changing POLL RATE to " + pollRate);
}
if (result.Err.Description === "Invalid session id" ||
result.Err.Description === "Empty session id" ||
result.Err.Description === "No response. Server appears to be offline.") {
appendLog("-- ** DEVICE DETACHED / SESSION INVALID ** --");
deviceAttached = false;
disconnectFromPlantronics();
}
} else {
// display list of events collected from REST service
if (result.Result.length > 0) {
for (var i = 0; i < result.Result.length; i++) {
appendLog("<font color=\"#0000FF\">Call Event: " + result.Result[i].Event_Log_Type_Name + ", " + print_r(result.Result[i]) + "</font>");
// Workout the actual call state and call id in question
var callState = SessionCallState.Lookup[result.Result[i]["Action"]];
var callId = result.Result[i]["CallId"]["Id"];
appendLog("CallState: " + callState + ", Call ID: " + callId);
}
}
}
});
}
</script>
</body>
</html>
// PLTCPlusPlusNativeSample.cpp : C++ Sample demonstrating basic call control. This implementation is linked to Plantronics native Spokes library
// Note: With Plantronics SDK installed, ensure the following project configuration:
// 1. C/C++ -> General -> Additional Include direcories =>
// C:/Program Files (x86)/Plantronics/Spokes3G SDK/Include;C:/Program Files/Plantronics/Spokes3G SDK/Include
// 2. Linker -> General -> Additional Library Directories =>
// C:/Program Files (x86)/Plantronics/Spokes3G SDK;C:/Program Files/Plantronics/Spokes3G SDK
// 3. Linker -> Input -> Additional Dependancies =>
// %(AdditionalDependencies);Spokes.lib
// **NOTE**: Native Library does not support Manager Pro, Multi-softphone or Multi-device configurations
// Please consider REST Service (PC and Mac) or COM Service (PC only) APIs
// For more information see: http://developer.plantronics.com/api-overview#second
#pragma once
#include <windows.h>
#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
#include <stdio.h>
#include <tchar.h>
#include <SDKDDKVer.h>
#include "Spokes3G.h"
#include "cpp\query_cast.h"
using namespace std;
bool gGotDevice = false;
std::string EnumToString(eCallState val)
{
std::string str;
switch (val)
{
case CALL_STATE_UNKNOWN: str = "CALL_STATE_UNKNOWN "; break;
case CALL_STATE_ACCEPT_CALL: str = "CALL_STATE_ACCEPT_CALL "; break;
case CALL_STATE_TERMINATE_CALL: str = " CALL_STATE_TERMINATE_CALL"; break;
case CALL_STATE_HOLD_CALL: str = "CALL_STATE_HOLD_CALL "; break;
case CALL_STATE_RESUME_CALL: str = " CALL_STATE_RESUME_CALL "; break;
case CALL_STATE_FLASH: str = "CALL_STATE_FLASH "; break;
case CALL_STATE_CALL_IN_PROGRESS: str = "CALL_STATE_CALL_IN_PROGRESS "; break;
case CALL_STATE_CALL_RINGING: str = "CALL_STATE_CALL_RINGING "; break;
case CALL_STATE_CALL_ENDED: str = "CALL_STATE_CALL_ENDED "; break;
case CALL_STATE_TRANSFER_TO_HEADSET: str = " CALL_STATE_TRANSFER_TO_HEADSET "; break;
case CALL_STATE_TRANSFER_TO_SPEAKER: str = "CALL_STATE_TRANSFER_TO_SPEAKER "; break;
case CALL_STATE_MUTEON: str = "CALL_STATE_MUTEON "; break;
case CALL_STATE_MUTEOFF: str = "CALL_STATE_MUTEOFF "; break;
case CALL_STATE_MOBILE_CALL_RINGING: str = " CALL_STATE_MOBILE_CALL_RINGING "; break;
case CALL_STATE_MOBILE_CALL_IN_PROGRESS: str = "CALL_STATE_MOBILE_CALL_IN_PROGRESS "; break;
case CALL_STATE_MOBILE_CALL_ENDED: str = " CALL_STATE_MOBILE_CALL_ENDED"; break;
case CALL_STATE_DON: str = " CALL_STATE_DON "; break;
case CALL_STATE_DOFF: str = "CALL_STATE_DOFF "; break;
case CALL_STATE_CALL_IDLE: str = " CALL_STATE_CALL_IDLE"; break;
case CALL_STATE_PLAY: str = "CALL_STATE_PLAY "; break;
case CALL_STATE_PAUSE: str = " CALL_STATE_PAUSE"; break;
case CALL_STATE_STOP: str = "CALL_STATE_STOP "; break;
case CALL_STATE_DTMF_KEY: str = " CALL_STATE_DTMF_KEY "; break;
case CALL_STATE_REJECT_CALL: str = "CALL_STATE_REJECT_CALL "; break;
case CALL_STATE_MAKE_CALL: str = " CALL_STATE_MAKE_CALL "; break; // Information only.
case CALL_STATE_HOOK: str = " CALL_STATE_HOOK"; break;
case CALL_STATE_HOOK_IDLE: str = " CALL_STATE_HOOK_IDLE "; break;
case CALL_STATE_HOOK_DOCKED: str = " CALL_STATE_HOOK_DOCKED"; break;
case CALL_STATE_HOOK_UNDOCKED: str = " CALL_STATE_HOOK_UNDOCKED"; break;
case CALL_STATE_BASE_EVENT: str = "CALL_STATE_BASE_EVENT "; break;
case CALL_STATE_CALL_ANSWERED_AND_ENDED: str = "CALL_STATE_CALL_ANSWERED_AND_ENDED "; break;
case CALL_STATE_CALL_UNANSWERED_AND_ENDED: str = "CALL_STATE_CALL_UNANSWERED_AND_ENDED "; break;
case CALL_STATE_DEVICE_ARRIVED: str = "CALL_STATE_DEVICE_ARRIVED "; break;
case CALL_STATE_DEVICE_REMOVED: str = "CALL_STATE_DEVICE_REMOVED "; break;
case CALL_STATE_DEVICE_CHANGE: str = "CALL_STATE_DEVICE_CHANGE "; break;
default:return "CALL_STATE_UNKNOWN";
};
return str;
}
std::string EnumToString(eDeviceState val)
{
std::string str;
switch (val)
{
case DEV_STATE_ADDED: str = " DEV_STATE_ADDED"; break;
case DEV_STATE_REMOVED: str = " DEV_STATE_REMOVED"; break;
default: str = " DEV_STATE_UNKNOWN";
}
return str;
}
void PrintDeviceDetails(IDevice* device)
{
bool muted;
IDeviceListener* myDeviceListener = nullptr;
DMResult res = device->getDeviceListener(&myDeviceListener);
if (res != DMResult::DM_RESULT_SUCCESS)
{
std::cout << "Device is not attached / unable to hook up to device listener events" << std::endl;
return;
}
std::cout << "Successfully hooked to device listener events" << std::endl;
wchar_t buffer1[1024];
device->getProductName(buffer1, 1024);
int32_t pid = device->getProductID();
std::wcout << "Device attached: " << buffer1;
std::wcout << L", Product ID = " << std::uppercase << std::hex << std::setfill(L'0') << std::setw(4) << pid << std::endl;
// Obtain initial device microphone mute state
res = myDeviceListener->getMute(muted);
std::cout << "Device mute state: muted = ";
(muted == true) ? std::cout << "True" : std::cout << "False"; std::cout << std::endl;
return;
}
// Call Object
class Call : public ICall
{
private:
int32_t m_id = 0;
public:
virtual int32_t getID() { return m_id; }
virtual void setID(int32_t id) { m_id = id; }
virtual int32_t getConferenceID() { return 0; }
virtual void setConferenceID(int32_t id) { /*no op*/ }
virtual bool getInConference() { return false; }
virtual void setInConference(bool bConference) { /*no op*/ }
void operator delete(void *p) { ::operator delete(p); }
~Call() { }
};
ICall* GetCall(Call& call)
{
std::string strCallId;
std::cout << "Enter Call Id: ";
std::getline(std::cin, strCallId);
int32_t id;
std::stringstream myStream(strCallId);
myStream >> id;
call.setID(id);
return &call;
}
ICall* GetCall(Call& call, int id)
{
call.setID(id);
return &call;
}
// Contact Object
class Contact : public IContact
{
private:
int32_t m_id = 0;
wstring m_name;
wstring m_friendlyName;
wstring m_sipUri;
wstring m_email;
wstring m_phone;
wstring m_workPhone;
wstring m_mobilePhone;
wstring m_homePhone;
public:
virtual const wchar_t* getName() const { return m_name.c_str(); }
virtual void setName(const wchar_t* pName) { m_name = pName; }
virtual const wchar_t* getFriendlyName() const { return m_friendlyName.c_str(); }
virtual void setFriendlyName(const wchar_t* pFName) { m_friendlyName = pFName; }
virtual int32_t getID() const { return m_id; }
virtual void setID(int32_t id) { m_id = id; }
virtual const wchar_t* getSipUri() const { return m_sipUri.c_str(); }
virtual void setSipUri(const wchar_t* pSip) { m_sipUri = pSip; }
virtual const wchar_t* getPhone() const { return m_phone.c_str(); }
virtual void setPhone(const wchar_t* pPhone) { m_phone = pPhone; }
virtual const wchar_t* getEmail() const { return m_email.c_str(); }
virtual void setEmail(const wchar_t* pEmail) { m_email = pEmail; }
virtual const wchar_t* getWorkPhone() const { return m_workPhone.c_str(); }
virtual void setWorkPhone(const wchar_t* pWPhone) { m_workPhone = pWPhone; }
virtual const wchar_t* getMobilePhone() const { return m_mobilePhone.c_str(); }
virtual void setMobilePhone(const wchar_t* pMPhone) { m_mobilePhone = pMPhone; }
virtual const wchar_t* getHomePhone() const { return m_homePhone.c_str(); }
virtual void setHomePhone(const wchar_t* pHPhone) { m_homePhone = pHPhone; }
void operator delete(void *p) { ::operator delete(p); }
~Contact() { }
};
IContact* GetContact()
{
std::string strContactName;
std::cout << "Enter contact name: ";
std::getline(std::cin, strContactName);
wstring wName(strContactName.begin(), strContactName.end());
std::cout << "Name:" << strContactName << std::endl;
IContact* contact = nullptr;
// getContact( &contact );
// contact->setName( wName.c_str() );
return contact;
}
IContact* GetContact(Contact& contact, std::string name)
{
std::wstring wsTmp(name.begin(), name.end());
contact.setName(wsTmp.c_str());
return &contact;
}
Call call;
Contact contact;
// create sink for receiving session events
class CallEventSink : public ICallEvents
{
private:
ISession* m_session = nullptr;
public:
CallEventSink() {}
CallEventSink(ISession* session) : m_session(session) { m_session->registerCallback(this); }
virtual ~CallEventSink() { m_session->unregisterCallback(this); }
virtual bool OnCallStateChanged(CallStateEventArgs const& pcscArgs)
{
std::string state = EnumToString(pcscArgs.callState);
//cout << "OnCallStateChanged Session event " << state.c_str() << endl;
return true;
}
virtual bool OnCallRequest(CallRequestEventArgs const& pcscArgs) { cout << "OnCallRequest Session event" << endl; return true; }
void operator delete(void *p) { ::operator delete(p); }
};
// create sink for receiving session manager events
class SessionManagerEventSink : public ISessionManagerEvents
{
private:
ISessionManager* m_sessionManager = nullptr;
public:
SessionManagerEventSink() {}
SessionManagerEventSink(ISessionManager* sessionManager) : m_sessionManager(sessionManager) { m_sessionManager->registerCallback(this); }
virtual ~SessionManagerEventSink() { m_sessionManager->unregisterCallback(this); gGotDevice = false; }
virtual bool OnCallStateChanged(CallStateEventArgs const& cseArgs)
{
int32_t callId = call.getID();
std::string state = EnumToString(cseArgs.callState);
std::cout << "Call State Changed: callid=" << callId << " new state=" << state.c_str() << endl;
return true;
}
virtual bool OnDeviceStateChanged(DeviceStateEventArgs const& devArgs)
{
eDeviceState devState = devArgs.deviceState;
std::string state = EnumToString(devState);
cout << "OnDeviceStateChanged: " << state.c_str() << endl;
gGotDevice = false; // schedule a device re-attach attempt next time around the main menu loop
if (devState == eDeviceState::DEV_STATE_ADDED)
{
std::cout << "Press Enter to attach this device." << std::endl;
}
return true;
}
void operator delete(void *p) { ::operator delete(p); }
};
void ShowMenu()
{
std::cout << std::endl;
std::cout << "plt sample menu" << std::endl;
std::cout << "--" << std::endl;
std::cout << "1 - ring/incoming call" << std::endl;
std::cout << "2 - outgoing call" << std::endl;
std::cout << "3 - answer call" << std::endl;
std::cout << "4 - hold call" << std::endl;
std::cout << "5 - resume call" << std::endl;
std::cout << "6 - mute call" << std::endl;
std::cout << "7 - unmute call" << std::endl;
std::cout << "8 - end call" << std::endl;
std::cout << "0 - quit" << std::endl;
std::cout << std::endl;
std::cout << "> ";
}
int _tmain(int argc, _TCHAR* argv[])
{
bool quit = false;
std::string input;
int cmd = -1;
int _callid = 0; // variable to track call id between my app and Plantronics
InitSpokesRuntime();
cout << "C++ Plantronics Native API Sample" << std::endl;
// create session manager
ISessionManager *sessionManager = nullptr;
if (SM_RESULT_SUCCESS == getSessionManager(&sessionManager))
{
// create session
ISession* session = nullptr;
if (SM_RESULT_SUCCESS == sessionManager->registerSession(L"Sample client", &session))
{
IDevice* activeDevice = nullptr;
SMResult res = session->getActiveDevice(&activeDevice); // always succeeds (since getActiveDevice() gets a proxy & retruns SM_RESULT_SUCCESS)
IUserPreference* userPreference = nullptr;
if (SM_RESULT_SUCCESS == sessionManager->getUserPreference(&userPreference))
{
// set this session as default one for receiving CallRequest events
int32_t pluginId;
session->getPluginId(&pluginId);
userPreference->setDefaultSoftphone(pluginId);
}
// get call command
ICallCommand* callCommand = nullptr;
if (SM_RESULT_SUCCESS == session->getCallCommand(&callCommand))
{
// sink to all call events
unique_ptr<CallEventSink> callEvents(new CallEventSink(session)); //auto callEvents = std::make_unique<CallEventSink>(session);
unique_ptr<SessionManagerEventSink> sessionManagerEvents(new SessionManagerEventSink(sessionManager)); //auto sessionManagerEvents = std::make_unique<SessionManagerEventSink>(sessionManager);
// enter in command loop
while (!quit)
{
// If we have not attached to device (or device was removed/added), then attempt to attach to a device
if (!gGotDevice)
{
std::cout << "Attempting device attach" << std::endl;
if (true == activeDevice->isAttached())
{
gGotDevice = true;
PrintDeviceDetails(activeDevice);
}
else
{
std::cout << "Unable to retrieve/hook to active device" << std::endl;
}
}
ShowMenu();
std::getline(std::cin, input);
// Convert from string to number safely.
std::stringstream inputStringStream(input);
if (!(inputStringStream >> cmd))
{
cmd = -1;
// check if device is attached and prompt error if it is
if (gGotDevice == true)
std::cout << "Invalid input, please try again" << std::endl;
}
switch (cmd)
{
case 1:
_callid++;
callCommand->incomingCall(GetCall(call, _callid), // inform Plantronics my app has an incoming (ringing) call
GetContact(contact, "Bob%20Smith"), RING_TONE_UNKNOWN, eAudioRoute::AUDIO_ROUTE_TO_HEADSET);
break;
case 2:
_callid++;
callCommand->outgoingCall(GetCall(call, _callid), // inform Plantronics my app has an outgoing call
GetContact(contact, "Bob%20Smith"), eAudioRoute::AUDIO_ROUTE_TO_HEADSET);
break;
case 3:
callCommand->answeredCall(GetCall(call, _callid)); // inform Plantronics my app has now answered an incoming (ringing) call
break;
case 4:
callCommand->holdCall(GetCall(call, _callid)); // place call on hold
break;
case 5:
callCommand->resumeCall(GetCall(call, _callid)); // resume the call
break;
case 6:
callCommand->muteCall(true); // mute the headset (note for wireless products, audio link must be active)
break;
case 7:
callCommand->muteCall(false); // unmute the headset (note for wireless products, audio link must be active)
break;
case 8:
callCommand->terminateCall(GetCall(call, _callid)); // inform Plantronics my app has now terminated the call
break;
case 0:
quit = true;
break;
default:
if (gGotDevice == true)
std::cout << "Unrecognised menu choice, please try again." << std::endl;
break;
}
Sleep(250);
}
input = "";
activeDevice->Release();
}
}
sessionManager->unregisterSession(session);
session->Release();
}
ShutDownSpokesRuntime();
return 0;
}