Click here to Skip to main content
Click here to Skip to main content

Plantronics Legend UC Headset – A Call Center Example

, 28 Feb 2013 CPOL
Plantronics Legend UC Headset – A Call Center Example

Editorial Note

This article is in the Product Showcase section for our sponsors at CodeProject. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.

When I was asked to consider checking out a new Plantronics headset, I immediately thought of my time working as a phone representative, answering customer support requests. My team would answer the phone using a telephone headset that made us look like we were landing planes for the military. All day we would sit at our desks in front of a screen that would show us information about the customers who were calling in. We could search for information in an online knowledge base, but for the most part, we spent our days staring at that screen.

Our call center had an automated time clock based on the time you were available to receive customer’s phone calls. When you arrived, you would clock in by keying a pin into your telephone keypad. Once your pin was keyed in, you would be online to receive calls. As long as you were authenticated to your phone, calls would be forwarded to you. When you would get up from your desk for a break, lunch, or for the end of the day you would log off your phone. This process would stop the calls from coming to your extension and clock out of the time clock.

In this example, I am going to recreate this scenario using the Plantronics Legend UC headset. While this is not a headset you’ll find in a call center it is a great platform for a technology demonstration today, anticipating sensor-equipped call center headsets tomorrow. This headset is Bluetooth capable and connects to many different soft-phones available including Lync, Skype, and Avaya. Additionally, there are sensors on-board to detect proximity to the Bluetooth receiver. There are also sensors to detect if the headset is on your head. This allows the headset to automatically answer calls when you put the headset over your ear. I think these two sensors are very interesting when considering the scope of this article, and I will focus on using them to automate our clock-in / clock-out process.

Initial Development Setup

In order for a workstation to interact with all of the sensors of a Plantronics headset, a client software package called Spokes must be installed. For developers, there is a software development kit that you can download and use in place of the client software. Through the rest of this sample, I am going to assume that you have downloaded, installed, and have the Spokes software service running.

I am going to start developing with Visual Studio 2012 and create a blank web forms application with ASP.Net 4.5. We want notifications to be presented to the call center representative and I am going to put together a simple timesheet grid. For these user-interface elements, I am going to add a trial copy of Telerik’s ASP.Net AJAX controls. Once the controls are installed, I will right-click the web project in the solution explorer and convert it to a RadControls Web Project with the Windows 7 theme.

To connect to the headset from the browser, I need to add a JavaScript file from the Plantronics SDK called spokes.js You can find this file, after you install the SDK, at: c:\Program Files (x86)\ Plantronics\Plantronics SDK\Samples\RestJsClient I placed my copy of this file in the Scripts folder in my web project. I want isolate all of the logic for connecting to the headset and time clock operations into a single JavaScript file that will be referenced by all pages in the application. For this, I created a JavaScript file called callcenter.js and added it to my Scripts folder. In order to achieve this common use of the headset throughout the application, I have added references to the spokes.js and callcenter.js to my site.master file that will be used by all pages in the application.

           <%--Site scripts--%>
            <asp:ScriptReference Path="~/Scripts/spokes.js" />
            <asp:ScriptReference Path="~/Scripts/callcenter.js" />
        </Scripts>
    </asp:ScriptManager>
Listing 1 – Script References added to Site.Master that will control the headset

The notifications will be presented inside of a Telerik Notification control. This control will present itself as a sliding window in the bottom corner of the screen, similar to other “toast notifications” you receive from system services from the Windows taskbar. The markup for this control is as follows:

       <telerik:RadNotification ID="statusNotification" runat="server" Animation="Slide" Title="Headset Status" OffsetX="-10" OffsetY="-10" ShowCloseButton="False" ViewStateMode="Disabled" Height="125" Width="250">
            <ContentTemplate>
                <table style="width: 250px; height: 75px;">
                    <tr>
                        <td valign="center">
                            <img id="Img1" runat="server" src="~/Images/headset_40.png" />
                        </td>
                        <td valign="center">
                            <span id="statusText"></span>
                        </td>
                    </tr>
                </table>
            </ContentTemplate>
        </telerik:RadNotification>
Listing 2 – RadNotification control markup

Building out the Headset Call Center

The callcenter.js file is a standalone component that isolates all of the negotiations with the headset and posting of notifications about the headset to the user. I have started the construction of this script with a self-executing function, to isolate private methods and expose only those methods needed for the rest of the application.

var CallCenter = (function() {
    var statusCtl;
    var spokes;
    var initFunction = function () {

        spokes = new Spokes("http://127.0.0.1:32001/Spokes");
        statusCtl = $find(controls.statusNotification.ClientID);

        ConnectSpokes();
    };
    return {
        Init: initFunction,
        Spokes : function() {
            return spokes;
        },
    };

})();

$().ready(function() {
    CallCenter.Init();
});
Listing 3 – JavaScript foundation for the CallCenter JavaScript object

In this code listing, I have allocated a private variable to store the reference to the Spokes service. Additionally, I have captured a reference to the Notification control object so that we may publish messages of our user later. The spokes object is constructed from a service that runs on the local machine and answers on port 32001. Once we have our reference to the spokes object, we need to connect to the headset with the connectSpokes() method. This method will allow us to start listening for state change events that the Spokes service will publish for us using a queue with a pub/sub mechanism.

   var ConnectSpokes = function() {
    	
        spokes.Device.deviceList(function (result) {
            if (result.isError) {
                ShowMessage("Unable to connect to headset");
            } else if (result.Result[0] == null) {
                ShowMessage("Error - Is there a headset connected?");
            } else {
                // attach to the device
                spokes.Device.attach(result.Result[0].Uid, ControlInterface);
                PollDeviceEvents();
            }
        });

    };

    //Creates a control interface
    function ControlInterface(session) {
        if (session.isError || !spokes.Device.isAttached) {
            ShowMessage("Session Registration Error");
        }
        else {
            spokes.Device.deviceInfo(function (result) {

                if (result.Result.RemoteFirmwareVersion == null) {
                    // no headset attached
                    ShowMessage("No headset attached to adapter");
                    clockedInState = false;
                } else {
                    ShowMessage("Connected to headset successfully");
                }

                console.log(result);
            });
        }
    }
Listing 4 – Connecting to the Spokes service with REST

There are several tests to note in these methods:

  • First, we are checking that the request for a list of devices did not return an error. If an error was returned from a list of devices, then the Plantronics adapters and headset are not properly connected and powered.
  • If the device list is empty, then there is an error as well. We will display a message indicating the user should check headset connections
  • In the controlInterface method there is a test for the RemoteFirmwareVersion. This is the only property exposed by the headset device itself that we can inspect. All other stateful properties that are exposed are those of the USB adapter. We must check for the presence of a value on this property in order to verify that a headset is properly connected to our workstation.

Figure 1 – Notification of a successful connection to the headset service

If these tests pass, we notify the user, attach to the headset and start listening for events in the pollDeviceEvents method. For reference, here is the body of the sendMessage method, which utilizes the notification control:

    function ShowMessage(msg) {
    	
        $("#statusText").html(msg);
        statusCtl.show();
    }
Listing 5 – The ShowMessage method that notifies our browser user

The pollDeviceEvents method is a batch of checks and pivots to determine how to best respond to events raised by the headset. Its source is below:

    function PollDeviceEvents() {
    	
        setInterval(function() {

            spokes.Device.events(function (result) {
                if (!result.isError) {

            	    for (var i = 0; i < result.Result.length; i++) {

                        var eventName = result.Result[i].Event_Name;

                        if (eventName == "Doff" || eventName == "Don" || eventName == "OutofRange" || eventName=="InRange") {

                            if (eventName == "Doff" || eventName == "OutofRange") {
                                BeginClockout();
                            }
                            else if (eventName == "Don" || eventName == "InRange") {

                                window.clearInterval(clockOutInterval);
                                statusCtl.set_autoCloseDelay(3000);

                                    LogTimeEvent(true);
                                    ShowMessage("Going on the clock");

                            }

                        }

                        console.log(result.Result[i].Event_Name);
                    }
                }
            })

        }, 500)


    };
Listing 6 – PollDeviceEvents method – Handling the events raised by the headset

This method is only interested in four events that the headset raises: Don, Doff, InRange and OutofRange. The first two events describe the device either being placed on or removed from the user’s head. The latter two events describe the relative proximity of the headset device to the workstation. In my scenario, when the user takes off their headset or they walk away from their workstation, we will use the BeginClockout method to allocate 30 seconds for them to come back or be clocked off.

    var secondsDelay = 30;
    var secondsUntilClockOut;
    function BeginClockout() {
    	
        // Don't set the clockout interval twice
        if (secondsUntilClockOut > 0)
            return;

        secondsUntilClockOut = secondsDelay;
        statusCtl.set_autoCloseDelay(secondsDelay * 1000);
        ShowMessage("You have removed your headset and will be clocked out in <span id='countDown'>" + secondsDelay +  "</span> seconds");

        clockOutInterval = window.setInterval(function () {

            if (secondsUntilClockOut == 0) {
                window.clearInterval(clockOutInterval);
                LogTimeEvent(false);
            }

            secondsUntilClockOut--;
            $("#countDown").html(secondsUntilClockOut);
        }, 1000);

    }
Listing 7 – The BeginClockout method – indicate that a user will be automatically clocked out

In the BeginClockout method, we set a timer to count every second until the specified secondsDelay value runs out. During this time, we will use our notification control through the ShowMessage method to illustrate how much time is left until the time clock action takes place.

Figure 2 – Notification of pending time clock action

The time clock will be acted upon in the LogTimeEvent method, passing in an indicator of the new state. I will extend the CallCenter object to provide a way to identify the employee and location of the time clock service, so that in the LogTimeEvent method I can make a standard jQuery-based ajax call to a WebAPI service with all of the information needed to effect the time clock.

    var timeClockUrl = "";
    var employeeId = -1;

    // Enhanced return statement for CallCenter
    return {
        Init: initFunction,
        Spokes : function() {
            return spokes;
        },
        set_TimeClockUrl: function(url) {
            timeClockUrl = url;
            return this;
        },
        set_EmployeeId: function(id) {
            employeeId = id;
            return this;
        }
    };


    function LogTimeEvent(beginWork) {
        // If beginWork is true, it is a ClockIn event, if false, it is a ClockOut event

        clockInTimeout = window.setTimeout(function () {
            // Do AJAX clock-in
            $.ajax(timeClockUrl, {
                async: false,
                cache: false,
                type: "POST",
                dataType: "json",
                contentType: "application/json",
                data: JSON.stringify({
                    EmployeeId: employeeId,
                    StartClock: beginWork
                })
            });

            clockedInState = beginWork;
            scheduledClockIn = false;

        }, 1000);

        scheduledClockIn = true;

    }
Listing 8 – The LogTimeEvent method – The transport that connects a headset event to a time clock event

The payload for this jQuery post contains the employeeId and the indicator of new time clock state. This method is configured to delay for one second the transmission of this information. This short delay allows us to trap repeated events from the headset and only transmit one call to the time clock service.

Configuring the Time Clock Service

For this sample, I have elected to use ASP.Net WebAPI as the service framework for our time clock. I have created an api folder in my project, and in this folder I will use the ‘Add New Item’ menu to add a new Web API Controller class named ClockController. Inside of this class, I’m going to build out the Post method to accept the payload from our jQuery ajax call inside of the LogTimeEvent method.

        public static readonly List<TimeClockEvent> TimeClockEvents = new List<TimeClockEvent>();


        public HttpResponseMessage Post([FromBody]ClockModel model)
        {

            if (model.StartClock)
            {
                var evt = new TimeClockEvent()
                {
                    Id = TimeClockEvents.Count + 1,
                    EmployeeId = model.EmployeeId,
                    ClockIn = DateTime.Now
                };
                TimeClockEvents.Add(evt);
            }
            else
            {
                var evt = TimeClockEvents.OrderByDescending(e => e.ClockIn).First(e => e.EmployeeId == model.EmployeeId);
                evt.ClockOut = DateTime.Now;
            }

            return new HttpResponseMessage(HttpStatusCode.Created);

        }


        public class ClockModel
        {
            public int EmployeeId;
            public bool StartClock;
        }

public class TimeClockEvent
{

    public int Id { get; set; }
    public int EmployeeId { get; set; }
    public DateTime ClockIn { get; set; }
    public DateTime ClockOut { get; set; }
    public TimeSpan Elapsed
    {
        get { return ClockOut - ClockIn; }
    }
}
Listing 9 – The ClockController and supporting models

I am using a static list as an in-memory store of time clock events. In a real application, you will want to use some other form of persisted storage like a database to store your time records. This POST method receives our payload as a ClockModel object. Based on the desired state of the time clock record, the method will create or update a record in the TimeClockEvents list.

With this data in place, we can create a simple timesheet with a grid control that is bound to the data source to demonstrate that we are in fact logging time records.

Figure 3 – A timesheet showing two previously entered records and a third record generated from a headset

Summary

In this article, we put together a JavaScript object that connected to a RESTful service on the local machine to communicate with a Plantronics headset. Our object would listen for events triggered from the headset’s internal sensors and send appropriate messages to a web service that we constructed with WebAPI. The sample code for this project can be downloaded here. Please note, the sample code contains a more robust connection and error handling for the headset events. The Plantronics Developer forum and SDK can be accessed at http://developer.plantronics.com The Telerik controls can be downloaded from http://www.telerik.com/products/aspnet-ajax/download.aspx

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jeffrey T. Fritz
Telerik
United States United States
A Microsoft MVP, ASPInsider and ASP.NET Developer Evangelist for Telerik. Jeffrey is a software developer coach, architect, and speaker in the Microsoft.Net community. A Pluralsight author and international speaker, Jeffrey makes regular appearances at conferences such as TechEd, DevIntersection, CodeStock, FalafelCon, DevReach and New York Code Camp as well as user group meetings in an effort to grow the next generation of software developers
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralThat's Hardcore PinmemberXenjin27-Feb-13 4:29 
GeneralSimpler use of RadNotification Pinmemberrdmptn27-Feb-13 3:55 
GeneralRe: Simpler use of RadNotification PinmemberJeffrey T. Fritz27-Feb-13 4:06 
QuestionSuggestion PinmemberMarc Morrell27-Feb-13 3:45 
AnswerRe: Suggestion PinmemberJeffrey T. Fritz27-Feb-13 3:48 
AnswerRe: Suggestion Pinmembervinodkumarnie29-Mar-13 22:14 
You just have write "You are now in The Matrix" instead of "Connected to headset successfully"... Thats it. Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.2 | Last Updated 28 Feb 2013
Article Copyright 2013 by Jeffrey T. Fritz
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid