homespun-grovepi
Version:
This is a Node.js module that reads sensors residing on a [Raspberry Pi](https://www.raspberrypi.org) and a [GrovePi+ Shield](http://www.dexterindustries.com/shop/grovepi-board/) and transcodes them to [TSRP](http://thethingsystem.com/dev/Thing-Sensor-Rep
233 lines (195 loc) • 8.02 kB
JavaScript
/*
___ usage: en_US ___
usage: node homespun.js [options] file
options:
-h, --help display this message
-c, --config name configuration name
description:
homespun will monitor any sensors connected to your GrovePi and generate TSRP messages for node-homework
___ usage ___
*/
var r2c, requestID, runloop, tsrp
, cadence = require('cadence/redux')
, crypto = require('crypto')
, dgram = require('dgram')
, fs = require('fs')
, underscore = require('underscore')
, util = require('util')
, uuid = require('node-uuid')
, GrovePi = require('node-grovepi').GrovePi
require('cadence/loops')
GrovePi.sensors.TemperatureDigital = {}
underscore.keys(GrovePi.sensors.DHTDigital.VERSION).forEach(function (moduleType) {
GrovePi.sensors.TemperatureDigital[moduleType] = function (pin) {
GrovePi.sensors.DHTDigital.apply(this, [ pin
, GrovePi.sensors.DHTDigital.VERSION[moduleType]
, GrovePi.sensors.DHTDigital.CELSIUS ])
}
GrovePi.sensors.TemperatureDigital[moduleType].prototype = new GrovePi.sensors.DHTDigital()
})
r2c =
{ lowP : function (v) { return (!v) }
, highP : function (v) { return (!!v) }
, maxP : function (v) { return (v >= 1023) }
, cm : function (v) { return (v / 100.0) }
, pct : function (v) { return (v / 1024.0) }
, epsilon : function (v) { return ((v * 4.95) / 1023.0) }
, v : function (v) { return v }
, 'v[0]' : function (v) { return v[0] }
, 'v[1]' : function (v) { return v[1] }
, 'v[1]%' : function (v) { if (v[1] < 1) return 0.0
if (v[1] >= 100) return 1.0
return (v[1] / 100.0)
}
, pH : function (v) { return (7 - 1000 * (v - 372) * 4.95 / (59.16 * 1023)) }
, resistpct : function (v) { if (v < 1) return 0.0
v = (1023 - v) * (10 / v)
if (v >= 100) return 1.0
return (v / 100.0)
}
, uv : function (v) { return ((v * 4980) / (1023 * 200)) }
}
requestID = 1
runloop = cadence(function (async, sensor) {
var suffix
, bootTime = new Date().getTime()
, properties = {}
, root = GrovePi.sensors
sensor.type.split('.').forEach(function (component) { root = root[component] })
sensor.sensor = new root(sensor.pin)
suffix = '/device/sensor/grovepi/' + sensor.sensorTag
sensor.template = {
path : '/api/v1/thing/reporting'
, requestID : '1'
, things :
{
}
}
underscore.keys(sensor.properties).forEach(function (key) {
properties[key] = sensor.properties[key][0]
})
sensor.template.things[suffix] = {
prototype :
{ device :
{ name : sensor.sensorType || 'Grove sensor'
, maker : sensor.sensorMaker || 'Dexter Industries / Seeed Studio'
, model :
{ number : sensor.modelNumber
}
}
, name : true
, status : [ 'present', 'absent', 'recent' ]
, properties : properties
}
, instances :
[ { name : sensor.name
, status : 'present'
, unit :
{ serial : sensor.udn
, udn : sensor.udn
}
, info : { }
, uptime : 0
} ]
}
async(function () {
setTimeout(async(), 5 * 1000)
}, function () {
var data
, value = sensor.sensor.read()
if ((Buffer.isBuffer(value)) && (value.length === 1)) value = value[0]
sensor.template.requestID = requestID++
sensor.template.things[suffix].instances[0].info = {}
underscore.keys(sensor.properties).forEach(function (key) {
var f = r2c[sensor.properties[key][1]]
if (!!f) sensor.template.things[suffix].instances[0].info[key] = f(value)
})
sensor.template.things[suffix].instances[0].uptime = new Date().getTime() - bootTime
data = new Buffer(JSON.stringify(sensor.template))
tsrp.send(data, 0, data.length, 22601, '224.0.9.1', async())
}, function (bytes) {/* jshint unused: false */
})()
})
require('arguable/executable')(module, cadence(function (async, options) {
var config, name, path
, logger = require('prolific').createLogger('homespun')
if (options.params.help) return console.log(options._usage.message)
if (options.params.config) {
name = options.argv[0]
path = options.argv[1]
} else {
path = options.argv[0]
}
if (!path) path = __dirname + '/config.json'
async(function () {
fs.readFile(path, 'utf8', async())
}, function (body) {
var i
, json = JSON.parse(body)
, keys = underscore.keys(json)
if (keys.length === 0) throw new Error('empty configuration file')
if (!name) name = underscore.keys(json)[0]
config = json[name]
if (!config) throw new Error('configuration name \"' + name + '\" is undefined')
i = 0
config.sensors.forEach(function (sensor) {
var root = GrovePi.sensors
if ((!sensor.name) || (!sensor.type) || (isNaN(sensor.pin)) || (!sensor.properties)
|| (!sensor.sensorType) || (!sensor.sensorTag) || (!sensor.modelNumber)) {
console.log(util.inspect(sensor, { depth : null }))
throw new Error('incomplete specification for sensor #' + i)
}
sensor.type.split('.').forEach(function (component) {
if (!root[component]) {
console.log(util.inspect(sensor, { depth : null }))
throw new Error('invalid type for sensor #' + i)
}
root = root[component]
})
if (typeof sensor.properties !== 'object') {
console.log(util.inspect(sensor, { depth : null }))
throw new Error('invalid properties for sensor #' + i)
}
underscore.keys(sensor.properties).forEach(function (key) {
var property = sensor.properties[key]
if ((!util.isArray(property)) || (!r2c[property[1]])) {
console.log(util.inspect(sensor, { depth : null }))
throw new Error('invalid property ' + key + ' for sensor #' + i)
}
})
i++
if (!!sensor.udn) return
async(function () {
crypto.randomBytes(16, async())
}, function (bytes) {
sensor.udn = uuid.v4(bytes)
fs.writeFile(path, JSON.stringify(json, null, 2), async())
})
})
}, function () {
tsrp = dgram.createSocket('udp4')
tsrp.bind(0, async())
}, function () {
tsrp.addMembership('224.0.9.1');
tsrp.setMulticastLoopback(true)
tsrp.setMulticastTTL(10)
}, function () {
var initP, loop
, board = new GrovePi.board({
debug : true
, onError : function (err) { throw err }
, onInit : function (/* firstP */) { initP = true }
})
board.init()
loop = async(function () {
if (!!initP) return [ loop ]
setTimeout(async(), 100)
})()
logger.info('initialized', { version : board.version() })
config.sensors.forEach(function (sensor) {
runloop(sensor, function (err) {
if (!!err) logger.error('initialize', { sensor : sensor.name, message : err.message })
})
})
})
}))