UNPKG

bleacon-fork

Version:

A Node.js library for creating, discovering, and configuring iBeacons

356 lines (267 loc) 11 kB
var events = require('events'); var util = require('util'); var debug = require('debug')('bleu-station'); var noble = require('@abandonware/noble'); var DEVICE_NAME_UUID = '2a00'; var MANUFACTURER_NAME_UUID = '2a29'; var MODEL_NUMBER_UUID = '2a24'; var HARDWARE_REVISION_UUID = '2a27'; var FIRMWARE_REVISION_UUID = '2a26'; var IBEACON_UUID_UUID = 'b0702881a295a8abf734031a98a512de'; var IBEACON_MAJOR_UUID = 'b0702882a295a8abf734031a98a512de'; var IBEACON_MINOR_UUID = 'b0702883a295a8abf734031a98a512de'; var IBEACON_MEASURED_TX_POWER_UUID = 'b0702884a295a8abf734031a98a512de'; var ADMIN_IBEACON_UUID_UUID = 'c8f21a07078a42df86600946ffd109be'; var ADMIN_IBEACON_MAJOR_UUID = '677ec16a743d42fcafe1d9f4a02a726f'; var ADMIN_IBEACON_MINOR_UUID = '7722712a07f4433f8e305a6dc26356ba'; var ADMIN_TX_POWER_UUID = '8aa2414e8e614d9cae14508ee3192dee'; var ADMIN_TX_POWER_CAL_UUID = '0b4700c35c5346519601b7e1e06b1bbf'; var ADMIN_DEVICE_NAME_UUID = '980ac81e94fd43f48b9a260a65dd3adc'; var ADMIN_PASSWORD_UUID = '12d8cca8b1cc4e48abf7767b5e0f3ff6'; var LATITUDE_UUID = 'fffc3dbb92d148f1aa5289a3d9517d79'; var LONGITUDE_UUID = '9e5f3adf337f4acfb54d929da486f512'; var VERSION_NUMBER_UUID = '676e5ff15cd7484ab98f97f3c96e6361'; var URL_1_UUID = '98a5a965efd34d16924718fd88bb8a30'; var URL_2_UUID = '3e8501d8aa3943368908b6e79d81a050'; var BleuStation = function(peripheral) { this._peripheral = peripheral; this._services = {}; this._characteristics = {}; this.uuid = peripheral.uuid; this.name = peripheral.advertisement.localName; this._peripheral.on('disconnect', this.onDisconnect.bind(this)); }; util.inherits(BleuStation, events.EventEmitter); BleuStation.is = function(peripheral) { var localName = peripheral.advertisement.localName; return (localName && localName.length === 8 && localName.indexOf('TC') === 0) || (localName === undefined && peripheral.advertisement.manufacturerData); }; BleuStation.discover = function(callback) { var startScanningOnPowerOn = function() { if (noble.state === 'poweredOn') { var onDiscover = function(peripheral) { if (!BleuStation.is(peripheral)) { return; } noble.removeListener('discover', onDiscover); noble.stopScanning(); var bleuStation = new BleuStation(peripheral); callback(bleuStation); }; noble.on('discover', onDiscover); noble.startScanning([], true); } else { noble.once('stateChange', startScanningOnPowerOn); } }; startScanningOnPowerOn(); }; BleuStation.prototype.toString = function() { return JSON.stringify({ uuid: this.uuid, name: this.name }); }; BleuStation.prototype.onDisconnect = function() { this.emit('disconnect'); }; BleuStation.prototype.connect = function(callback) { this._peripheral.connect(callback); }; BleuStation.prototype.disconnect = function(callback) { this._peripheral.disconnect(callback); }; BleuStation.prototype.discoverServicesAndCharacteristics = function(callback) { this._peripheral.discoverAllServicesAndCharacteristics(function(error, services, characteristics) { if (error === null) { for (var i in services) { var service = services[i]; this._services[service.uuid] = service; } for (var j in characteristics) { var characteristic = characteristics[j]; this._characteristics[characteristic.uuid] = characteristic; } } callback(error); }.bind(this)); }; BleuStation.prototype.readDataCharacteristic = function(uuid, callback) { this._characteristics[uuid].read(function(error, data) { callback(data); }); }; BleuStation.prototype.readStringCharacteristic = function(uuid, callback) { this.readDataCharacteristic(uuid, function(data) { if (data[0] === data.length - 1) { data = data.slice(1); } callback(data.toString()); }); }; BleuStation.prototype.readUInt16Characteristic = function(uuid, callback) { this.readDataCharacteristic(uuid, function(data) { callback(data.readUInt16BE(0)); }); }; BleuStation.prototype.readInt8Characteristic = function(uuid, callback) { this.readDataCharacteristic(uuid, function(data) { callback(data.readInt8(0)); }); }; BleuStation.prototype.readUInt8Characteristic = function(uuid, callback) { this.readDataCharacteristic(uuid, function(data) { callback(data.readUInt8(0)); }); }; BleuStation.prototype.readDoubleCharacteristic = function(uuid, callback) { this.readDataCharacteristic(uuid, function(data) { callback(data.readDoubleLE(0)); }); }; BleuStation.prototype.writeDataCharacteristic = function(uuid, data, callback) { this._characteristics[uuid].write(data, false, callback); }; BleuStation.prototype.writeStringCharacteristic = function(uuid, value, callback) { var valueLength = value.length; var data = new Buffer(valueLength + 1); var valueData = new Buffer(value); data[0] = valueLength; for (var i = 0; i < valueLength; i++) { data[i + 1] = valueData[i]; } this.writeDataCharacteristic(uuid, data, callback); }; BleuStation.prototype.writeUInt16Characteristic = function(uuid, value, callback) { var data = new Buffer(2); data.writeUInt16BE(value, 0); this.writeDataCharacteristic(uuid, data, callback); }; BleuStation.prototype.writeInt8Characteristic = function(uuid, value, callback) { var data = new Buffer(1); data.writeInt8(value, 0); this.writeDataCharacteristic(uuid, data, callback); }; BleuStation.prototype.writeUInt8Characteristic = function(uuid, value, callback) { var data = new Buffer(1); data.writeUInt8(value, 0); this.writeDataCharacteristic(uuid, data, callback); }; BleuStation.prototype.writeDoubleCharacteristic = function(uuid, value, callback) { var data = new Buffer(8); data.writeDoubleLE(value, 0); this.writeDataCharacteristic(uuid, data, callback); }; BleuStation.prototype.login = function(password, callback) { this._characteristics[ADMIN_PASSWORD_UUID].discoverDescriptors(function(error, descriptors) { var clientCharacteristicConfigurationDescriptor = null; for (var i in descriptors) { var descriptor = descriptors[i]; if (descriptor.uuid === '2902') { clientCharacteristicConfigurationDescriptor = descriptor; break; } } clientCharacteristicConfigurationDescriptor.writeValue(new Buffer('0000', 'hex'), function(error) { this.writeAdminPassword(password, function(result) { callback(result === 0); }.bind(this)); }.bind(this)); }.bind(this)); }; BleuStation.prototype.readDeviceName = function(callback) { this.readStringCharacteristic(DEVICE_NAME_UUID, callback); }; BleuStation.prototype.readManufacturerName = function(callback) { this.readStringCharacteristic(MANUFACTURER_NAME_UUID, callback); }; BleuStation.prototype.readModelNumber = function(callback) { this.readStringCharacteristic(MODEL_NUMBER_UUID, callback); }; BleuStation.prototype.readHardwareRevision = function(callback) { this.readStringCharacteristic(HARDWARE_REVISION_UUID, callback); }; BleuStation.prototype.readFirmwareRevision = function(callback) { this.readStringCharacteristic(FIRMWARE_REVISION_UUID, callback); }; BleuStation.prototype.readUuid = function(callback) { this.readDataCharacteristic(IBEACON_UUID_UUID, function(data) { callback(data.toString('hex')); }); }; BleuStation.prototype.readMajor = function(callback) { this.readUInt16Characteristic(IBEACON_MAJOR_UUID, callback); }; BleuStation.prototype.readMinor = function(callback) { this.readUInt16Characteristic(IBEACON_MINOR_UUID, callback); }; BleuStation.prototype.readTxPower = function(callback) { this.readUInt8Characteristic(ADMIN_TX_POWER_UUID, function(value) { var txPower = value * 5 + 25; callback(txPower); }.bind(this)); }; BleuStation.prototype.readMeasuredTxPower = function(callback) { this.readInt8Characteristic(IBEACON_MEASURED_TX_POWER_UUID, callback); }; BleuStation.prototype.readAdminDeviceName = function(callback) { this.readStringCharacteristic(ADMIN_DEVICE_NAME_UUID, callback); }; BleuStation.prototype.writeUuid = function(uuid, callback) { this.writeDataCharacteristic(ADMIN_IBEACON_UUID_UUID, new Buffer(uuid, 'hex'), callback); }; BleuStation.prototype.writeMajor = function(major, callback) { this.writeUInt16Characteristic(ADMIN_IBEACON_MAJOR_UUID, major, callback); }; BleuStation.prototype.writeMinor = function(minor, callback) { this.writeUInt16Characteristic(ADMIN_IBEACON_MINOR_UUID, minor, callback); }; BleuStation.prototype.writeTxPower = function(txPower, callback) { if (txPower > 100) { txPower = 100; } else if (txPower < 25) { txPower = 25; } var value = Math.ceil((txPower - 25) / 5); this.writeUInt8Characteristic(ADMIN_TX_POWER_UUID, value, callback); }; BleuStation.prototype.writeMeasuredTxPower = function(measureTxPower, callback) { this.writeInt8Characteristic(ADMIN_TX_POWER_CAL_UUID, measureTxPower, callback); }; BleuStation.prototype.writeAdminDeviceName = function(deviceName, callback) { this.writeStringCharacteristic(ADMIN_DEVICE_NAME_UUID, deviceName, callback); }; BleuStation.prototype.writeAdminPassword = function(password, callback) { this.writeStringCharacteristic(ADMIN_PASSWORD_UUID, password, function() { this.readUInt8Characteristic(ADMIN_PASSWORD_UUID, callback); }.bind(this)); }; BleuStation.prototype.readLatitude = function(callback) { this.readDoubleCharacteristic(LATITUDE_UUID, callback); }; BleuStation.prototype.writeLatitude = function(latitude, callback) { this.writeDoubleCharacteristic(LATITUDE_UUID, latitude, callback); }; BleuStation.prototype.readLongitude = function(callback) { this.readDoubleCharacteristic(LONGITUDE_UUID, callback); }; BleuStation.prototype.writeLongitude = function(longitude, callback) { this.writeDoubleCharacteristic(LONGITUDE_UUID, longitude, callback); }; BleuStation.prototype.readVersionNumber = function(callback) { this.readInt8Characteristic(VERSION_NUMBER_UUID, callback); }; BleuStation.prototype.readURL1 = function(callback) { this.readStringCharacteristic(URL_1_UUID, callback); }; BleuStation.prototype.writeURL1 = function(url1, callback) { this.writeStringCharacteristic(URL_1_UUID, url1, callback); }; BleuStation.prototype.readURL2 = function(callback) { this.readStringCharacteristic(URL_2_UUID, callback); }; BleuStation.prototype.writeURL2 = function(url2, callback) { this.writeStringCharacteristic(URL_2_UUID, url2, callback); }; module.exports = BleuStation;