CISCO xAPI - HTTP Get Requests from a Hockey Fan

CISCO recently launched support for HTTP GET and PUT as commands that can be run from macros running on their CISCO Room systems. I love the idea of being able to expand the use of the CISCO Room System and Touch 10 panel to put APIs on connected systems to use. There are plenty of valid use cases for this - room booking systems, integration of enterprise applications and obviously integration with other AV systems in the room.

For me - I thought it would be great to have the latest scores of my favourite hockey team right on the Touch 10! GO SENS GO!

Below is a rough outline of how I completed this little project.

hockey-panel.png

Create the Panel

So - I started with a new panel on the Touch 10. The panel has 2 pages (tabs) - one for my Ottawa Senators - the other for the Montreal Canadians (boo). Each tab has 4 rows with textboxes that will contain information about the last game and the next upcoming game. In the table below - each data field is listed, as well as, the Widgit id’s on each panel page.

Field Ottawa Senators Tab
Widgit id
Boston Bruins Tab
Widgit id
Last Game Opponent OttawaLastOpponent MTLLastOpponent
Last Game Score OttawaLastScore MTLLastScore
Next Game Date OttawaNextDate MTLNextDate
Next Game Opponent OttawaNextOpponent MTLNextOpponent

So - my new panel on the Touch 10 looks like this:

Panel-dev.JPG

Add the Macro

Now - I need the macro with the fancy HTTP GET request to get the NHL Game Info. Good thing the NHL has an open REST API where you can get all kinds of data about the NHL - games, locations, player, etc…. I will not get into how the API works because it can be found on the internet.

So - we now flip over to the Macro editor and click the Create new macro button. Give the macro the name HOCKEY and turn it on. You can then paste the following code into the editor:

const xapi = require('xapi');

//Created by Colin Bush - BUSHTEK INC
//2019-10-31

function NextGameInfo(TeamID,TeamIDforPANEL) {
  var NextGameDay;
  var NextGameOpponent;
  var NextGameObj;
  var HockeyUrl = 'https://statsapi.web.nhl.com/api/v1/teams/' + TeamID + '?expand=team.schedule.next';

    //Get Next Game Info
    xapi.command('HttpClient Get', { 'Header': ["Content-Type: application/json"] , 'Url':HockeyUrl, 'AllowInsecureHTTPS': 'True'}).then((result) => {
    console.log(result.body);
    NextGameObj = JSON.parse(result.Body);
    NextGameDay = NextGameObj.teams[0].nextGameSchedule.dates[0].date;
    console.log (NextGameDay);
    if (NextGameObj.teams[0].nextGameSchedule.dates[0].games[0].teams.home.team.id == TeamID) {
      NextGameOpponent = NextGameObj.teams[0].nextGameSchedule.dates[0].games[0].teams.away.team.name;
    } else {
      NextGameOpponent = NextGameObj.teams[0].nextGameSchedule.dates[0].games[0].teams.home.team.name;
    }
    console.log ('NEXT GAME OPPONENT ' + NextGameOpponent);
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': TeamIDforPANEL+'NextDate', 'Value' :  NextGameDay});
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': TeamIDforPANEL+'NextOpponent', 'Value' :  NextGameOpponent});
    }).catch((error) => { console.error('NextGame ERROR - ' + error); });
}

function LastGameInfo(TeamID,TeamIDforPANEL) {
   //Get Last Game Info
    var LastGameObj;
    var LastGameDay;
    var LastGameOpponent;
    var MyScore;
    var OtherScore;
    var Score;
    var HockeyUrl = 'https://statsapi.web.nhl.com/api/v1/teams/' + TeamID + '?expand=team.schedule.previous';
    
    //Get LastGameInfo
    xapi.command('HttpClient Get', { 'Header': ["Content-Type: application/json"] , 'Url':HockeyUrl, 'AllowInsecureHTTPS': 'True'}).then((result) => {
    var body = result.Body;
    LastGameObj = JSON.parse(body);
    LastGameDay = LastGameObj.teams[0].previousGameSchedule.dates[0].date;
    console.log (LastGameDay);
    if (LastGameObj.teams[0].previousGameSchedule.dates[0].games[0].teams.home.team.id == TeamID) {
      LastGameOpponent = LastGameObj.teams[0].previousGameSchedule.dates[0].games[0].teams.away.team.name;
      MyScore =  LastGameObj.teams[0].previousGameSchedule.dates[0].games[0].teams.home.score;
      OtherScore = LastGameObj.teams[0].previousGameSchedule.dates[0].games[0].teams.away.score;
      } else {
      LastGameOpponent = LastGameObj.teams[0].previousGameSchedule.dates[0].games[0].teams.home.team.name;
      MyScore =  LastGameObj.teams[0].previousGameSchedule.dates[0].games[0].teams.away.score;
      OtherScore =  LastGameObj.teams[0].previousGameSchedule.dates[0].games[0].teams.home.score;
    }
    if (MyScore > OtherScore) {
      Score = 'WIN ' + MyScore +'-'+OtherScore;
    } else {
      Score = 'LOST '+ MyScore +'-'+OtherScore;
    }
    var LastOpp = TeamIDforPANEL + 'LastOpponent';
    var LastScore = TeamIDforPANEL + 'LastScore';
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': TeamIDforPANEL + 'LastOpponent', 'Value' :  LastGameOpponent});
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': TeamIDforPANEL + 'LastScore', 'Value' :  Score});
    console.log (MyScore + "-" + OtherScore);
    }).catch((error) => { console.error('LastGame ERROR - ' + error); });
}

function onGui(event) {
  if (event.PanelId == 'Hockey') {
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': 'OttawaLastOpponent', 'Value' :  ''});
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': 'OttawaLastScore', 'Value' :  ''});
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': 'OttawaNextDate', 'Value' :  ''});
    xapi.command('UserInterface Extensions Widget SetValue', { 'WidgetId': 'OttawaNextOpponent', 'Value' :  ''});
    LastGameInfo(9,'Ottawa');
    NextGameInfo(9,'Ottawa');
    LastGameInfo(8,'MTL');
    NextGameInfo(8,'MTL');
  }
}

xapi.event.on('UserInterface Extensions Panel Clicked', onGui);

Decoding the code a bit…

EVENT - xapi.event.on('UserInterface Extensions Panel Clicked', onGui);

  • Last line in the code above

  • It essentially says trigger the code in the onGui function whenever someone clicks on a panel

FUNCTION - onGui

  • This function is run when the event above is triggered

  • It checks to see if the Hockey Panel was clicked and, if so, it runs the functions to update the textboxes on the panel pages, specifically LastGameInfo and NextGameInfo

FUNCTION - LastGameInfo and NextGameInfo

  • These function makes the HTTP Get requests to the NHL API for the info about the last/next games played by both teams

  • The 1st parameter is the NHL API Team Code - 9 for Ottawa, 8 for Montreal

  • The 2nd parameter is the team identifier in the Widgit id of the panel pages

  • The functions are called twice within the onGui function - once for Ottawa and once for Montreal

xAPI Commands

  • xapi.command('HttpClient Get',…

    • Used within the LastGameInfo and NextGameInfo functions to make the HTTP Get request to the NHL API

    • The TEAM id which is the 1st parameter in function calls is inserted into the URL that is requested with the HTTP Get (HockeyUrl variable)

    • The blob that is returned is parsed using JSON.parse so that it can be easily read

    • Once you understand the NHL API - you can use a JSON parser test site to read what is returned.

  • xapi.command('UserInterface Extensions Widget SetValue',…

    • Used within the LastGameInfo and NextGameInfo functions to update the textboxes on the panels

    • The team identifier (2nd parameter) is used to build the name of the Widget to be updated on the panel pages

THAT IS IT!

That is the end of my attempt at using the CISCO Touch 10 to help me keep on top of my Hockey Pool. I am not a professional javascript coder so this was just a fun project to see what is possible. Below is a link to my panel xml file - if you would like to see what mine looked like (it is easier than recreating it). Make sure you merge it into your configuration or you may lose what you had before.

CISCO Touch10 Panel - Hockey.xml

Looking forward to my next adventure into the world of HTTP Put! I am thinking about trying to write Room Analytics data to a GOOGLE SHEET…

Enjoy this project and keep your fingers crossed for the Ottawa Senators - it is going to be a tough year!

GO SENS GO!

CISCO xAPI - Making a call from VBScript

I love using a CISCO DX80 but one of my pet peeves are the on-screen menus.

I use my DX80 as my primary computer display. It never fails - when I open a calendar invitation and ready myself to dial-in to a meeting, the on-screen menu pops up over top of my calendar invite. I then have to move my calendar invitation to my other display so I can see the dial-in instructions. (no comments about the one-button-to-push please).

My solution was to write a little VBScript that will prompt me for the dial-in information and then use the CISCO xAPI (api) to have my DX80 make the call. It uses the same logic as outlined in my previous post just fronts it with the user prompts for the dial string.

IMPORTANT NOTE - Don’t use administrator credentials in your script. Create a user account for this.

The top of the script has 3 variables:

  • user - set this variable to the username that should be used to authenticate to the endpoint.

  • pass - you really shouldn’t really include the password in the script file. If you set this to ““ - the script will prompt you for the password. If you really must - set this variable to the password for the user.

  • IPAddressFixed - if you are going to use this script to make calls from different endpoints - set this variable to ““ and the script will prompt you for the IP address of the endpoint you want to make the call. If you are always going to use this script to make calls on a single endpoint - set this variable to the IP Address of the endpoint and the script will not ask you for this information.

Please forgive the lack of error checking after it sends the Dial command…

This script has been tested with EX90s and some of the SX line as well.

Hope this helps…

Set xmlHTTP = CreateObject("Msxml2.ServerXMLHTTP.6.0")

Dim user
Dim pass
'SET PASS to blank if you want the script to prompt for password
user="" ' or "supersecretpassword" if you must enter the password
user="apiuser"
'SET IPAddressFixed to blank if you want the script to prompt for IP address of Endpoint
IPAddressFixed = "10.0.0.10"

'GET CISCO ENDPOINT IP
If IPAddressFixed = "" then
    IPAddress=inputbox("Enter IP of SYSTEM","CISCO CALL SCRIPT")
    if IPAddress = "" then
        msgbox "Call Cancelled"
        Wscript.quit
    end if
end if

'GET PASSWORD
if pass="" then
    pass=inputbox("Enter Password","CISCO CALL SCRIPT")
    if pass = "" then   
        msgbox "Call Cancelled"
        Wscript.Quit
    end if
end if

'GET SIP DIAL STRING
sipDial = inputbox ("Enter SIP Dial String","CISCO CALL SCRIPT")
if sipDial= "" then
    msgbox "Call Cancelled"
    Wscript.Quit
end if

'MAKE XML POST
'== uncomment the next line if need to ignore certificate errors 
xmlHTTP.setOption 2, 13056
'== uncomment the next line if you need to bypass the system proxy 
xmlHTTP.SetProxy 1

if IPAddress = "" then
    xmlHttp.Open "POST", "https://" & IPAddressFixed & "/putxml", False, user, pass
else
    xmlHTTP.Open "POST", "https://" & IPAddress & "/putxml", false, user, pass
end if
xmlHTTP.setRequestHeader "Content-Type", "text/xml"
'xmlHTTP.setRequestHeader "Authorization", "Basic " & Base64Encode(user & ":" & pass)

xmlHTTP.send "<Command><Dial><Number>"&sipDial&"</Number><Protocol>SIP</Protocol></Dial></Command>"
msgbox "CALLING NOW"

'DONE

CISCO xAPI - GET and PUT Requests

I refer to myself as a “cut-&-paste programmer” - I know enough to be dangerous. Google is an excellent source of information and, if you understand the basics, you can piece together code to do almost anything. So - here we go….

This first blog is to just set the stage for using VBScript and the xAPI to interact with a CISCO endpoint. I am accessing the CISCO endpoint API using HTTPS - CISCO refers to it as XMLAPI. With this API you use POST requests to tell the endpoint to do something or to change a configuration and GET requests to get configuration or status information from the endpoint.

For either POST or GET - your script makes a connection to the endpoint, authenticates and returns the results.

Steps involved in VBScript:

Endpoint Info for Examples
Endpoint IP - 192.168.2.11
Authentication User - apiuser
Authentication Password - supersecretpassword

  1. Create the Msxml2.ServerXMLHTTP.6.0 object

  2. Open the connection to the endpoint using either POST or GET

  3. Set the Request Headers

  4. Send the API Commands

  5. Display the response

The script examples below are very basic and do not do any formatting of the responses so you can get an idea of what is sent to the endpoint and what the results look like.

XML POST REQUEST EXAMPLE

This is an script that uses a POST request to initiate a call from the endpoint to the BlueJeans Test Site.

The API commands are XML formatted.

<Command><Dial>
<Number>111@199.48.152.152</Number><Protocol>SIP</Protocol>
</Dial></Command>

The API commands can be found on the support.cisco.com site or by browsing to the commands.xml page from the endpoint.

https://<endpointIP>/command.xml

'====  EXAMPLE FOR MAKING A CALL ====
'1. Create the XMLHttp Object
Set xmlHTTP = CreateObject("Msxml2.ServerXMLHTTP.6.0")
'2. Make the Connection to the Endpoint
xmlHTTP.setOption 2, 13056 'Required to ignore certificate errors
xmlHTTP.Open "POST", "https://192.168.0.1/putxml", false, “apiuser”, “supersecretpassword”
'3. Set the Request Header
xmlHTTP.setRequestHeader "Content-Type", "text/xml"
'4. Send the API commands
xmlHTTP.send "<Command><Dial><Number>111@199.48.152.152</Number><Protocol>SIP</Protocol></Dial></Command>"
'5. Display the Response from the API command
MsgBox xmlHTTP.responseText

xml POST RequesT Example

This is example of a scripted POST request that will return whether the endpoint is muted or not.

You can see the open request is a GET and includes the path to the status information you want, in this case the status of the Microphones.

For a list of the all of the status information you can collect off the endpoint - you can get the status.xml page from the endpoint in a browser.

https:/<endpointIP>/status.xml

'==== EXAMPLE FOR CHECKING MUTE STATUS ====
'1. Create the XMLHttp Object
Set xmlHTTP = CreateObject("Msxml2.ServerXMLHTTP.6.0")
'2. Make the Connection to the Endpoints
xmlHTTP.setOption 2, 13056 ' Required to ignore certificate errors
xmlHTTP.Open "GET", "https://192.168.2.11/getxml?location=/Status/Audio/Microphones", false, "apiuser","supersecretpassword"
'3. Set the Request Headers
xmlHTTP.setRequestHeader "Content-Type", "text/xml"
'4. Send the API Commands
xmlHTTP.send
'5 Display the Response from the API command
MsgBox xmlHTTP.responseText

You will see the the msgbox at the end of the script shows the XML representation of the status of the microphones including the Mute value.

In the image of the msgbox here - you can see Mute is ON.



muteSTATUS.png

Controlling CISCO DX80 with the xAPI

I have been using the new CISCO DX80 for some time now it works great for video calling, as well as, the primary display for my computer.  I had a bit of trouble with the focus of the camera but new firmware and better light it seems to be working well now.

dx80.jpg

While the unit is great - I have struggled a bit with the interface particularly as a busy Tech Guy. This series of blogs will cover some simple tasks I have attempted to automate with the CISCO xAPI that are a little clunky with the DX80.

Most of these will work with any CISCO endpoint but most of the testing was done with a CISCO DX80 and EX90!

The following are some of the functions that I have automated with some pretty simple VBScript on my laptop.

  • Mute/UnMute - This was the first function I wanted to automate. There is a physical button to mute/un-mute but I wanted to do it using a keystroke on my computer.

  • Dialing - This was my biggest pet peeve! I use the DX80 as my primary display. When I open an Outlook meeting invitation with the Virtual Meeting Room (SIP address) in it and touch the screen on my DX80 to dial the address - the on-screen menu displays over top of my Outlook invite so I can’t see the dialing instructions…

  • Pull live call statistics - This was just something I was interested in doing as a network guy.

As mentioned above - I am doing most of this with VBScript but will also share a short post that shows how to send these commands from a Mac using CURL.

For an excellent source of information on the CISCO xAPI - check out the following:

https://developer.cisco.com/site/roomdevices/

Hope you enjoy this series…

Colin

UC Blog - Post #1

The purpose of this BLOG will be for me to ramble on about interesting things happening in the UC, videoconferencing, VoIP and network communications industry.  I am in Canada and do much of my consulting work with the Government of Canada.  As such, this BLOG will focus on how changes in this industry are affecting Canada and my client base.

Looking forward to doing this...

Colin