node-red-contrib-iot-virtual-device
Version:
A set of nodes to help you create simulated IoT devices connected to IBM Watson IoT Platform.
216 lines (185 loc) • 6.21 kB
JavaScript
module.exports = exports = deviceManager = function(RED){
cache = {};
//var Proxy = require('harmony-proxy'); wait till it will be by default in node.js
evEmiter = null;
var _ = require("underscore");
var chance = require("chance").Chance();
deviceManager.addSchema = function(schema){
ensureSchemaConfig(schema);
var schemaEvt = deviceManager.getSimpleSchema(schema);
schemaEvt.topic = "deviceSchema";
evEmiter.emit('schemaAdded', schemaEvt);
};
deviceManager.removeSchema = function(schema){
if(cache[schema.deviceType] && cache[schema.deviceType].schema)
delete cache[schema.deviceType];
};
deviceManager.getSchema = function(deviceType){
return (cache[deviceType]) ? cache[deviceType].schema : null;
};
deviceManager.getAllSchemas = function(simpleForm){
var schemas = [];
_.each(cache, function(schemaConfig){
if(schemaConfig.schema)
schemas.push( (simpleForm)? deviceManager.getSimpleSchema(schemaConfig.schema) : schemaConfig.schema);
});
return schemas;
};
deviceManager.getSimpleSchema = function(schema){
var simSchema = {deviceType: schema.deviceType, properties: [], events: []};
_.each(schema.props, function(prp){
simSchema.properties.push(prp.name);
});
_.each(schema.evts, function(evt){
simSchema.events.push(evt.name);
});
return simSchema;
};
deviceManager.getDevice = function(deviceType, deviceId){
if(!deviceId || !cache[deviceType])
return;
return cache[deviceType].runningDevices[deviceId];
};
deviceManager.getDevicebyId = function(deviceId){
for (var deviceType in cache) {
var device = cache[deviceType].runningDevices[deviceId];
if(device) return device;
}
};
deviceManager.removeDevice = function(device){
if(!device || !cache[device.deviceType])
return;
if(cache[device.deviceType].runningDevices[device.deviceId]){
delete cache[device.deviceType].runningDevices[device.deviceId];
//emit delete event
var deviceClone = JSON.parse(JSON.stringify(device));//clone device
deviceClone.topic = "deviceDeleted";
evEmiter.emit('deviceDeleted', deviceClone);
evEmiter.emit('deviceDeleted-'+ device.deviceType, deviceClone);
evEmiter.emit('deviceDeleted-'+ device.deviceType + "-" + device.deviceId, deviceClone);
}
};
deviceManager.createDevice = function(deviceId, schema, msg){
ensureSchemaConfig(schema);
var device = {
deviceId: deviceId,
deviceType: schema.deviceType,
properties: {}
};
_.each(schema.props, function (schemaPrp){
var value = null;
if(msg && msg.payload && msg.payload.properties && msg.payload.properties[schemaPrp.name]){//value from payload
value = msg.payload.properties[schemaPrp.name];
}
else{// default value
if(schemaPrp.defaultValue){
value = getTypedValue(schemaPrp.defaultValue.type, schemaPrp.defaultValue.value);
}
else if(schemaPrp.random){
try {
value = chance[schemaPrp.random.func].apply(chance, [schemaPrp.random.args]);
} catch (e) {
RED.log.error("Cannot set default value");
}
}
}
var sendPrpChangeEvent = function(oldValue , newValue){
var propEvt = JSON.parse(JSON.stringify(device));//clone device
propEvt.changedProperty = {name: schemaPrp.name, oldValue: oldValue, newValue: newValue};
propEvt.topic = "propertyChange";
evEmiter.emit('propertyChange', propEvt);
evEmiter.emit('propertyChange-'+device.deviceType, propEvt);
evEmiter.emit('propertyChange-'+device.deviceType + "-" + schemaPrp.name, propEvt);
evEmiter.emit('propertyChange-'+device.deviceType + "-" + schemaPrp.name + "-" + device.deviceId, propEvt);
};
var prp = value;
var touch = function(){
sendPrpChangeEvent(prp, prp);
};
if(_.isObject(prp)){
prp.touch = touch;
}
Object.defineProperty(device.properties, schemaPrp.name, {
configurable: true,
enumerable: true,
get: function() { return prp; },
set: function(val) {
if(prp === val) return;
if(prp.touch === touch && _.isObject(val)){
val.touch = touch;
}
var oldValue = prp;
prp = val;
sendPrpChangeEvent(oldValue, val);
}
});
});
if(cache[schema.deviceType].runningDevices[device.deviceId]){
throw "device " + device.deviceId + " already exist";
}
cache[schema.deviceType].runningDevices[device.deviceId] = device;
var createEvt = JSON.parse(JSON.stringify(device));//clone device
createEvt.topic = "deviceCreated";
evEmiter.emit('deviceCreated', createEvt);
evEmiter.emit('deviceCreated-'+ device.deviceType, createEvt);
evEmiter.emit('deviceCreated-'+ device.deviceType + "-" + device.deviceId, createEvt);
return device;
};
function getTypedValue(type, value){
var val = null;
switch (type) {
case "date":
val = Date.now();
break;
case "num":
val = Number(value);
break;
case "bool":
val = /^true$/i.test(value);
break;
case "json":
try {
val = JSON.parse(value);
} catch(e) {
RED.log.error("invalid json");
}
break;
default:
val = value;
}
return val;
}
function ensureSchemaConfig(schema){
if(cache[schema.deviceType] === undefined){
cache[schema.deviceType] = {
schema: schema,
runningDevices: {}
};
}
//config without active schema
else if(cache[schema.deviceType].schema === undefined){
cache[schema.deviceType].schema = schema;
}
else if(cache[schema.deviceType].schema.id !== schema.id){
throw "More then one schema for device Type";
}
}
function init(server,redSettings) {
var events = require("events");
var ev = new events.EventEmitter();
evEmiter = new events.EventEmitter();
RED.httpAdmin.get('/simulatedDevice/js/*', function(req, res){
var options = {
root: __dirname + '/../static/',
dotfiles: 'deny'
};
res.sendFile(req.params[0], options);
});
}
inited = false;
if (!inited) {
inited = true;
init(RED.server, RED.settings);
}
deviceManager.events = evEmiter;
};