UNPKG

homespun

Version:

This is the root of the homespun family of repositories: you run a server in your home that collects sensor readings and uploads them to the management cloud of your choice. At present, upload to only one cloud is available: [numerous](http://numerousapp.

209 lines (159 loc) 6.98 kB
var cadence = require('cadence/redux') , dgram = require('dgram') , snmp = require('snmp-native') , path = require('path') , underscore = require('underscore') , util = require('util') , Driver = require(path.join(__dirname, 'prototype-driver.js')) require('cadence/loops') // jscs:disable requireMultipleVarDecl var SNMP = function (config, services) { Driver.call(this, config, services) } var oidI = function (s) { return underscore.map(s.split('.'), function (n) { return +n }) } var oidS = function (b) { return underscore.map(b, function (n) { return n.toString() }).join('.') } var sysObjectIDs = { serverscheck : '1.3.6.1.4.1.17095' } // jscs:enable requireMultipleVarDecl util.inherits(SNMP, Driver); SNMP.prototype.initialize = cadence(function (async) {/* jshint unused: false */ var self = this this.stopP = false this.socket = dgram.createSocket('udp4').on('error', function (err) { self.logger.error('initialize', { event : 'createSocket', err : err.message }) console.log(err.stack) }).on('message', function (buffer, rinfo) { var bindings, dispatcher, packet packet = snmp.parse(buffer) if ((!packet.pdu) || (packet.pdu.type !== 2) || (packet.pdu.error !== 0) || (!util.isArray(packet.pdu.varbinds)) || (packet.pdu.varbinds.length !== 3)) return bindings = packet.pdu.varbinds if (bindings[1].type !== 6) return dispatcher = self.dispatch[oidS(bindings[1].value)] if (!dispatcher) return dispatcher.bind(self)(rinfo, bindings, function (err) { if (!err) return self.logger.error('initialize' , { event : 'dispatcher' , err : err.message , rinfo : rinfo }) console.log(err.stack) }) }).on('listening', function () { var data, packet self.logger.info('initialize' , { message : 'SNMP listening on broadcast udp://*' + ':' + this.address().port }) this.setBroadcast(true) this.setTTL(10) self.ping.bind(self)() self.timer = setInterval(self.ping.bind(self), 30 * 1000) }) this.socket.bind(0) }) SNMP.prototype.ping = function () { var data, packet if (this.stopP) return packet = new snmp.Packet() // sysDescr.0, sysObjectID.0, and sysName.0 packet.pdu.varbinds[0].oid = oidI('1.3.6.1.2.1.1.1.0') packet.pdu.varbinds[1] = underscore.extend({}, packet.pdu.varbinds[0] , { oid : oidI('1.3.6.1.2.1.1.2.0') } ) packet.pdu.varbinds[2] = underscore.extend({}, packet.pdu.varbinds[0] , { oid : oidI('1.3.6.1.2.1.1.5.0') } ) data = snmp.encode(packet) this.socket.send(data, 0, data.length, 161, '255.255.255.255', function (err, bytes) {/* jshint unused: false */ if (!err) return this.logger.error('initialize', { event : 'send', err : err.message }) console.log(err.stack) }.bind(this)) } SNMP.prototype.finalize = cadence(function (async) {/* jshint unused: false */ this.props.status = 'finishing' this.stopP = true this.socket.close() }) SNMP.prototype.dispatch = {} SNMP.prototype.dispatch[sysObjectIDs.serverscheck] = cadence(function (async, rinfo, bindings) { var properties, sensor, session, uuid session = new snmp.Session({ host : rinfo.address }) async(function () { properties = {} session.getSubtree({ oid : oidI(sysObjectIDs.serverscheck + '.3') }, async()) }, function (varbinds) { var name name = '-' varbinds.forEach(function (varbind) { var leaf = varbind.oid[varbind.oid.length - 2] if ((leaf % 4) === 1) name = varbind.value else if ((leaf % 4) === 2) { underscore.extend(properties, this.normalize(name, varbind.value)) } }.bind(this)) session.getSubtree({ oid : oidI(sysObjectIDs.serverscheck + '.11') }, async()) }, function (varbinds) { var capabilities, names names = {} varbinds.forEach(function (varbind) { var leaf = varbind.oid[varbind.oid.length - 2] , subtree = varbind.oid[varbind.oid.length - 3] if (leaf === 1) names[subtree] = varbind.value else if ((leaf === 2) && (!!names[subtree])) { underscore.extend(properties, this.normalize(names[subtree], varbind.value)) } }.bind(this)) // TODO: use MAC address + rinfo.port uuid = rinfo.address + ':' + rinfo.port sensor = this.sensors[uuid] || {} sensor.name = bindings[2].value sensor.lastReading = {} capabilities = { fields : [] } underscore.keys(properties).forEach(function (key) { capabilities.fields.push(this.sensorType(key)) sensor.lastReading[key] = properties[key] }.bind(this)) async(function () { if (!!this.sensors[uuid]) return this.register(this, sensor.name, uuid, capabilities, async()) }, function (sensorID) { if (sensorID === false) return if (!!sensorID) { this.sensors[uuid] = underscore.extend(sensor, { sensorID : sensorID }) } this.upsync(this, sensor.sensorID, sensor.lastReading, async()) }) }) }) /* name: Int. Temp/Ext. Temp | Airflow | Sound Meter | Humidity | Water Detect | Dust Sensor temperature | airflow | noise | humidity | liquid_detected | particles.2_5 units: 'C' | m/s | dB | RH-% * 100 | boolean | mg/m3 datapoints: '26.51' | '0.01' | '48.93' | '36.82' / 100 | 'DRY' | '0.01' */ SNMP.prototype.normalize = function (name, value) { var f, key key = { Airflow : 'airflow' , 'Dust Sensor' : 'particles.2_5' , 'Ext. Temp' : 'temperature' , Humidity : 'humidity' , 'Int. Temp' : 'temperature' , 'Sound Meter' : 'noise' , 'Water Detect' : 'liquid_detect' }[name] if (!key) return f = { airflow : function () { return parseFloat(value) } , humidity : function () { return (parseFloat(value) / 100) } , liquid_detect : function () { return (value !== 'DRY') } , noise : function () { return parseFloat(value) } , 'particles.2_5' : function () { return parseFloat(value) } , temperature : function () { return parseFloat(value) } }[key] if (!f) return return underscore.object([ key ], [ f() ]) } module.exports = SNMP