cdif
Version:
Common device interconnect framework
316 lines (283 loc) • 11.6 kB
JavaScript
var events = require('events');
var util = require('util');
var uuid = require('uuid');
var CdifDevice = require('cdif-device');
var deviceDB = require('./device-db');
var DeviceAuth = require('./device-auth');
var validator = require('./validator');
var logger = require('./logger');
var OAuthDevice = require('./oauth/oauth');
var CdifError = require('./error').CdifError;
var DeviceError = require('./error').DeviceError;
function DeviceManager(mm) {
this.deviceMap = {};
this.deviceAuth = new DeviceAuth();
this.moduleManager = mm;
this.moduleManager.on('deviceonline', this.onDeviceOnline.bind(this));
this.moduleManager.on('deviceoffline', this.onDeviceOffline.bind(this));
this.moduleManager.on('purgedevice', this.onPurgeDevice.bind(this));
this.on('discoverall', this.onDiscoverAll.bind(this));
this.on('stopdiscoverall', this.onStopDiscoverAll.bind(this));
this.on('devicelist', this.onGetDiscoveredDeviceList.bind(this));
this.on('connect', this.onConnectDevice.bind(this));
this.on('disconnect', this.onDisconnectDevice.bind(this));
this.on('invokeaction', this.onInvokeDeviceAction.bind(this));
this.on('getspec', this.onGetDeviceSpec.bind(this));
this.on('devicestate', this.onGetDeviceState.bind(this));
this.on('subscribe', this.onEventSubscribe.bind(this));
this.on('unsubscribe', this.onEventUnsubscribe.bind(this));
this.on('getschema', this.onGetDeviceSchema.bind(this));
this.on('setoauthtoken', this.onSetDeviceOAuthAccessToken.bind(this));
this.on('getrooturl', this.onGetDeviceRootUrl.bind(this));
}
util.inherits(DeviceManager, events.EventEmitter);
DeviceManager.prototype.onDeviceOnline = function(cdifDevice, m) {
var _this = this;
if (cdifDevice.oauth_version === '1.0' || cdifDevice.oauth_version === '2.0') {
var oauth = new OAuthDevice(cdifDevice);
oauth.createOAuthDevice();
}
if (this.checkDeviceInterface(cdifDevice) === false) return;
validator.validateDeviceSpec(cdifDevice.spec, function(error) {
if (error) {
return logger.error(error.message + ', device spec: ' + JSON.stringify(cdifDevice.spec));
}
if (m == null) {
return logger.error('unknown module for device: ' + cdifDevice.spec.device.friendlyName);
}
cdifDevice.getHWAddress(function(err, addr) {
var hwAddr;
var deviceUUID;
if (!err) {
hwAddr = addr;
deviceDB.getDeviceUUIDFromHWAddr(hwAddr, function(err, data) {
if (err) {
return logger.error(err);
}
if (!data) {
deviceUUID = uuid.v4();
deviceDB.setDeviceUUID(hwAddr, deviceUUID, function(err) {
if (err) {
logger.error('cannot insert address record for device:' + deviceUUID);
}
});
} else {
deviceUUID = data.uuid;
}
deviceDB.setSpecForDevice(hwAddr, JSON.stringify(cdifDevice.spec));
// TODO: handle device offline and purge dead devices
cdifDevice.module = m;
cdifDevice.auth = _this.deviceAuth;
cdifDevice.hwAddr = hwAddr;
cdifDevice.deviceID = deviceUUID;
cdifDevice.online = true;
//it could return a new device object instance with initial states here
//module install route rely on this to create new device object
logger.info('new device online: ' + cdifDevice.deviceID);
_this.deviceMap[deviceUUID] = cdifDevice;
});
} else {
logger.error('cannot get HW address for device: ' + cdifDevice.spec.device.friendlyName);
}
});
});
};
// for now this is not triggered
DeviceManager.prototype.onDeviceOffline = function(cdifDevice, m) {
logger.error('device offline: ' + cdifDevice.deviceID);
cdifDevice.online = false;
};
// purge all device objects which are managed by the unloaded module
DeviceManager.prototype.onPurgeDevice = function(m) {
for (var deviceID in this.deviceMap) {
if (this.deviceMap[deviceID].module === m) {
delete this.deviceMap[deviceID];
logger.info('device purged: ' + deviceID);
}
}
};
DeviceManager.prototype.onDiscoverAll = function(callback) {
this.moduleManager.discoverAllDevices();
callback(null);
};
DeviceManager.prototype.onStopDiscoverAll = function(callback) {
this.moduleManager.stopDiscoverAllDevices();
callback(null);
};
DeviceManager.prototype.onGetDiscoveredDeviceList = function(callback) {
var deviceList = {};
for (var i in this.deviceMap) {
var cdifDevice = this.deviceMap[i];
if (cdifDevice.spec) {
//this is ugly but the easiest way to handle this request
var desc = JSON.parse(JSON.stringify(cdifDevice.spec));
desc.device.serviceList = {};
deviceList[i] = desc;
}
}
callback(null, deviceList);
};
DeviceManager.prototype.ensureDeviceState = function(deviceID, token, callback) {
var cdifDevice = this.deviceMap[deviceID];
if (cdifDevice == null) { // check null or undefined
return callback(new CdifError('device not found: ' + deviceID));
}
if (cdifDevice.module.discoverState === 'discovering') {
return callback(new CdifError('in discovering'));
}
// if (cdifDevice.connectionState !== 'connected') {
// return callback(new CdifError('device not connected'));
// }
if (cdifDevice.online === false) {
return callback(new CdifError('device offlined'));
}
if (cdifDevice.spec.device.userAuth === true) {
// make sure this is sync
this.deviceAuth.verifyAccess(cdifDevice, cdifDevice.secret, token, callback);
} else {
callback(null);
}
};
DeviceManager.prototype.onConnectDevice = function(cdifDevice, user, pass, session) {
var _this = this;
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
try {
device.connect(user, pass, function(err, secret, redirectObj) {
if (this.expired === true) return;
var sess = this.session;
sess.clearDeviceTimer(this);
if (err) {
return sess.callback(err, null);
}
if (secret) {
// FIXME: this brings in 'user' from context which could be an issue, bring in from session
_this.deviceAuth.generateToken(user, secret, function(err, token) {
if (err) {
return sess.callback(new CdifError('cannot generate access token'), null);
}
if (redirectObj) {
return sess.callback(null, {'device_access_token': token, 'url_redirect': redirectObj});
} else {
return sess.callback(null, {'device_access_token': token});
}
});
} else {
if (redirectObj != null) {
return sess.callback(null, {'url_redirect': redirectObj});
} else {
return sess.callback(null, null);
}
}
//FIXME: do not emit presentation event more than once, this brings in from context
if (device.spec.device.devicePresentation === true) {
_this.emit('presentation', cdifDevice.deviceID);
}
}.bind(timer));
} catch (e) {
if (timer.expired === true) return;
this.clearDeviceTimer(timer);
return this.callback(new DeviceError(e.message), null);
}
}.bind(session));
};
DeviceManager.prototype.onDisconnectDevice = function(cdifDevice, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
try {
device.disconnect(function(err) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err);
}.bind(timer));
} catch (e) {
if (timer.expired === true) return;
this.clearDeviceTimer(timer);
return this.callback(new DeviceError(e.message), null);
}
}.bind(session));
};
DeviceManager.prototype.onInvokeDeviceAction = function(cdifDevice, serviceID, actionName, args, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
try {
device.deviceControl(serviceID, actionName, args, function(err, data) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err, data);
}.bind(timer));
} catch (e) {
if (timer.expired === true) return;
this.clearDeviceTimer(timer);
return this.callback(new DeviceError(e.message), null); //framework won't throw
}
}.bind(session));
};
DeviceManager.prototype.onGetDeviceSpec = function(cdifDevice, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
device.getDeviceSpec(function(err, data) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err, data);
}.bind(timer));
}.bind(session));
};
DeviceManager.prototype.onGetDeviceState = function(cdifDevice, serviceID, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
device.getServiceStates(serviceID, function(err, data) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err, data);
}.bind(timer));
}.bind(session));
};
DeviceManager.prototype.onEventSubscribe = function(subscriber, cdifDevice, serviceID, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
device.subscribeDeviceEvent(subscriber, serviceID, function(err) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err);
}.bind(timer));
}.bind(session));
};
DeviceManager.prototype.onEventUnsubscribe = function(subscriber, cdifDevice, serviceID, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
device.unSubscribeDeviceEvent(subscriber, serviceID, function(err) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err);
}.bind(timer));
}.bind(session));
};
DeviceManager.prototype.onGetDeviceSchema = function(cdifDevice, path, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
device.resolveSchemaFromPath(path, null, function(err, self, data) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err, data);
}.bind(timer));
}.bind(session));
};
DeviceManager.prototype.onSetDeviceOAuthAccessToken = function(cdifDevice, params, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
device.setOAuthAccessToken(params, function(err) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err);
}.bind(timer));
}.bind(session));
};
DeviceManager.prototype.onGetDeviceRootUrl = function(cdifDevice, session) {
session.setDeviceTimer(cdifDevice, function(error, device, timer) {
device.getDeviceRootUrl(function(err, data) {
if (this.expired === true) return;
this.session.clearDeviceTimer(this);
return this.session.callback(err, data);
}.bind(timer));
}.bind(session));
};
DeviceManager.prototype.checkDeviceInterface = function(cdifDevice) {
if (!(cdifDevice instanceof CdifDevice)) {
logger.error('object is not an instance of CDIF device');
return false;
}
return true;
};
module.exports = DeviceManager;