UNPKG

irrelon-presence

Version:

A Node.js bluetooth presence scanner. Uses bluetooth to detect devices in range of the bluetooth antenna.

226 lines (185 loc) 5.81 kB
var btwatch = require('btwatch'), Emitter = require('irrelon-emitter'), fs = require('fs'), spawn = require('child_process').spawn, inRange = {}; var Presence = function () { // On startup, read our DB of mac addresses we should scan for try { this.macArr = require('./devices.json'); } catch (e) { this.macArr = []; } }; Emitter(Presence); /** * Starts watching for all devices that have been added to * the watch list in the past. */ Presence.prototype.startWatch = function () { var self = this, i; if (!this.started) { this.started = true; console.log(new Date().toUTCString() + ' :: Started watching'); btwatch.on('change', function (inRange, macAddress) { if (inRange) { inRange[macAddress] = true; console.log(new Date().toUTCString() + ' :: %s is now in range', macAddress); self.emit('found', macAddress); } else { inRange[macAddress] = false; console.log(new Date().toUTCString() + ' :: %s no longer in range', macAddress); self.emit('lost', macAddress); } }); for (i = 0; i < this.macArr.length; i++) { console.log('Watching ' + this.macArr[i].name + ' (' + this.macArr[i].mac + ')'); btwatch.watch(this.macArr[i].mac); } } else { console.log(new Date().toUTCString() + ' :: Already started watching!'); } }; /** * Stops watching for devices. */ Presence.prototype.stopWatch = function () { var i; if (this.started) { this.started = false; for (i = 0; i < this.macArr.length; i++) { console.log('Stopping watch on ' + this.macArr[i].name + ' (' + this.macArr[i].mac + ')'); btwatch.unwatch(this.macArr[i].mac); } console.log(new Date().toUTCString() + ' :: Stopped watching'); } else { console.log(new Date().toUTCString() + ' :: Cannot stop watching as we haven\'t started yet!'); } }; /** * Scans for all discoverable bluetooth devices. * @param {Function} callback The callback method to call once the scan has * finished. */ Presence.prototype.scan = function (callback) { console.log(new Date().toUTCString() + ' :: Starting device scan...'); var hci = spawn('hcitool', ['scan']); var result = ''; var arr = []; hci.stdout.on('data', function (data) { result += data; }); hci.on('close', function () { var mac, name, hit, regexp = /([0-9A-Fa-f:]{17})(\t*?)(.*?)\n/g; while((hit = regexp.exec(result)) !== null) { //console.log(hit); if (hit && hit[0]) { mac = hit[1]; name = hit[3].trim(); arr.push({ name: name, mac: mac }); console.log(new Date().toUTCString() + ' :: Device found: ' + name + ' (' + mac + ')'); } } console.log(new Date().toUTCString() + ' :: Device scan finished'); callback(false, arr); }); hci.on('error', function (err) { console.log('err', err); console.log(new Date().toUTCString() + ' :: Device scan finished'); callback(err, arr); }); }; Presence.prototype.pair = function (interfaceNum, mac, channel, callback) { if (mac) { var cmd = spawn('rfcomm', ['connect', interfaceNum, mac, channel]), result = ''; cmd.stdout.on('data', function (data) { result += data; }); cmd.on('close', function () { console.log(new Date().toUTCString() + ' :: Pairing finished'); callback(false, JSON.stringify(result)); }); cmd.on('error', function (err) { console.log('err', err); console.log(new Date().toUTCString() + ' :: Pairing finished'); callback(err, JSON.stringify(result)); }); } }; /** * Adds a device to the watch list via it's mac address. * @param {String} mac The mac address in hex format of the * device to watch for. * @param {String} name The name of the device, used when * outputting human-readable info in the logs. */ Presence.prototype.addWatch = function (mac, name) { console.log(new Date().toUTCString() + ' :: Added device to watch list: ' + name + ' (' + mac + ')'); this.macArr.push({mac: mac, name: name}); if (this.started) { btwatch.watch(mac); } }; /** * Removes a device from the watch list via it's mac address. * @param {String} mac The mac address of a device that has been * previously registered via addWatch(). */ Presence.prototype.removeWatch = function (mac) { var i, recordIndex = -1; // Find the record for (i = 0 ; i < this.macArr.length; i++) { if (this.macArr[i].mac === mac) { // Found record recordIndex = i; break; } } if (recordIndex > -1) { // Remove the watch from the stack if (this.started) { btwatch.unwatch(mac); } this.macArr.splice(recordIndex, 1); } else { // No match found! console.log(new Date().toUTCString() + ' :: Could not remove device from watch list as it was not found (' + mac + ')'); } }; /** * Returns an array of devices that are currently * on the watch list. */ Presence.prototype.devices = function () { return JSON.parse(JSON.stringify(this.macArr)); }; /** * Returns a boolean denoting if the device with the passed * mac address is currently in range or not. * @param {String} mac The mac address of the device to check * for. * @returns {boolean} True if in range, false if not. */ Presence.prototype.inRange = function (mac) { return Boolean(inRange[mac]); }; /** * Saves the current watch list to storage so that when * Presence is loaded next time around, the devices added * will be loaded into the watch list automatically. */ Presence.prototype.saveData = function () { fs.writeFile('./devices.json', JSON.stringify(this.macArr), function () { console.log(new Date().toUTCString() + ' :: Device data saved'); }); }; module.exports = Presence;