Thanks to a Nodejs library jsxapi we can bring these codec in from the cold and give them some powerful tools to handle in-room controls from the Touch 10 interface. My example focuses on in-room controls but this logic can be applied across monitoring any of the events that a Cisco video endpoint could generate.
There are some great macro examples on the DevNet Github repo. I am going to use one as an example to show how to convert it into a nodejs server application. The example I am using today is the audio only dial button. The example interface is shown below.

Typically when you select said button you get this interface:

I am going to change this up slightly and instead of providing a straight dialing interface for an audio call, I am going to provide the ability to enter a meeting number to join a Webex. See below:
Once the meeting number is entered and the Join button is pressed the appropriate @mysite.webex.com is appended to the call and the calling interface launches. So a slight change but easy dialing into a meeting is always a good thing. If you want to stick to the original like the example it should be a matter of a few small changes.
To turn this into a Nodejs application we need two files. First is the typical Node application launch file index.js. This file is pretty simple and launches the application by creating an array of endpoint objects using the TPXapi module which is our second file. My list of endpoints is a simple array but feel free to use a database or json file in its place to store your list of host URL's or IP addresses.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var TpXapi = require( "./utils/endpoint"); | |
const codecs = ['10.10.10.116']; | |
const videoArray = []; | |
codecs.forEach( function (ip) { | |
const endpoint = { | |
'password':"password", | |
'username':"admin", | |
'ipAddress':ip | |
}; | |
const videoCodec = new TpXapi(endpoint); | |
videoArray.push(videoCodec); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Constructor for creating a endpoint object with video xapi. | |
const util = require('util'); | |
const EventEmitter = require('events').EventEmitter; | |
const jsxapi = require('jsxapi'); | |
//pass in object | |
function TPXapi(endpoint){ | |
this.endpoint = endpoint; | |
this.xapi; | |
this.connectedStatus = 'false'; | |
this.domain = '.webex.com'; | |
this.mysite = 'mysite'; | |
this.init(); | |
} | |
util.inherits(TPXapi,EventEmitter); | |
TPXapi.prototype.init = function(){ | |
const self = this; | |
return self.connect() | |
.then((status) =>{ | |
console.log(status); | |
self.onReady(); | |
return; | |
}) | |
.catch((err) => { | |
console.error(err); | |
}) | |
} | |
//connect to ssh service on endpoints | |
TPXapi.prototype.connect = function() { | |
var self = this; | |
return new Promise((resolve, reject) => { | |
self.xapi = jsxapi.connect('ssh://' + self.endpoint.ipAddress||self.endpoint.url, { | |
username: self.endpoint.username, | |
password: self.endpoint.password, | |
keepaliveInterval: 4000 | |
}); | |
self.onError(); | |
resolve ("Connection opening..........") | |
.catch ((err) => { | |
reject (console.error(err)); | |
}); | |
}); | |
} | |
//close ssh connection | |
TPXapi.prototype.closeConnect = function(){ | |
const self = this; | |
return new Promise((resolve, reject) => { | |
console.log("xapi session closed."); | |
self.connectedStatus = "false"; | |
resolve (self.xapi.close()); | |
return self; | |
}) | |
}; | |
//Load event monitoring after connecting via ssh to endpoint | |
TPXapi.prototype.onReady = function(){ | |
const self = this; | |
self.xapi.on('ready', () => { | |
console.log("connexion successful!"); | |
self.connectedStatus = "true"; | |
self.webexMeeting(); | |
return self; | |
}) | |
}; | |
//event monitors for webex meetings | |
TPXapi.prototype.webexMeeting = function(){ | |
const self =this; | |
self.xapi.event.on('UserInterface Extensions Event PageOpened', (event) => { | |
console.log('event', event.PageId); | |
if (event.PageId == 'webex_dial') { | |
//Intial press of webex button | |
self.xapi.command('UserInterface Message TextInput Display', { | |
FeedbackId: 'MeetingID_webex', | |
InputType: 'Numeric', | |
KeyboardState: 'Open', | |
Title: 'Join a Webex Meeting', | |
Text: 'Enter the Meeting ID', | |
Placeholder: 'Meeting ID', | |
SubmitText: 'Join' | |
}); | |
} | |
}); | |
//Making calling to webex meeeting once ID is entered. | |
self.xapi.event.on('UserInterface Message TextInput Response', (event) => { | |
if (event.FeedbackId === 'MeetingID_webex') { | |
var ID = event.Text; | |
var URI = ID + '@' +self.mysite+ self.domain; | |
self.xapi.command('Dial', { Number: URI }); | |
} | |
}); | |
}; | |
//track ssh error events and reconnect | |
TPXapi.prototype.onError = function(){ | |
const self = this; | |
self.xapi.on('error', (err) => { | |
console.error(`connexion failed: ${err}`); | |
if(err === 'client-timeout'){ | |
setTimeout(function(){ | |
self.init(); | |
}, 4000) | |
}else{ | |
self.closeConnect(); | |
setTimeout(function(){ | |
self.init(); | |
}, 4000) | |
} | |
}); | |
}; | |
module.exports = TPXapi; | |
VoIPNorm