UNPKG

flower-power-ble

Version:

node.js/BLE lib to comminucate with Parrot Flower Power devices

236 lines (189 loc) 8.08 kB
var FlowerPower = require('../index') , dgram = require('dgram') , os = require('os') ; var devices = {}; FlowerPower.discover(function(peripheral) { var uuid; uuid = peripheral.uuid; if (!devices[uuid]) devices[uuid] = { uuid: uuid, name: peripheral.name, props: {}, peripheral: peripheral }; devices[uuid].state = 'discovered'; devices[uuid].props.lastSample = new Date().getTime(); peripheral.on('disconnect', function() { var device; device = devices[uuid]; device.state = 'disconnected'; device.props.lastSample = new Date().getTime(); tsrp(uuid); }).connect(onconnect(uuid)); }); var onconnect = function(uuid) { return function(err) { didconnect(err, uuid); }; }; var didconnect = function(err, uuid) { var device = devices[uuid] , peripheral = device.peripheral; if (!!err) return console.log(device.name + ': ' + err.message); device.state = 'connected'; device.props.rssi = peripheral._peripheral.rssi; device.props.lastSample = new Date().getTime(); peripheral.discoverServicesAndCharacteristics(function() { var i; if (!device.serialNumber) { return peripheral.readSerialNumber(function(serialNumber) { device.serialNumber = serialNumber; peripheral.readFriendlyName(function(friendlyName) { device.friendlyName = friendlyName; return peripheral.disconnect(); }); }); } i = 4; peripheral.readBatteryLevel(function(batteryLevel) { device.props.batteryLevel = batteryLevel; if (--i === 0) return peripheral.disconnect(); device.props.lastSample = new Date().getTime(); }); peripheral.readSunlight(function(sunlight) { // sunlight is PPF (photons per square meter), convert to lux // according to http://www.apogeeinstruments.com/conversion-ppf-to-lux/ device.props.light = sunlight * 54; if (--i === 0) return peripheral.disconnect(); device.props.lastSample = new Date().getTime(); }); peripheral.readTemperature(function(temperatureC, temperatureF) {/* jshint unused: false */ var antoine; antoine = 8.07131 - (1730.63 / (233.426 + temperatureC)); device.props.temperature = temperatureC; device.lastPressure = Math.pow(10, antoine - 2); if (--i === 0) return peripheral.disconnect(); device.props.lastSample = new Date().getTime(); }); peripheral.readSoilMoisture(function(relativeHumidity) { // moisture = relativeHumidity * (saturationVaporPressure(temperatureC) / 100); if (device.lastPressure) device.props.moisture = relativeHumidity * device.lastPressure; if (--i === 0) return peripheral.disconnect(); device.props.lastSample = new Date().getTime(); }); }); }; setInterval(function() { var device, now, uuid; now = new Date().getTime(); for (uuid in devices) { if (!devices.hasOwnProperty(uuid)) continue; device = devices[uuid]; if ((device.state !== 'disconnected') || (!!device.remote)) continue; device.peripheral.connect(onconnect(uuid)); } }, 60 * 60 * 1000); setInterval(function() { var device, now, uuid; now = new Date().getTime(); for (uuid in devices) { if (!devices.hasOwnProperty(uuid)) continue; device = devices[uuid]; if ((device.state !== 'disconnected') || (!!device.remote)) continue; if (!!device.serialNumber) { tsrp(uuid); continue; } console.log('retry serialNumber: ' + uuid + ' (rssi ' + device.props.rssi + ')'); device.peripheral.connect(onconnect(uuid)); } }, 45 * 1000); var ipaddr = '224.192.32.20' , portno = 22601 , requestID = Math.round(Math.random() * Math.pow(2, 31)) - 1 , socket ; var ifApply = function(cb) { var ifa, ifaddrs, ifname, ifaces; ifaces = os.networkInterfaces(); for (ifname in ifaces) { if (!ifaces.hasOwnProperty(ifname)) continue; ifaddrs = ifaces[ifname]; for (ifa = 0; ifa < ifaddrs.length; ifa++) { if ((!ifaddrs[ifa].internal) && (ifaddrs[ifa].family === 'IPv4')) cb(ifname, ifaddrs[ifa].address); } } }; ifApply(function(ifname, ifaddr) { var ifsock = dgram.createSocket('udp4'); ifsock.on('message', function(message, rinfo) { var device, deviceType, i, instance, remote, report, rssi, thing, uuid; /* console.log('>>> rinfo: ' + JSON.stringify(rinfo)); console.log('>>> message: ' + message.toString()); */ try { report = JSON.parse(message); } catch(ex) { return console.log('TSRP error: ' + ex.message); } if ((!report.requestID) || (report.path !== '/api/v1/thing/reporting')) return; for (deviceType in report.things) { if (!report.things.hasOwnProperty(deviceType)) continue; thing = report.things[deviceType]; if (!thing.instances) continue; for (i = 0; i < thing.instances.length; i++) { instance = thing.instances[i]; if ((!instance.unit) || (!instance.unit.udn) || (instance.unit.udn.indexOf('uuid:') !== 0) || (!instance.info) || (isNaN(instance.info.rssi))) continue; rssi = instance.info.rssi; uuid = instance.unit.udn.substring(5); for (device in devices) { if ((!devices.hasOwnProperty(device)) || (device !== uuid)) continue; remote = devices[device].remote; if ((!!remote) && ((remote.rssi < rssi) || ((remote.rssi === rssi) && (remote.requestID < report.requestID)))) { devices[device].remote = { rinfo: rinfo, requestID: report.requestID, rssi: rssi }; } devices[device].remote.nextUpdate = new Date().getTime() + (60 * 1000 * 1000); if ((device.props.rssi > device.remote.rssi) || (requestID > device.remote.requestID)) delete(device.remote); } } } }).on('listening', function() { console.log('listening on ' + ifname + ' for multicast udp://' + ipaddr + ':' + portno); this.addMembership(ipaddr); }).on('error', function(err) { console.log('socket error: ' + err.message); }).bind(portno, ifaddr); if (!socket) socket = ifsock; }); var tsrp = function(uuid) { var device, json, packet; device = devices[uuid]; if (!device.serialNumber) return; json = { path : '/api/v1/thing/reporting' , requestID : requestID.toString() , things : { '/device/climate/flower-power/plant' : { prototype : { device : { name : 'Flower Power' , maker : 'Parrot' } , name : true , status : [ 'recent', 'absent' ] , properties : { 'temperature' : 'celsius' , 'light' : 'lux' , 'moisture' : 'millibars' , 'batteryLevel' : 'percentage' , 'rssi' : 's8' } } , instances : [ { name : device.friendlyName || device.name , status : 'recent' , unit : { 'serial' : device.serialNumber , 'udn' : 'uuid:' + device.uuid } , info : device.props } ] } } }; requestID++; packet = new Buffer(JSON.stringify(json)); socket.send(packet, 0, packet.length, portno, ipaddr, function(err, octets) {/* jshint unused: false */ if (!!err) console.log('socket.send: ' + err.message); }); };