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
JavaScript
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;