UNPKG

twonet

Version:

A Node implementation of the Qualcomm Life 2net API.

585 lines (508 loc) 19.9 kB
'use strict'; var logme = require('logme'); var async = require("async"); var request = require("request"); var xml2js = require("xml2js"); var parser = new xml2js.Parser({explicitArray:true}); var config = require('./config'); var debug = function() {}; if(process.env.DEBUG) { try { debug = require('debug')('twonet'); } catch (e) { console.log("Notice: 'debug' module is not available. This should be installed with `npm install debug` to enable debug messages", e); debug = function() {}; } } function TwoNetAPI(customer_id, auth_key, region, env) { var configuration = config[region][env]; this.api_rev = 'revY'; this.xmlns = 'urn:com.twonet.sp.cuc.revisiony'; this.customer_id = customer_id; // defaults to dev environment this.auth_key = auth_key; this.host_name = configuration.host_name; this.url_path = configuration.url_path; this.getHubGeneration = function(hubId) { var gen = -1; if (hubId.charAt(8) === '0') { gen = 1; } else if (hubId.charAt(8) === 'T') { gen = 2; } return gen; }; this.buildPath = function(command) { return(this.url_path + this.api_rev + command); }; this.call = function(command, method, write_object, callback) { var options = { url: "https://" + this.host_name + "/" + this.buildPath(command), headers: { 'authKey' : this.auth_key, 'customerId' : this.customer_id, 'Content-Type': 'application/xml' }, method: method, body: write_object }; debug('Call ' + command); debug(options); debug(write_object); // wrap the http request call for the retry loop var api_method = function(done,results) { request(options, function(error, response, body) { if( error ) { debug('Request error: ' + error); done(-1,{}); } else if( response.statusCode !== 200 ) { debug(response.statusCode); debug(response.headers); done(response.statusCode, response); } else { done(null,response); } }); }; // qualcomm will throttle us. this retry loop is intended // to mitigate this hazard. // async.retry({times:4,interval:2000}, api_method, function(status_code, response) { // retries complete because it either worked or we've // hit the max retry limit. if( status_code && status_code !== 200 ) { logme.error('Total fail making this call go through : ' + command); callback(status_code,response.body); } else { callback(200,response.body); } } ); }; this.parseQCLResults = function(status, result, callback) { if( !result || result.indexOf(this.xmlns) < 0 ) { if( status !== 200 ) { logme.error('QCL call failed with an empty return object : ' + status); callback(-1,{}); } else { callback(0, {result:result}); } return; } parser.parseString(result, function(err,jsonResult) { // API Error state if( status !== 200 ) { if( err ) { logme.error('Unable to parse the QCL error data. Impossible!'); } else { debug('QCL call error : ' + status); debug(jsonResult); } callback(-1, {}); } else if( err ) { // API success but parsing failed logme.error('Unable to parse QCL results : ' + err); debug(jsonResult); callback(-1, {}); } else { callback(0, jsonResult); } }); }; this.createDeviceByCustomerAndModelRequest = function(model) { return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DeviceByCustomerAndModel xmlns="' + this.xmlns + '">' + '<model>' + model + '</model>' + '<recordRange>' + '<startIndex>0</startIndex>' + '<numberOfRecords>100</numberOfRecords>' + '</recordRange></DeviceByCustomerAndModel>' ); }; this.hubRequest = function(hub_id) { var twonetId = undefined; var hubGeneration = this.getHubGeneration(hub_id); if (hubGeneration === 1) { twonetId = 'TWONETID'; } else if (hubGeneration === 2) { twonetId = 'TWONET2ID'; } return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<HubHardware xmlns="' + this.xmlns + '">' + '<hardwareId>' + hub_id + '</hardwareId>' + '<hardwareIdType>' + twonetId + '</hardwareIdType>' + '</HubHardware>' ); }; this.hubCommandRequest = function(hub_id,priority) { var twonetId = undefined; var hubGeneration = this.getHubGeneration(hub_id); if (hubGeneration === 1) { twonetId = 'TWONETID'; } else if (hubGeneration === 2) { twonetId = 'TWONET2ID'; } return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<HubHealthInfoMessage xmlns="' + this.xmlns + '">' + '<hubHardware>' + '<hardwareId>' + hub_id + '</hardwareId>' + '<hardwareIdType>' + twonetId + '</hardwareIdType>' + '</hubHardware>' + '<priority>' + priority + '</priority>' + '</HubHealthInfoMessage>' ); }; this.hubDevicesRequest = function(hub_id,start,count) { var twonetId = undefined; var hubGeneration = this.getHubGeneration(hub_id); if (hubGeneration === 1) { twonetId = 'TWONETID'; } else if (hubGeneration === 2) { twonetId = 'TWONET2ID'; } return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DeviceByHub xmlns="' + this.xmlns + '">' + '<hub>' + '<HubId>' + hub_id + '</HubId>' + '<HubIdType>' + twonetId + '</HubIdType>' + '</hub>' + '<recordRange><startIndex>' + start + '</startIndex>' + '<numberOfRecords>' + count + '</numberOfRecords>' + '</recordRange></DeviceByHub >' ); }; this.hubHardwareRequest = function(activate, hub_id) { var xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'; var twonetId = undefined; var hubGeneration = this.getHubGeneration(hub_id); if (hubGeneration === 1) { twonetId = 'TWONETID'; } else if (hubGeneration === 2) { twonetId = 'TWONET2ID'; } if( activate ) { xml += '<ActivateHub xmlns="' + this.xmlns + '">'; } xml += '<HubHardware xmlns="' + this.xmlns + '">' + '<hardwareId>' + hub_id + '</hardwareId>' + '<hardwareIdType>' + twonetId + '</hardwareIdType>' + '</HubHardware>'; if( activate ) { xml += '</ActivateHub>'; } return(xml); }; this.deviceRequest = function(model_name) { return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DeviceByCustomerAndModel xmlns="' + this.xmlns + '">' + '<model>' + model_name + '</model>' + '<recordRange>' + '<startIndex>0</startIndex>' + '<numberOfRecords>100</numberOfRecords>' + '</recordRange></DeviceByCustomerAndModel>' ); }; this.createDeviceRequest = function(model_name,mac,sensor_type) { var result = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<AddDevice xmlns="' + this.xmlns + '">' + '<deviceCustomer>' + this.customer_id + '</deviceCustomer>' + '<deviceSerialNumber>' + mac + '</deviceSerialNumber>' + '<airInterfaceType>' + sensor_type + '</airInterfaceType>' + '<softwareVersion>1.12</softwareVersion>' + '<deviceModelName>' + model_name + '</deviceModelName>' + '<deviceAddress>' + mac + '</deviceAddress>' + '<deviceCommType>DIRECT</deviceCommType>'; if( sensor_type === 'BLUETOOTH' ) { result += '<specification><btSpecification>' + '<btPairingMode>SECURE</btPairingMode>' + '<btPasskey>1234</btPasskey>' + '</btSpecification></specification>'; } result += '</AddDevice>'; return result; }; this.deviceIDRequest = function(mac, sensor_type) { return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DeviceIdentifier xmlns="' + this.xmlns + '">' + '<airInterfaceAndAddress>' + '<deviceAddress>' + mac + '</deviceAddress>' + '<airInterfaceType>' + sensor_type + '</airInterfaceType>' + '</airInterfaceAndAddress>' + '</DeviceIdentifier>' ); }; this.deviceRegisterRequest = function(hub_id, mac, sensor_type) { var twonetId = undefined; var hubGeneration = this.getHubGeneration(hub_id); if (hubGeneration === 1) { twonetId = 'TWONETID'; } else if (hubGeneration === 2) { twonetId = 'TWONET2ID'; } return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<RegisterDevice xmlns="' + this.xmlns + '">' + '<deviceIdentifier><airInterfaceAndAddress>' + '<deviceAddress>' + mac + '</deviceAddress>' + '<airInterfaceType>' + sensor_type + '</airInterfaceType>' + '</airInterfaceAndAddress> </deviceIdentifier>' + '<hub>' + '<HubId>' + hub_id + '</HubId>' + '<HubIdType>' + twonetId + '</HubIdType>' + '</hub>' + '</RegisterDevice>' ); }; this.deviceAssociationRequest = function(action, hub_id, mac, sensor_type) { var twonetId = undefined; var hubGeneration = this.getHubGeneration(hub_id); if (hubGeneration === 1) { twonetId = 'TWONETID'; } else if (hubGeneration === 2) { twonetId = 'TWONET2ID'; } return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<HubToDeviceAssociation xmlns="' + this.xmlns + '">' + '<action>' + action + '</action>' + '<hub>' + '<HubId>' + hub_id + '</HubId>' + '<HubIdType>' + twonetId + '</HubIdType>' + '</hub>' + '<hubAirInterface>' + '<airInterfaceType>' + sensor_type + '</airInterfaceType>' + '<hubDeviceAddressRange>' + '<fromRange>' + mac + '</fromRange>' + '<toRange>' + mac + '</toRange>' + '</hubDeviceAddressRange>' + '</hubAirInterface>' + '</HubToDeviceAssociation>' ); }; this.deviceMessageRequest = function(model_name, mac, command) { return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DeviceMessage xmlns="' + this.xmlns + '">' + '<deviceIdentifier>' + '<serialNumberAndModel>' + '<deviceSerialNumber>' + mac.replace(/:/gi,'') + '</deviceSerialNumber>' + '<deviceModelName>' + model_name + '</deviceModelName>' + '</serialNumberAndModel>' + '</deviceIdentifier>' + '<message>' + new Buffer(command).toString('base64') + '</message>' + '<priority>true</priority>' + '<toDA>true</toDA>' + '</DeviceMessage>' ); }; this.deviceCommandTrackingRequest = function(command_id) { return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DeviceCommandTrackingNumber xmlns="' + this.xmlns + '">' + '<trackingNumber>' + command_id + '</trackingNumber>' + '</DeviceCommandTrackingNumber>' ); }; this.devicePassthroughUpdateRequest = function(hub_id, mac, sensor_type, key, value) { var twonetId = undefined; var hubGeneration = this.getHubGeneration(hub_id); if (hubGeneration === 1) { twonetId = 'TWONETID'; } else if (hubGeneration === 2) { twonetId = 'TWONET2ID'; } return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DevicePassthroughs xmlns="' + this.xmlns + '">' + '<DevicePassthroughIdentifier>' + '<hub>' + '<HubId>' + hub_id + '</HubId>' + '<HubIdType>' + twonetId + '</HubIdType>' + '</hub>' + '<DeviceIdentifier>' + '<airInterfaceAndAddress>' + '<deviceAddress>' + mac + '</deviceAddress>' + '<airInterfaceType>' + sensor_type + '</airInterfaceType>' + '</airInterfaceAndAddress>' + '</DeviceIdentifier>' + '</DevicePassthroughIdentifier>' + '<Passthroughs>' + '<Passthrough>' + '<key>' + key + '</key>' + '<value>' + value + '</value>' + '</Passthrough>' + '</Passthroughs>' + '</DevicePassthroughs>' ); }; this.deviceCommandRequest = function(hub_id, mac, sensor_type, message) { return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<DeviceMessage xmlns="' + this.xmlns + '">' + '<deviceIdentifier>' + '<airInterfaceAndAddress>' + '<deviceAddress>' + mac + '</deviceAddress>' + '<airInterfaceType>' + sensor_type + '</airInterfaceType>' + '</airInterfaceAndAddress>' + '</deviceIdentifier>' + '<message>' + message + '</message>' + '<priority>true</priority>' + '</DeviceMessage>' ); }; } TwoNetAPI.prototype.changeConfig = function(region, env) { var configuration = config[region][env]; this.host_name = configuration.host_name; this.url_path = configuration.url_path; }; TwoNetAPI.prototype.getHubs = function(callback) { var api = this; var hubs = []; var hub_index = 0; var hub_call_count = 100; var record_range = function(start, count) { return('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<RecordRange xmlns="urn:com.twonet.sp.cuc.revisiony">' + '<startIndex>' + start + '</startIndex>' + '<numberOfRecords>' + count + '</numberOfRecords>' + '</RecordRange>'); } var do_work = function(status,result) { parser.parseString(result,function(err,jsonResult) { if(err) { logme.error('error: '+err); callback('Failed to parse results'); } else if( !jsonResult.HubByCustomerDetails ) { logme.error('failed to find hub results'); logme.inspect(jsonResult); } var total_hubs = parseInt(jsonResult.HubByCustomerDetails.totalHub[0],10); hubs = hubs.concat(jsonResult.HubByCustomerDetails.hubAndStatus); hub_index += hub_call_count; debug('fetching from hub list...'); if (hub_index <= total_hubs) { api.call('/hub/retrieveAll', 'POST', record_range(hub_index,hub_call_count), do_work); } else { var results = []; hubs.forEach(function(h) { results.push({ hub_type : h.hub[0].HubIdType[0], hub_id : h.hub[0].HubId[0], status : h.status[0] }); }); callback(0, hubs); } }); }; // initiate the API call for the first batch of hubs this.call('/hub/retrieveAll', 'POST', record_range(hub_index,hub_call_count), do_work); }; TwoNetAPI.prototype.getHub = function(hub_id, callback) { var that = this; async.parallel({ hub_details : function(done) { that.call('/hub/retrieve', 'POST', that.hubRequest(hub_id), function(status,details) { that.parseQCLResults(status,details, done); }); }, device_list : function(done) { that.call('/device/registeredDevices/retrieve', 'POST', that.hubDevicesRequest(hub_id,0,100),function(status,devices) { that.parseQCLResults(status,devices,done); }); } }, function(status, results) { if( status ) { logme.error('getHub() tasks failed : ' + status); callback(-1,[]); return; } var hub_details = {}; hub_details.HubDetails = results.hub_details.HubDetails; hub_details.DeviceByHubDetails = results.device_list.DeviceByHubDetails; callback(0,hub_details); }); }; TwoNetAPI.prototype.activateHub = function(hub_id, callback) { var that = this; this.call('/hub/activation', 'PUT', this.hubHardwareRequest(true, hub_id), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.deactivateHub = function(hub_id, callback) { var that = this; this.call('/hub/deactivation', 'PUT', this.hubHardwareRequest(false, hub_id), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.activateHubStatus = function(hub_id, callback) { var that = this; that.call('/hub/retrieve', 'POST', that.hubRequest(hub_id), function(status,details) { that.parseQCLResults(status,details,callback); }); }; TwoNetAPI.prototype.activateDevice = function(mac, sensor_type, callback) { var that = this; this.call('/device/activation', 'PUT', this.deviceIDRequest(mac, sensor_type), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.deactivateDevice = function(mac, sensor_type, callback) { var that = this; this.call('/device/deactivation', 'PUT', this.deviceIDRequest(mac, sensor_type), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.getDevice = function(mac, sensor_type, callback) { var that = this; this.call('/device/retrieve', 'POST', this.deviceIDRequest(mac, sensor_type), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.getDevices = function(model_name, callback) { var that = this; var devices = []; this.call('/device/model/device/retrieveAll', 'POST', this.deviceRequest(model_name), function(status,result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.createDevice = function(model_name, mac, sensor_type, callback) { var that = this; this.call('/device/create', 'PUT', this.createDeviceRequest(model_name, mac, sensor_type), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.registerDevice = function(hub_id, mac, sensor_type, callback) { var that = this; this.call('/device/register', 'POST', this.deviceRegisterRequest(hub_id, mac, sensor_type), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.associateDevice = function(action, hub_id, mac, sensor_type, callback) { var that = this; this.call('/hub/deviceAssociation/update', 'POST', this.deviceAssociationRequest(action.toUpperCase(), hub_id, mac, sensor_type), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.deviceCommand = function(command, mac, model_name, callback) { var that = this; this.call('/device/command', 'PUT', that.deviceMessageRequest(model_name, mac, command), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.deviceCommandStatus = function(command_id, callback) { var that = this; this.call('/device/command/status', 'POST', that.deviceCommandTrackingRequest(command_id), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.updateDevicePassthrough = function(hub_id, mac, sensor_type, key, value, callback) { var that = this; this.call('/device/passthroughs/update', 'POST', that.devicePassthroughUpdateRequest(hub_id, mac, sensor_type, key, value), function(status, result) { that.parseQCLResults(status, result, callback); }); }; TwoNetAPI.prototype.sendDeviceCommand = function(hub_id, mac, sensor_type, message, callback) { var that = this; this.call('/device/command', 'PUT', that.deviceCommandRequest(hub_id, mac, sensor_type, message), function(status, result) { that.parseQCLResults(status, result, callback); }); }; module.exports = TwoNetAPI;