Orvibo.prototype.prepareMessage = function(commandID, macAddress, macPadding, data, callback) { var args = Args([{ commandID: Args.STRING | Args.Required }, { macAddress: Args.STRING | Args.Optional, _default: "" }, { macPadding: Args.STRING | Args.Optional, _default: options.macPadding }, { data: Args.OBJECT | Args.Optional, _default: {} }, { callback: Args.FUNCTION | Args.Optional }], arguments); var packet, len var dataStr = "" // Go through all of data's properties and merges the values into a single string for (key in args.data) { dataStr += args.data[key] } // We need to define packet twice, because we can't determine the length of the string as we're building it. // So we build the string, count the length, then redefine the string, with the length we stored earlier. packet = options.magicWord + "0000" + args.commandID + args.macAddress + args.macPadding + dataStr packet = options.magicWord + _.padLeft((packet.length / 2).toString(16).toLowerCase(), 4, "0") + args.commandID + args.macAddress + args.macPadding + dataStr if (typeof args.callback !== "undefined") { args.callback(message) } return packet }
Orvibo.prototype.switchEndian = function(data) { var args = Args([{ data: Args.STRING | Args.Required }], arguments); return _.flatten(_.chunk(args.data, 2).reverse()).join("") }
function parallel() { const args = Args([ {arr: Args.ARRAY | Args.Required}, {func: Args.FUNCTION | Args.Optional}, {callback: Args.FUNCTION | Args.Required} ], arguments); const arrLength = args.arr.length; let results = {length: arrLength}; if (arrLength === 0) { args.callback(null, []); } else if (args.func) { args.arr.forEach(function (value, index) { args.func(value, parallelCallback(index)); }); } else { args.arr.forEach(function (func, index) { func(parallelCallback(index)); }); } function parallelCallback(index) { return function (error, data) { if (error) { args.callback(error, []); } else { results[String(index)] = data; if (Object.keys(results).length - 1 === results.length) { args.callback(error, Array.from(results, (v, k) => v)); } } }; } }
exports.forArgs = function(args) { return Args([ {conditions: Args.OBJECT}, {generator: Args.STRING | Args.Optional, _default: 'predicate'}, {placeholderOffset: Args.INT | Args.Optional, _default: 0} ], args); };
exports.glob = function(patterns, globOptions, outputDir, options, callback, updateCallback, removeCallback) { args = Args([ { patterns: Args.ANY | Args.Required }, { globOptions: Args.ANY | Args.Required }, { outputDir: Args.STRING | Args.Required }, { options: Args.OBJECT | Args.Optional, _default: {} }, { callback: Args.FUNCTION | Args.Optional, _default: function() {} }, { updateCallback: Args.FUNCTION | Args.Optional, _default: function() {} }, { removeCallback: Args.FUNCTION | Args.Optional, _default: function() {} } ], arguments) var patterns = args.patterns, globOptions = args.globOptions, outputDir = args.outputDir, options = args.options, callback = args.callback, updateCallback = args.updateCallback, removeCallback = args.removeCallback; mirrorGlob(patterns, globOptions, outputDir, function(inputFile, outputFile, extraFiles, cb) { var fileFunOptions = _.omit(options, [ 'sourceMapDir', 'watch', 'extension' ]); fileFunOptions.sourceMap = extraFiles.sourceMap; exports.file(inputFile, outputFile, fileFunOptions, cb); }, { extension: 'css', sourceMapDir: options.sourceMapDir, watch: options.watch }, callback, updateCallback, removeCallback); }
Orvibo.prototype.enterLearningMode = function(device) { var args = Args([{ device: Args.OBJECT | Args.Optional }, { callback: Args.FUNCTION | Args.Optional }], arguments); if (typeof args.device === "undefined") { debug("Putting all AllOnes into learning mode") this.devices.forEach(function(item) { if (item.type != "AllOne") { return } this.enterLearningMode(item) }.bind(this)) } else { if (args.device.type != "AllOne") { return } message = this.prepareMessage({ commandID: "6c73", macAddress: args.device.macAddress, macPadding: args.device.macPadding, data: { // Not sure what this data is for. I don't know what half of the data does, frankly! other: "010000000000" } }) this.sendMessage(message, args.device.ip) this.emit("learningmode", device) if (typeof args.callback !== "undefined") { args.callback(device) } } }
Orvibo.prototype.updateTimer = function(device, date, index, state, repeat) { var args = Args([{ device: Args.OBJECT | Args.Required }, { date: Args.DATE | Args.Required }, { index: Args.INT | Args.Optional, _default: -1 }, { state: Args.BOOLEAN | Args.Optional, // Should this be required instead? What about AllOne? _default: false }, { repeat: Args.STRING | Args.Optional, _default: "ff" }, { callback: Args.FUNCTION | Args.Optional }], arguments); this.addTimer({ device: args.device, date: args.date, index: args.index, state: args.state, update: true, repeat: args.repeat, }) }
Orvibo.prototype.modifyRemotePassword = function(device, oldpassword, newpassword) { var args = Args([{ device: Args.OBJECT | Args.Required }, { oldpassword: Args.STRING | Args.Optional, _check: function(ssid) { return ssid.length <= 12 }, _default: "888888" }, { newpassword: Args.STRING | Args.Optional, _default: "", _check: function(password) { return password.length <= 12 } }, { callback: Args.FUNCTION | Args.Optional }], arguments); message = this.prepareMessage({ macAddress: args.device.macAddress, macPadding: args.device.macPadding, commandID: "6d70", data: { unknown: "78ed40", recordlength: "0c", oldpassword: hex.hex(_.padRight(args.oldpassword, 12, " ")), newpassword: hex.hex(_.padRight(args.newpassword, 12, " ")) } }) this.sendMessage(message, args.device.ip) }
Orvibo.prototype.query = function(device, table, callback) { var args = Args([{ device: Args.OBJECT | Args.Optional }, { table: Args.STRING | Args.Optional, _default: "04" }, { callback: Args.FUNCTION | Args.Optional }], arguments); if (typeof args.device === "undefined") { debug("Subscribing to all devices") this.devices.forEach(function(item) { this.query(item) }.bind(this)) } else { message = this.prepareMessage({ commandID: "7274", macAddress: args.device.macAddress, macPadding: args.device.macPadding, data: { blank: "00000000", // There are two tables we're interested in, // Table 04 is neat info about the device, Table 03 // is timing data (e.g. turn socket on at 8pm etc.) table: args.table, blank2: "000000000000" } }) this.sendMessage(message, args.device.ip) } if (typeof args.callback !== "undefined") { args.callback(args.device, args.table) } }
Orvibo.prototype.discover = function(device, callback) { // args-js handles optional and required arguments, along with default values. // We use it here to determine if we need to append a MAC address to the discover packet var args = Args([{ device: Args.OBJECT | Args.Optional, _default: { macAddress: "", macPadding: "" } }, { callback: Args.FUNCTION | Args.Optional }], arguments); // prepareMessage is a helper function that puts together various info into // a ready-to-send packet. See the function definition further down for specifics message = this.prepareMessage({ commandID: "7161", macAddress: args.device.macAddress, macPadding: args.device.macPadding, }) debug("Discovery packet sent") // Tell any listeners that we've sent a discovery message this.emit("discoverysent", args.device) this.sendMessage(message, options.broadcastIP) if (typeof args.callback !== "undefined") { args.callback(args.device) } }
exports.findArgs = function(args){ return Args([ {conditions : Args.ANY | Args.Optional, _default : {}}, {options : Args.OBJECT | Args.Optional, _default : {}}, {next : Args.FUNCTION | Args.Optional, _default : function(err,res){ if(err) console.log(err) else console.log(res) }} ], args); }
Orvibo.prototype.addTimer = function(device, date, index, state, update, repeat) { var args = Args([{ device: Args.OBJECT | Args.Required }, { date: Args.DATE | Args.Required }, { index: Args.INT | Args.Optional, _default: -1 }, { state: Args.BOOLEAN | Args.Optional, // Should this be required instead? What about AllOne? _default: false }, { update: Args.BOOLEAN | Args.Optional, // Should this be required instead? What about AllOne? _default: false }, { repeat: Args.STRING | Args.Optional, _default: "ff" }, { callback: Args.FUNCTION | Args.Optional }], arguments); if (args.index == -1) { args.index = args.device.timers.length + 1 } args.date = moment(args.date) if (args.device.type == "Socket") { message = this.prepareMessage({ macAddress: args.device.macAddress, macPadding: args.device.macPadding, commandID: "746d", data: { blank: "00000000", table: "03", command: args.update ? "0001" : "0000", // 0000 = Create, 0001 = Update, 0002 = Delete unknown: "1c00", index: this.switchEndian(_.padLeft(args.index, 4, "0")), name: "6e756c6c202020202020202020202020", // null + padding. For an AllOne, I suspect this would be the name of an IR command or something? state: args.state ? "0100" : "0000", year: this.switchEndian(_.padLeft(args.date.year().toString(16), 4, "0")), month: this.switchEndian(_.padLeft((args.date.month() + 1).toString(16), 2, "0")), day: this.switchEndian(_.padLeft((args.date.day() + 1).toString(16), 2, "0")), hour: this.switchEndian(_.padLeft((args.date.hour() + 1).toString(16), 2, "0")), minute: this.switchEndian(_.padLeft((args.date.minute() + 1).toString(16), 2, "0")), second: this.switchEndian(_.padLeft((args.date.second() + 1).toString(16), 2, "0")), repeat: args.repeat } }) this.sendMessage(message, args.device.ip) } else { debug("NOT SOCKET") } }
exports.whereArgs = function(args){ var args = Args([ {where : Args.STRING | Args.Optional, _default : "1=1"}, {params : Args.ANY | Args.Optional, _default : []}, {next : Args.FUNCTION | Args.Optional, _default : function(err,res){ if(err) console.log(err) else console.log(res) }} ], args); if(!_.isArray(args.params)) args.params = [args.params]; return args; }
exports.queryArgs = function(args){ var args = Args([ {sql : Args.STRING | Args.Required}, {params : Args.ARRAY | Args.Optional, _default : []}, {options : Args.OBJECT | Args.Optional, _default : {single : false}}, {next : Args.FUNCTION | Args.Optional, _default : function(err,res){ if(err) console.log(err); else console.log(res); }} ], args); if(!_.isArray(args.params)) args.params = [args.params]; return args; }
Orvibo.prototype.getDevice = function(macAddress, callback) { var args = Args([{ macAddress: Args.STRING | Args.Required }], arguments); var device = _.where(this.devices, { macAddress: args.macAddress })[0] if (typeof callback !== "undefined") { args.callback(device) } return device }
exports.searchArgs = function(args, source){ args = Args([ {fields : Args.OBJECT | Args.Optional, _default: {}}, {options: Args.OBJECT | Args.Optional, _default: {}}, {next : Args.FUNCTION | Args.Optional, _default: function(err, res){ if(err) console.log(err); else console.log(res); }} ], args); args.options = new QueryOptions(args.options, source); return args; };
self.parseResource = function(str) { args = Args([ {str: Args.STRING | Args.Required}, ], arguments); var match = str.match('(.+) '+self.keyword+' (.+)'); if(match === null) { throw new Error('Invalid resource specification expected format: "<url> '+self.keyword+' <variable>"'); } return { url: match[1], variable: match[2] }; };
self.pipe = function pipe() { var args = arg([ {target: arg.OBJECT | arg.FUNCTION | arg.Required}, ], arguments); var p = _stream.promise; p.then(function(source){ if(typeof args.target === 'function') { return args.target(source); } return source.pipe(args.target); }); return p; };
schema.statics.findOneOrCreate = function findOneOrCreate() { var self = this; var args = Args([ {cond: Args.OBJECT | Args.Required}, {doc: Args.OBJECT | Args.Optional}, {callback: Args.FUNCTION | Args.Optional} ], arguments); return self.findOne(args.cond, function(err, result) { if (result) { args.callback(err, result); } else { self.create(args.doc || args.cond, args.callback); } }); };
Orvibo.prototype.hexToInt = function(data, switchendian) { var args = Args([{ data: Args.STRING | Args.Required }, { switchendian: Args.BOOLEAN | Args.Optional, _default: true }], arguments); if (args.switchendian) { args.data = this.switchEndian({ data: args.data }) } return parseInt(args.data, 16) }
exports.findArgs = function(args, source) { args = Args([ {conditions : Args.ANY | Args.Optional, _default : {}}, {options : Args.OBJECT | Args.Optional, _default: {}}, {next : Args.FUNCTION | Args.Optional, _default : function(err,res){ if(err) console.log(err); else console.log(res); }} ], args); if (_.isFunction(args.conditions)) { //this is our callback as the only argument, caught by Args.ANY args.next = args.conditions; } args.options = new QueryOptions(args.options, source); return args; };
function FeignRequestClient(){ var args = Args([ { defaults: Args.OBJECT | Args.Optional}, { debug: Args.BOOL | Args.Optional, _default: false}, { json: Args.BOOL | Args.Optional, _default: true} ], arguments); this.requestFn = request; this.requestFn.debug = args.debug; if (args.defaults){ this.requestFn = this.requestFn.defaults(args.defaults); } this.isJson = args.json; if (args.json){ this.requestFn = this.requestFn.defaults({json: true}); } }
FeignRequestClient.prototype._createRequestJsOptions = function(baseUrl, requestOptions, parameters){ var options = Args([ { method: Args.STRING | Args.Optional, _default: 'GET' }, { uri: Args.STRING | Args.Required} ], [requestOptions]); if (options.method == 'GET'){ options.qs = parameters; } else if (this.isJson) { if (parameters != null) options.body = parameters; } else { options.form = parameters; } options.baseUrl = baseUrl; return options; }
Orvibo.prototype.updateDevice = function(device, table, data) { var args = Args([{ device: Args.OBJECT | Args.Required }, { table: Args.STRING | Args.Optional, _default: "04" }, { data: Args.STRING | Args.Required, }, { callback: Args.FUNCTION | Args.Optional }], arguments); // 00000000 04 00018a0001004325accfdeadbeef202020202020efbeaddecfac202020202020383838383838202020202020446561646c7920426565662020202020040010000000090000000500000010272a796fd01027766963656e7465722e6f727669626f2e636f6d20202020202020202020202020202020202020202019001600002c0a80101ffffff000100000a00000 this.prepareMessage({ macAddress: args.device.macAddress, macPadding: args.device.macPadding, commandID: "746d", blank: "00000000", table: args.table || "04" }) }
Orvibo.prototype.extractRecords = function(data, lengthcount, littleendian) { var args = Args([{ data: Args.STRING | Args.Required }, { lengthcount: Args.INT | Args.Optional, _default: 2 }, { littleendian: Args.BOOLEAN | Args.Optional, _default: true }], arguments); var res = [] debug("Extracting records from %s", data) // Only interested in looping while we have data while (args.data != "") { debug("Data left: %s", data) if (args.littleendian) { num = parseInt(_.flatten(_.chunk(args.data.substr(0, args.lengthcount * 2), 2).reverse()).join(""), 16) * 2 } else { num = parseInt(args.data.substr(0, args.lengthcount * 2), 16) * 2 } // Get the first two bytes and make it into a number. Spin it all around because these numbers are little endian // If the number of bytes to get (factoring in 2 bytes for the length) is less than our remaining data, if (num + args.lengthcount * 2 <= args.data.length) { debug("Pushing %s to the array", args.data.substr(args.lengthcount * 2, num)) // Shove it onto our array res.push(args.data.substr(args.lengthcount * 2, num)) // And reset `data` to be the data, less our already-extracted record args.data = args.data.substr(num + args.lengthcount * 2) } else { // If we've got more bytes to grab than there is available, stop args.data = "" } } // Return what we've got. debug("Extracted %d records", res.length) return res }
Orvibo.prototype.deleteTimer = function(device, index) { var args = Args([{ device: Args.OBJECT | Args.Required }, { index: Args.INT | Args.Optional, _default: -1 }, { callback: Args.FUNCTION | Args.Optional }], arguments); this.prepareMessage({ macAddress: args.device.macAddress, macPadding: args.device.macPadding, commandID: "746d", data: { extra: "0000000003", command: "0002", index: this.switchEndian(_.padLeft(args.index, 4, "0")), } }) this.sendMessage(message, args.device.ip) }
Orvibo.prototype.setState = function(device, state, callback) { var args = Args([{ device: Args.OBJECT | Args.Required }, { state: Args.BOOL | Args.Required }, { callback: Args.FUNCTION | Args.Optional }], arguments); if (args.device.type != "Socket") { return } // Sets our device's state property to our new value. args.device.state = args.state message = this.prepareMessage({ commandID: "6463", macAddress: args.device.macAddress, macPadding: args.device.macPadding, data: { // Session ID? blank: "00000000", // Ternary operators are cool, but hard to read. // This one says "if state is true, set state to 01, otherwise, set to 00" state: args.state ? "01" : "00" } }) // This is a misnomer. addDevice also updates devices if they exist this.addDevice(args.device) this.emit("setstate", args.device, args.state) this.sendMessage(message, args.device.ip) if (typeof args.callback !== "undefined") { args.callback(args.device, args.state) } }
Orvibo.prototype.sendMessage = function(message, address, port, sock, callback) { var args = Args([{ message: Args.STRING | Args.Required }, { address: Args.STRING | Args.Required }, { sock: Args.OBJECT | Args.Optional, _default: socket }, { port: Args.INT | Args.Optional, _default: options.port }], arguments); args.message = new Buffer(args.message.toLowerCase(), "hex"); // We need to send as a buffer. this line takes our message and makes it into one. args.sock.send(args.message, 0, args.message.length, args.port, args.address, function(err, bytes) { // Send the message. Parameter 2 is offset, so it's 0. if (err) throw err; // Error? CRASH AND BURN BB! debug("Message sent to %s:%s with length %d", args.address, args.port, args.message.length) this.emit("messageSent", args.message, args.address, args.sock.address().address, args.sock); // Tell the world we've sent a packet. Include message, who it's being sent to, plus the address it's being sent from }.bind(this)); // Again, we do .bind(this) so calling this.emit(); comes from OrviboAllOne, and not from scktClient if (typeof callback !== "undefined") { args.callback(message, address, port) } }
Orvibo.prototype.subscribe = function(device, callback) { var args = Args([{ device: Args.OBJECT | Args.Optional, }, { callback: Args.FUNCTION | Args.Optional }], arguments); if (typeof args.device === "undefined") { debug("Subscribing to all devices") this.devices.forEach(function(item) { this.subscribe(item) }.bind(this)) } else { debug("Subscribing to", device.macAddress) // prepareMessage is used a little differently here. // anything within "data" is flattened into a single string (so order matters!) // "macReversed" could be called "jslkdf" -- it doesn't matter in the end message = this.prepareMessage({ commandID: "636c", macAddress: args.device.macAddress, macPadding: args.device.macPadding, data: { // This takes a MAC address, splits it up into chunks of 2 (so [A, C], [C, F] etc.) // then reverses the chunks (so it becomes [C, F], [A, C]), flattens the array, // then finally joins it together as one long string. Phew! macReversed: this.switchEndian(args.device.macAddress), macPadding: args.device.macPadding } }) this.sendMessage(message, device.ip) } if (typeof args.callback !== "undefined") { args.callback(args.device) } }
Orvibo.prototype.emitRF = function(device, sessionID, state, rfkey, rfid, callback) { var args = Args([{ device: Args.OBJECT | Args.Required }, { sessionID: Args.STRING | Args.Optional, _default: "00000000" }, { state: Args.BOOL | Args.Required }, { rfkey: Args.STRING | Args.Optional, _default: "2a00" }, { rfid: Args.STRING | Args.Required }, { callback: Args.FUNCTION | Args.Optional }], arguments); message = this.prepareMessage({ commandID: "6463", macAddress: args.device.macAddress, macPadding: args.device.macPadding, data: { sessionID: args.sessionID, randomA: _.padLeft(Math.floor((Math.random() * 255)).toString(16), 2, "0"), randomB: _.padLeft(Math.floor((Math.random() * 255)).toString(16), 2, "0"), state: args.state ? "01" : "00", rfkey: args.rfkey, rfid: args.rfid } }) this.sendMessage(message, args.device.ip) this.emit("rfemitted", args.device, args.sessionID, args.state, args.rfkey, args.rfid) if (typeof args.callback !== "undefined") { args.callback(args.device, args.sessionID, args.state, args.rfkey, args.rfid) } }