Comparing version 0.0.10 to 0.0.11
/* @license | ||
* | ||
* BLE Abstraction Tool: chromeos adapter | ||
* Version: 0.0.4 | ||
* Version: 0.0.5 | ||
* | ||
@@ -36,3 +36,3 @@ * The MIT License (MIT) | ||
// Node. Does not work with strict CommonJS | ||
module.exports = factory(root, require('bleat')); | ||
module.exports = factory(root, require('./bleat.core')); | ||
} else { | ||
@@ -67,19 +67,21 @@ // Browser globals with support for web workers (root is window) | ||
chrome.bluetooth.getAdapterState(checkForError(errorFn, function(adapterInfo) { | ||
chrome.bluetooth.onDeviceAdded.addListener(function(deviceInfo) { | ||
if (this.foundFn) { | ||
var device = new bleat.Device(deviceInfo.address, deviceInfo.name, deviceInfo.uuids || []); | ||
this.foundFn(device); | ||
} | ||
}); | ||
chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(function(characteristicInfo) { | ||
if (typeof this.charNotifies[characteristicInfo.instanceId] === "function") { | ||
this.charNotifies[characteristicInfo.instanceId](characteristicInfo.value); | ||
delete this.charNotifies[characteristicInfo.instanceId]; | ||
} | ||
}); | ||
chrome.bluetooth.onDeviceRemoved.addListener(this.checkDisconnect); | ||
chrome.bluetooth.onDeviceChanged.addListener(function(deviceInfo) { | ||
if (deviceInfo.connected === false) checkDisconnect(deviceInfo); | ||
}); | ||
if (adapterInfo.available) readyFn(); | ||
if (adapterInfo.available) { | ||
chrome.bluetooth.onDeviceAdded.addListener(function(deviceInfo) { | ||
if (this.foundFn) { | ||
var device = new bleat.Device(deviceInfo.address, deviceInfo.name, deviceInfo.uuids || []); | ||
this.foundFn(device); | ||
} | ||
}); | ||
chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(function(characteristicInfo) { | ||
if (typeof this.charNotifies[characteristicInfo.instanceId] === "function") { | ||
this.charNotifies[characteristicInfo.instanceId](characteristicInfo.value); | ||
delete this.charNotifies[characteristicInfo.instanceId]; | ||
} | ||
}); | ||
chrome.bluetooth.onDeviceRemoved.addListener(this.checkDisconnect); | ||
chrome.bluetooth.onDeviceChanged.addListener(function(deviceInfo) { | ||
if (deviceInfo.connected === false) checkDisconnect(deviceInfo); | ||
}); | ||
readyFn(); | ||
} | ||
else errorFn("adapter not enabled"); | ||
@@ -86,0 +88,0 @@ })); |
/* @license | ||
* | ||
* BLE Abstraction Tool: evothings adapter | ||
* Version: 0.0.5 | ||
* Version: 0.0.6 | ||
* | ||
@@ -36,3 +36,3 @@ * The MIT License (MIT) | ||
// Node. Does not work with strict CommonJS | ||
module.exports = factory(root, require('bleat')); | ||
module.exports = factory(root, require('./bleat.core')); | ||
} else { | ||
@@ -39,0 +39,0 @@ // Browser globals with support for web workers (root is window) |
/* @license | ||
* | ||
* BLE Abstraction Tool | ||
* Version: 0.0.10 | ||
* BLE Abstraction Tool: core functionality | ||
* Version: 0.0.11 | ||
* | ||
@@ -161,2 +161,604 @@ * The MIT License (MIT) | ||
}; | ||
})); | ||
/* @license | ||
* | ||
* BLE Abstraction Tool: chromeos adapter | ||
* Version: 0.0.5 | ||
* | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Rob Moran | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
// https://github.com/umdjs/umd | ||
(function (root, factory) { | ||
if (typeof define === 'function' && define.amd) { | ||
// AMD. Register as an anonymous module. | ||
define(['bleat'], factory.bind(this, root)); | ||
} else if (typeof exports === 'object') { | ||
// Node. Does not work with strict CommonJS | ||
module.exports = factory(root, require('./bleat.core')); | ||
} else { | ||
// Browser globals with support for web workers (root is window) | ||
factory(root, root.bleat); | ||
} | ||
}(this, function(root, bleat) { | ||
"use strict"; | ||
function checkForError(errorFn, continueFn) { | ||
return function() { | ||
if (chrome.runtime.lastError) { | ||
errorFn(chrome.runtime.lastError.message); | ||
return; | ||
} | ||
if (typeof continueFn === "function") { | ||
var args = [].slice.call(arguments); | ||
continueFn.apply(this, args); | ||
} | ||
}; | ||
} | ||
// https://developer.chrome.com/apps/bluetoothLowEnergy | ||
if (root.chrome && root.chrome.bluetooth && root.chrome.bluetoothLowEnergy) { | ||
bleat.addAdapter("chromeos", { | ||
charNotifies: {}, | ||
deviceDisconnects: {}, | ||
foundFn: null, | ||
init: function(readyFn, errorFn) { | ||
chrome.bluetooth.getAdapterState(checkForError(errorFn, function(adapterInfo) { | ||
if (adapterInfo.available) { | ||
chrome.bluetooth.onDeviceAdded.addListener(function(deviceInfo) { | ||
if (this.foundFn) { | ||
var device = new bleat.Device(deviceInfo.address, deviceInfo.name, deviceInfo.uuids || []); | ||
this.foundFn(device); | ||
} | ||
}); | ||
chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(function(characteristicInfo) { | ||
if (typeof this.charNotifies[characteristicInfo.instanceId] === "function") { | ||
this.charNotifies[characteristicInfo.instanceId](characteristicInfo.value); | ||
delete this.charNotifies[characteristicInfo.instanceId]; | ||
} | ||
}); | ||
chrome.bluetooth.onDeviceRemoved.addListener(this.checkDisconnect); | ||
chrome.bluetooth.onDeviceChanged.addListener(function(deviceInfo) { | ||
if (deviceInfo.connected === false) checkDisconnect(deviceInfo); | ||
}); | ||
readyFn(); | ||
} | ||
else errorFn("adapter not enabled"); | ||
})); | ||
}, | ||
checkDisconnect: function(deviceInfo) { | ||
if (typeof this.deviceDisconnects[deviceInfo.address] === "function") { | ||
this.deviceDisconnects[deviceInfo.address](); | ||
delete this.deviceDisconnects[deviceInfo.address]; | ||
} | ||
}, | ||
scan: function(foundFn, errorFn) { | ||
chrome.bluetooth.getDevices(checkForError(errorFn, function(devices) { | ||
devices.forEach(function(deviceInfo) { | ||
var device = new bleat.Device(deviceInfo.address, deviceInfo.name, deviceInfo.uuids || []); | ||
foundFn(device); | ||
}); | ||
})); | ||
this.foundFn = foundFn; | ||
chrome.bluetooth.startDiscovery(checkForError(errorFn)); | ||
}, | ||
stop: function(errorFn) { | ||
this.foundFn = null; | ||
chrome.bluetooth.stopDiscovery(checkForError(errorFn)); | ||
}, | ||
connect: function(device, connectFn, disconnectFn, errorFn) { | ||
chrome.bluetoothLowEnergy.connect(device.address, null, checkForError(errorFn, function() { | ||
this.deviceDisconnects[device.address] = function() { | ||
device.connected = false; | ||
device.services = {}; | ||
disconnectFn(); | ||
}; | ||
device.connected = true; | ||
chrome.bluetoothLowEnergy.getServices(device.address, checkForError(errorFn, function(services) { | ||
services.forEach(function(serviceInfo) { | ||
var service = new bleat.Service(serviceInfo.uuid, serviceInfo.instanceId, serviceInfo.isPrimary); | ||
device.services[service.uuid] = service; | ||
chrome.bluetoothLowEnergy.getCharacteristics(service.handle, checkForError(errorFn, function(characteristics) { | ||
characteristics.forEach(function(characteristicInfo) { | ||
var characteristic = new bleat.Characteristic(characteristicInfo.uuid, characteristicInfo.instanceId, characteristicInfo.properties); | ||
service.characteristics[characteristic.uuid] = characteristic; | ||
chrome.bluetoothLowEnergy.getDescriptors(characteristic.handle, checkForError(errorFn, function(descriptors) { | ||
descriptors.forEach(function(descriptorInfo) { | ||
var descriptor = new bleat.Descriptor(descriptorInfo.uuid, descriptorInfo.instanceId); | ||
characteristic.descriptors[descriptor.uuid] = descriptor; | ||
}); | ||
})); | ||
}); | ||
})); | ||
}); | ||
setTimeout(connectFn, 0); | ||
})); | ||
})); | ||
}, | ||
disconnect: function(device, errorFn) { | ||
chrome.bluetoothLowEnergy.disconnect(device.address, checkForError(errorFn)); | ||
}, | ||
readCharacteristic: function(characteristic, completeFn, errorFn) { | ||
chrome.bluetoothLowEnergy.readCharacteristicValue(characteristic.handle, checkForError(errorFn, function(characteristicInfo) { | ||
completeFn(characteristicInfo.value); | ||
})); | ||
}, | ||
writeCharacteristic: function(characteristic, bufferView, completeFn, errorFn) { | ||
chrome.bluetoothLowEnergy.writeCharacteristicValue(characteristic.handle, bufferView.buffer, checkForError(errorFn, completeFn)); | ||
}, | ||
enableNotify: function(characteristic, notifyFn, completeFn, errorFn) { | ||
chrome.bluetoothLowEnergy.startCharacteristicNotifications(characteristic.handle, null, checkForError(errorFn, function() { | ||
this.charNotifies[characteristic.handle] = notifyFn; | ||
completeFn(); | ||
})); | ||
}, | ||
disableNotify: function(characteristic, completeFn, errorFn) { | ||
chrome.bluetoothLowEnergy.stopCharacteristicNotifications(characteristic.handle, checkForError(errorFn, function() { | ||
if (this.charNotifies[characteristic.handle]) delete this.charNotifies[characteristic.handle]; | ||
completeFn(); | ||
})); | ||
}, | ||
readDescriptor: function(descriptor, completeFn, errorFn) { | ||
chrome.bluetoothLowEnergy.readDescriptorValue(descriptor.handle, checkForError(errorFn, function(descriptorInfo) { | ||
completeFn(descriptorInfo.value); | ||
})); | ||
}, | ||
writeDescriptor: function(descriptor, bufferView, completeFn, errorFn) { | ||
chrome.bluetoothLowEnergy.writeDescriptorValue(descriptor.handle, bufferView.buffer, checkForError(errorFn, completeFn)); | ||
} | ||
}); | ||
} | ||
})); | ||
/* @license | ||
* | ||
* BLE Abstraction Tool: evothings adapter | ||
* Version: 0.0.6 | ||
* | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Rob Moran | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
// https://github.com/umdjs/umd | ||
(function (root, factory) { | ||
if (typeof define === 'function' && define.amd) { | ||
// AMD. Register as an anonymous module. | ||
define(['bleat'], factory.bind(this, root)); | ||
} else if (typeof exports === 'object') { | ||
// Node. Does not work with strict CommonJS | ||
module.exports = factory(root, require('./bleat.core')); | ||
} else { | ||
// Browser globals with support for web workers (root is window) | ||
factory(root, root.bleat); | ||
} | ||
}(this, function(root, bleat) { | ||
"use strict"; | ||
var BLUETOOTH_BASE_UUID = "-0000-1000-8000-00805f9b34fb"; | ||
var CCCD_UUID = "00002902-0000-1000-8000-00805f9b34fb"; | ||
// Advert parsing from https://github.com/evothings/evothings-examples/blob/master/resources/libs/evothings/easyble/easyble.js | ||
// Should be encapsulated in the native Android implementation (see issue #62) | ||
function b64ToUint6(nChr) { | ||
return nChr > 64 && nChr < 91 ? nChr - 65 | ||
: nChr > 96 && nChr < 123 ? nChr - 71 | ||
: nChr > 47 && nChr < 58 ? nChr + 4 | ||
: nChr === 43 ? 62 | ||
: nChr === 47 ? 63 | ||
: 0; | ||
} | ||
function base64DecToArr(sBase64, nBlocksSize) { | ||
var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""); | ||
var nInLen = sB64Enc.length; | ||
var nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2; | ||
var taBytes = new Uint8Array(nOutLen); | ||
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { | ||
nMod4 = nInIdx & 3; | ||
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; | ||
if (nMod4 === 3 || nInLen - nInIdx === 1) { | ||
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { | ||
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; | ||
} | ||
nUint24 = 0; | ||
} | ||
} | ||
return taBytes; | ||
} | ||
function littleEndianToUint16(data, offset) { | ||
return (data[offset + 1] << 8) + data[offset]; | ||
} | ||
function littleEndianToUint32(data, offset) { | ||
return (data[offset + 3] << 24) + (data[offset + 2] << 16) + (data[offset + 1] << 8) + data[offset]; | ||
} | ||
function arrayToUUID(array, offset) { | ||
var uuid = ""; | ||
var pointer = 0; | ||
[4, 2, 2, 2, 6].forEach(function(length) { | ||
uuid += '-'; | ||
for (var i = 0; i < length; i++, pointer++) { | ||
uuid += ("00" + array[offset + pointer].toString(16)).slice(-2); | ||
} | ||
}); | ||
return uuid.substr(1); | ||
} | ||
function parseAdvert(deviceInfo) { | ||
var advert = { | ||
name: deviceInfo.name, | ||
serviceUUIDs: [] | ||
}; | ||
if (deviceInfo.advertisementData) { | ||
if (deviceInfo.advertisementData.kCBAdvDataLocalName) advert.name = deviceInfo.advertisementData.kCBAdvDataLocalName; | ||
if (deviceInfo.advertisementData.kCBAdvDataServiceUUIDs) { | ||
deviceInfo.advertisementData.kCBAdvDataServiceUUIDs.forEach(function(serviceUUID) { | ||
if (serviceUUID.length > 8) advert.serviceUUIDs.push(serviceUUID.toLowerCase()); | ||
else advert.serviceUUIDs.push(("00000000" + serviceUUID.toLowerCase()).slice(-8) + BLUETOOTH_BASE_UUID); | ||
}); | ||
} | ||
} else if (deviceInfo.scanRecord) { | ||
var byteArray = base64DecToArr(deviceInfo.scanRecord); | ||
var pos = 0; | ||
while (pos < byteArray.length) { | ||
var length = byteArray[pos++]; | ||
if (length === 0) break; | ||
length -= 1; | ||
var type = byteArray[pos++]; | ||
var i; | ||
if (type == 0x02 || type == 0x03) { // 16-bit Service Class UUIDs | ||
for (i = 0; i < length; i += 2) { | ||
advert.serviceUUIDs.push(("0000" + littleEndianToUint16(byteArray, pos + i).toString(16)).slice(-8) + BLUETOOTH_BASE_UUID); | ||
} | ||
} else if (type == 0x04 || type == 0x05) { // 32-bit Service Class UUIDs | ||
for (i = 0; i < length; i += 4) { | ||
advert.serviceUUIDs.push(("00000000" + littleEndianToUint32(byteArray, pos + i).toString(16)).slice(-8) + BLUETOOTH_BASE_UUID); | ||
} | ||
} else if (type == 0x06 || type == 0x07) { // 128-bit Service Class UUIDs | ||
for (i = 0; i < length; i += 4) { | ||
advert.serviceUUIDs.push(arrayToUUID(byteArray, pos + i)); | ||
} | ||
} else if (type == 0x08 || type == 0x09) { // Local Name | ||
advert.name = evothings.ble.fromUtf8(new Uint8Array(byteArray.buffer, pos, length)).replace('\0', ''); | ||
} | ||
pos += length; | ||
} | ||
} | ||
return advert; | ||
} | ||
// https://github.com/evothings/cordova-ble/blob/master/ble.js | ||
if (root.cordova) { | ||
var platform = root.cordova.platformId; | ||
bleat.addAdapter("evothings", { | ||
deviceHandles: {}, | ||
characteristicHandles: {}, | ||
descriptorHandles: {}, | ||
notifyCallbacks: {}, | ||
init: function(readyFn, errorFn) { | ||
if (root.evothings && evothings.ble) readyFn(); | ||
else document.addEventListener("deviceready", readyFn); | ||
}, | ||
scan: function(foundFn, errorFn) { | ||
evothings.ble.startScan(function(deviceInfo) { | ||
var advert = parseAdvert(deviceInfo); | ||
var device = new bleat.Device(deviceInfo.address, advert.name, advert.serviceUUIDs); | ||
foundFn(device); | ||
}, errorFn); | ||
}, | ||
stop: function(errorFn) { | ||
evothings.ble.stopScan(); | ||
}, | ||
connect: function(device, connectFn, disconnectFn, errorFn) { | ||
evothings.ble.connect(device.address, function(connectInfo) { | ||
if (connectInfo.state === 0) { // Disconnected | ||
device.connected = false; | ||
device.services = {}; | ||
if (this.deviceHandles[device.address]) { | ||
evothings.ble.close(this.deviceHandles[device.address]); | ||
delete this.deviceHandles[device.address]; | ||
} | ||
disconnectFn(); | ||
} else if (connectInfo.state === 2) { // Connected | ||
device.connected = true; | ||
this.deviceHandles[device.address] = connectInfo.deviceHandle; | ||
evothings.ble.readAllServiceData(connectInfo.deviceHandle, function(services) { | ||
services.forEach(function(serviceInfo) { | ||
var service = new bleat.Service(serviceInfo.uuid, serviceInfo.handle, (serviceInfo.type === 0)); | ||
device.services[service.uuid] = service; | ||
serviceInfo.characteristics.forEach(function(characteristicInfo) { | ||
var properties = [];// [characteristicInfo.permission + characteristicInfo.property + characteristicInfo.writeType] | ||
var characteristic = new bleat.Characteristic(characteristicInfo.uuid, characteristicInfo.handle, properties); | ||
service.characteristics[characteristic.uuid] = characteristic; | ||
this.characteristicHandles[characteristicInfo.handle] = connectInfo.deviceHandle; | ||
characteristicInfo.descriptors.forEach(function(descriptorInfo) { | ||
var descriptor = new bleat.Descriptor(descriptorInfo.uuid, descriptorInfo.handle); | ||
characteristic.descriptors[descriptor.uuid] = descriptor; | ||
this.descriptorHandles[descriptorInfo.handle] = connectInfo.deviceHandle; | ||
}, this); | ||
}, this); | ||
}, this); | ||
connectFn(); | ||
}.bind(this), errorFn); | ||
} | ||
}.bind(this), errorFn); | ||
}, | ||
disconnect: function(device, errorFn) { | ||
if (this.deviceHandles[device.address]) evothings.ble.close(this.deviceHandles[device.address]); | ||
}, | ||
readCharacteristic: function(characteristic, completeFn, errorFn) { | ||
evothings.ble.readCharacteristic(this.characteristicHandles[characteristic.handle], characteristic.handle, function(data) { | ||
// Re-enable notification on iOS if there was one, see issue #61 | ||
if (platform === "ios" && this.notifyCallbacks[characteristic.uuid]) this.enableNotify(characteristic, this.notifyCallbacks[characteristic.uuid], null, errorFn); | ||
completeFn(data); | ||
}.bind(this), errorFn); | ||
}, | ||
writeCharacteristic: function(characteristic, bufferView, completeFn, errorFn) { | ||
evothings.ble.writeCharacteristic(this.characteristicHandles[characteristic.handle], characteristic.handle, bufferView, completeFn, errorFn); | ||
}, | ||
enableNotify: function(characteristic, notifyFn, completeFn, errorFn) { | ||
var callbackFn = function() { | ||
evothings.ble.enableNotification(this.characteristicHandles[characteristic.handle], characteristic.handle, notifyFn, errorFn); | ||
if (platform === "ios") this.notifyCallbacks[characteristic.uuid] = notifyFn; | ||
if (completeFn) completeFn(); | ||
}; | ||
if (platform === "android") { | ||
// Android needs the CCCD descriptor written to for notifications | ||
// Should be encapsulated in native android layer (see issue #30) | ||
var descriptor = characteristic.descriptors[CCCD_UUID]; | ||
if (!descriptor) { | ||
errorFn("cannot find notify descriptor"); | ||
return; | ||
} | ||
this.writeDescriptor(descriptor, new Uint8Array([1, 0]), callbackFn.bind(this), errorFn); | ||
} else callbackFn.call(this); | ||
}, | ||
disableNotify: function(characteristic, completeFn, errorFn) { | ||
evothings.ble.disableNotification(this.characteristicHandles[characteristic.handle], characteristic.handle, completeFn, errorFn); | ||
if (platform === "ios") { | ||
delete this.notifyCallbacks[characteristic.uuid]; | ||
// iOS doesn't call back after disable (see issue #65) | ||
completeFn(); | ||
} | ||
}, | ||
readDescriptor: function(descriptor, completeFn, errorFn) { | ||
evothings.ble.readDescriptor(this.descriptorHandles[descriptor.handle], descriptor.handle, completeFn, errorFn); | ||
}, | ||
writeDescriptor: function(descriptor, bufferView, completeFn, errorFn) { | ||
evothings.ble.writeDescriptor(this.descriptorHandles[descriptor.handle], descriptor.handle, bufferView, completeFn, errorFn); | ||
} | ||
}); | ||
} | ||
})); | ||
/* @license | ||
* | ||
* BLE Abstraction Tool: noble adapter | ||
* Version: 0.0.1 | ||
* | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Rob Moran | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
// https://github.com/umdjs/umd | ||
(function (root, factory) { | ||
if (typeof define === 'function' && define.amd) { | ||
// AMD. Register as an anonymous module. | ||
define(['noble', 'bleat'], factory); | ||
} else if (typeof exports === 'object') { | ||
// Node. Does not work with strict CommonJS | ||
module.exports = factory(require('noble'), require('./bleat.core')); | ||
} else { | ||
// Browser globals with support for web workers (root is window) | ||
factory(root.noble, root.bleat); | ||
} | ||
}(this, function(noble, bleat) { | ||
"use strict"; | ||
function checkForError(errorFn, continueFn) { | ||
return function(error) { | ||
if (error) errorFn(error); | ||
else if (typeof continueFn === "function") { | ||
var args = [].slice.call(arguments, 1); | ||
continueFn.apply(this, args); | ||
} | ||
}; | ||
} | ||
function expandUUID(uuid) { | ||
var BLUETOOTH_BASE_UUID = "-0000-1000-8000-00805f9b34fb"; | ||
if (uuid.length > 8) return uuid.toLowerCase(); | ||
else return ("00000000" + uuid.toLowerCase()).slice(-8) + BLUETOOTH_BASE_UUID; | ||
} | ||
// https://github.com/sandeepmistry/noble | ||
if (noble) { | ||
bleat.addAdapter("noble", { | ||
foundFn: null, | ||
deviceHandles: {}, | ||
characteristicHandles: {}, | ||
descriptorHandles: {}, | ||
charNotifies: {}, | ||
init: function(readyFn, errorFn) { | ||
function stateCB(state) { | ||
if (state === "poweredOn") { | ||
noble.on('discover', function(deviceInfo) { | ||
if (this.foundFn) { | ||
var address = (deviceInfo.address && deviceInfo.address !== "unknown") ? deviceInfo.address : deviceInfo.uuid; | ||
this.deviceHandles[address] = deviceInfo; | ||
var serviceUUIDs = []; | ||
deviceInfo.advertisement.serviceUuids.forEach(function(serviceUUID) { | ||
serviceUUIDs.push(expandUUID(serviceUUID)); | ||
}); | ||
var device = new bleat.Device(address, deviceInfo.advertisement.localName || address, serviceUUIDs); | ||
this.foundFn(device); | ||
} | ||
}.bind(this)); | ||
readyFn(); | ||
} | ||
else errorFn("adapter not enabled"); | ||
} | ||
if (noble.state === "unknown") noble.once('stateChange', stateCB.bind(this)); | ||
else stateCB(noble.state); | ||
}, | ||
scan: function(foundFn, errorFn) { | ||
this.foundFn = foundFn; | ||
noble.startScanning([], false, checkForError(errorFn)); | ||
}, | ||
stop: function(errorFn) { | ||
noble.stopScanning(); | ||
}, | ||
connect: function(device, connectFn, disconnectFn, errorFn) { | ||
var baseDevice = this.deviceHandles[device.address]; | ||
baseDevice.once("connect", function() { | ||
baseDevice.discoverAllServicesAndCharacteristics(checkForError(errorFn, function(services, characteristics) { | ||
services.forEach(function(serviceInfo) { | ||
var service = new bleat.Service(expandUUID(serviceInfo.uuid), expandUUID(serviceInfo.uuid), false); | ||
serviceInfo.characteristics.forEach(function(characteristicInfo) { | ||
var charUUID = expandUUID(characteristicInfo.uuid); | ||
this.characteristicHandles[charUUID] = characteristicInfo; | ||
var characteristic = new bleat.Characteristic(charUUID, charUUID, characteristicInfo.properties); | ||
characteristicInfo.on('read', function(data, isNotification) { | ||
if (isNotification === true && typeof this.charNotifies[charUUID] === "function") { | ||
var arrayBuffer = new Uint8Array(data).buffer; | ||
this.charNotifies[charUUID](arrayBuffer); | ||
} | ||
}.bind(this)); | ||
service.characteristics[characteristic.uuid] = characteristic; | ||
characteristicInfo.discoverDescriptors(checkForError(errorFn, function(descriptors) { | ||
descriptors.forEach(function(descriptorInfo) { | ||
var descUUID = expandUUID(descriptorInfo.uuid); | ||
this.descriptorHandles[descUUID] = descriptorInfo; | ||
var descriptor = new bleat.Descriptor(descUUID, descUUID); | ||
characteristicInfo.descriptors[descriptor.uuid] = descriptor; | ||
}, this); | ||
}.bind(this))); | ||
}, this); | ||
device.services[service.uuid] = service; | ||
}, this); | ||
device.connected = true; | ||
connectFn(); | ||
}.bind(this))); | ||
}.bind(this)); | ||
baseDevice.once("disconnect", function() { | ||
device.connected = false; | ||
disconnectFn(); | ||
}.bind(this)); | ||
baseDevice.connect(checkForError(errorFn)); | ||
}, | ||
disconnect: function(device, errorFn) { | ||
this.deviceHandles[device.address].disconnect(checkForError(errorFn)); | ||
}, | ||
readCharacteristic: function(characteristic, completeFn, errorFn) { | ||
this.characteristicHandles[characteristic.handle].read(checkForError(errorFn, function(data) { | ||
var arrayBuffer = new Uint8Array(data).buffer; | ||
completeFn(arrayBuffer); | ||
})); | ||
}, | ||
writeCharacteristic: function(characteristic, bufferView, completeFn, errorFn) { | ||
var buffer = new Buffer(new Uint8Array(bufferView.buffer)); | ||
this.characteristicHandles[characteristic.handle].write(buffer, true, checkForError(errorFn, completeFn)); | ||
}, | ||
enableNotify: function(characteristic, notifyFn, completeFn, errorFn) { | ||
this.characteristicHandles[characteristic.handle].once("notify", function(state) { | ||
if (state !== true) return errorFn("notify failed to enable"); | ||
this.charNotifies[characteristic.uuid] = notifyFn; | ||
completeFn(); | ||
}.bind(this)); | ||
this.characteristicHandles[characteristic.handle].notify(true, checkForError(errorFn)); | ||
}, | ||
disableNotify: function(characteristic, completeFn, errorFn) { | ||
this.characteristicHandles[characteristic.handle].once("notify", function(state) { | ||
if (state !== false) return errorFn("notify failed to disable"); | ||
if (this.charNotifies[characteristic.uuid]) delete this.charNotifies[characteristic.uuid]; | ||
completeFn(); | ||
}.bind(this)); | ||
this.characteristicHandles[characteristic.handle].notify(false, checkForError(errorFn)); | ||
}, | ||
readDescriptor: function(descriptor, completeFn, errorFn) { | ||
this.descriptorHandles[descriptor.handle].readValue(checkForError(errorFn, function(data) { | ||
var arrayBuffer = new Uint8Array(data).buffer; | ||
completeFn(arrayBuffer); | ||
})); | ||
}, | ||
writeDescriptor: function(descriptor, bufferView, completeFn, errorFn) { | ||
var buffer = new Buffer(new Uint8Array(bufferView.buffer)); | ||
this.descriptorHandles[descriptor.handle].writeValue(buffer, checkForError(errorFn, completeFn)); | ||
} | ||
}); | ||
} | ||
})); |
/* @license | ||
* | ||
* BLE Abstraction Tool | ||
* Version: 0.0.10 | ||
* BLE Abstraction Tool: core functionality | ||
* Version: 0.0.11 | ||
* | ||
@@ -31,3 +31,3 @@ * The MIT License (MIT) | ||
* BLE Abstraction Tool: chromeos adapter | ||
* Version: 0.0.4 | ||
* Version: 0.0.5 | ||
* | ||
@@ -56,6 +56,6 @@ * The MIT License (MIT) | ||
*/ | ||
function(a,b){"function"==typeof define&&define.amd?define(["bleat"],b.bind(this,a)):"object"==typeof exports?module.exports=b(a,require("bleat")):b(a,a.bleat)}(this,function(a,b){"use strict";function c(a,b){return function(){if(chrome.runtime.lastError)return void a(chrome.runtime.lastError.message);if("function"==typeof b){var c=[].slice.call(arguments);b.apply(this,c)}}}a.chrome&&a.chrome.bluetooth&&a.chrome.bluetoothLowEnergy&&b.addAdapter("chromeos",{charNotifies:{},deviceDisconnects:{},foundFn:null,init:function(a,d){chrome.bluetooth.getAdapterState(c(d,function(c){chrome.bluetooth.onDeviceAdded.addListener(function(a){if(this.foundFn){var c=new b.Device(a.address,a.name,a.uuids||[]);this.foundFn(c)}}),chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(function(a){"function"==typeof this.charNotifies[a.instanceId]&&(this.charNotifies[a.instanceId](a.value),delete this.charNotifies[a.instanceId])}),chrome.bluetooth.onDeviceRemoved.addListener(this.checkDisconnect),chrome.bluetooth.onDeviceChanged.addListener(function(a){a.connected===!1&&checkDisconnect(a)}),c.available?a():d("adapter not enabled")}))},checkDisconnect:function(a){"function"==typeof this.deviceDisconnects[a.address]&&(this.deviceDisconnects[a.address](),delete this.deviceDisconnects[a.address])},scan:function(a,d){chrome.bluetooth.getDevices(c(d,function(c){c.forEach(function(c){var d=new b.Device(c.address,c.name,c.uuids||[]);a(d)})})),this.foundFn=a,chrome.bluetooth.startDiscovery(c(d))},stop:function(a){this.foundFn=null,chrome.bluetooth.stopDiscovery(c(a))},connect:function(a,d,e,f){chrome.bluetoothLowEnergy.connect(a.address,null,c(f,function(){this.deviceDisconnects[a.address]=function(){a.connected=!1,a.services={},e()},a.connected=!0,chrome.bluetoothLowEnergy.getServices(a.address,c(f,function(e){e.forEach(function(d){var e=new b.Service(d.uuid,d.instanceId,d.isPrimary);a.services[e.uuid]=e,chrome.bluetoothLowEnergy.getCharacteristics(e.handle,c(f,function(a){a.forEach(function(a){var d=new b.Characteristic(a.uuid,a.instanceId,a.properties);e.characteristics[d.uuid]=d,chrome.bluetoothLowEnergy.getDescriptors(d.handle,c(f,function(a){a.forEach(function(a){var c=new b.Descriptor(a.uuid,a.instanceId);d.descriptors[c.uuid]=c})}))})}))}),setTimeout(d,0)}))}))},disconnect:function(a,b){chrome.bluetoothLowEnergy.disconnect(a.address,c(b))},readCharacteristic:function(a,b,d){chrome.bluetoothLowEnergy.readCharacteristicValue(a.handle,c(d,function(a){b(a.value)}))},writeCharacteristic:function(a,b,d,e){chrome.bluetoothLowEnergy.writeCharacteristicValue(a.handle,b.buffer,c(e,d))},enableNotify:function(a,b,d,e){chrome.bluetoothLowEnergy.startCharacteristicNotifications(a.handle,null,c(e,function(){this.charNotifies[a.handle]=b,d()}))},disableNotify:function(a,b,d){chrome.bluetoothLowEnergy.stopCharacteristicNotifications(a.handle,c(d,function(){this.charNotifies[a.handle]&&delete this.charNotifies[a.handle],b()}))},readDescriptor:function(a,b,d){chrome.bluetoothLowEnergy.readDescriptorValue(a.handle,c(d,function(a){b(a.value)}))},writeDescriptor:function(a,b,d,e){chrome.bluetoothLowEnergy.writeDescriptorValue(a.handle,b.buffer,c(e,d))}})}),/* @license | ||
function(a,b){"function"==typeof define&&define.amd?define(["bleat"],b.bind(this,a)):"object"==typeof exports?module.exports=b(a,require("./bleat.core")):b(a,a.bleat)}(this,function(a,b){"use strict";function c(a,b){return function(){if(chrome.runtime.lastError)return void a(chrome.runtime.lastError.message);if("function"==typeof b){var c=[].slice.call(arguments);b.apply(this,c)}}}a.chrome&&a.chrome.bluetooth&&a.chrome.bluetoothLowEnergy&&b.addAdapter("chromeos",{charNotifies:{},deviceDisconnects:{},foundFn:null,init:function(a,d){chrome.bluetooth.getAdapterState(c(d,function(c){c.available?(chrome.bluetooth.onDeviceAdded.addListener(function(a){if(this.foundFn){var c=new b.Device(a.address,a.name,a.uuids||[]);this.foundFn(c)}}),chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(function(a){"function"==typeof this.charNotifies[a.instanceId]&&(this.charNotifies[a.instanceId](a.value),delete this.charNotifies[a.instanceId])}),chrome.bluetooth.onDeviceRemoved.addListener(this.checkDisconnect),chrome.bluetooth.onDeviceChanged.addListener(function(a){a.connected===!1&&checkDisconnect(a)}),a()):d("adapter not enabled")}))},checkDisconnect:function(a){"function"==typeof this.deviceDisconnects[a.address]&&(this.deviceDisconnects[a.address](),delete this.deviceDisconnects[a.address])},scan:function(a,d){chrome.bluetooth.getDevices(c(d,function(c){c.forEach(function(c){var d=new b.Device(c.address,c.name,c.uuids||[]);a(d)})})),this.foundFn=a,chrome.bluetooth.startDiscovery(c(d))},stop:function(a){this.foundFn=null,chrome.bluetooth.stopDiscovery(c(a))},connect:function(a,d,e,f){chrome.bluetoothLowEnergy.connect(a.address,null,c(f,function(){this.deviceDisconnects[a.address]=function(){a.connected=!1,a.services={},e()},a.connected=!0,chrome.bluetoothLowEnergy.getServices(a.address,c(f,function(e){e.forEach(function(d){var e=new b.Service(d.uuid,d.instanceId,d.isPrimary);a.services[e.uuid]=e,chrome.bluetoothLowEnergy.getCharacteristics(e.handle,c(f,function(a){a.forEach(function(a){var d=new b.Characteristic(a.uuid,a.instanceId,a.properties);e.characteristics[d.uuid]=d,chrome.bluetoothLowEnergy.getDescriptors(d.handle,c(f,function(a){a.forEach(function(a){var c=new b.Descriptor(a.uuid,a.instanceId);d.descriptors[c.uuid]=c})}))})}))}),setTimeout(d,0)}))}))},disconnect:function(a,b){chrome.bluetoothLowEnergy.disconnect(a.address,c(b))},readCharacteristic:function(a,b,d){chrome.bluetoothLowEnergy.readCharacteristicValue(a.handle,c(d,function(a){b(a.value)}))},writeCharacteristic:function(a,b,d,e){chrome.bluetoothLowEnergy.writeCharacteristicValue(a.handle,b.buffer,c(e,d))},enableNotify:function(a,b,d,e){chrome.bluetoothLowEnergy.startCharacteristicNotifications(a.handle,null,c(e,function(){this.charNotifies[a.handle]=b,d()}))},disableNotify:function(a,b,d){chrome.bluetoothLowEnergy.stopCharacteristicNotifications(a.handle,c(d,function(){this.charNotifies[a.handle]&&delete this.charNotifies[a.handle],b()}))},readDescriptor:function(a,b,d){chrome.bluetoothLowEnergy.readDescriptorValue(a.handle,c(d,function(a){b(a.value)}))},writeDescriptor:function(a,b,d,e){chrome.bluetoothLowEnergy.writeDescriptorValue(a.handle,b.buffer,c(e,d))}})}),/* @license | ||
* | ||
* BLE Abstraction Tool: evothings adapter | ||
* Version: 0.0.5 | ||
* Version: 0.0.6 | ||
* | ||
@@ -84,2 +84,29 @@ * The MIT License (MIT) | ||
*/ | ||
function(a,b){"function"==typeof define&&define.amd?define(["bleat"],b.bind(this,a)):"object"==typeof exports?module.exports=b(a,require("bleat")):b(a,a.bleat)}(this,function(a,b){"use strict";function c(a){return a>64&&91>a?a-65:a>96&&123>a?a-71:a>47&&58>a?a+4:43===a?62:47===a?63:0}function d(a,b){for(var d,e,f=a.replace(/[^A-Za-z0-9\+\/]/g,""),g=f.length,h=b?Math.ceil((3*g+1>>2)/b)*b:3*g+1>>2,i=new Uint8Array(h),j=0,k=0,l=0;g>l;l++)if(e=3&l,j|=c(f.charCodeAt(l))<<18-6*e,3===e||g-l===1){for(d=0;3>d&&h>k;d++,k++)i[k]=j>>>(16>>>d&24)&255;j=0}return i}function e(a,b){return(a[b+1]<<8)+a[b]}function f(a,b){return(a[b+3]<<24)+(a[b+2]<<16)+(a[b+1]<<8)+a[b]}function g(a,b){var c="",d=0;return[4,2,2,2,6].forEach(function(e){c+="-";for(var f=0;e>f;f++,d++)c+=("00"+a[b+d].toString(16)).slice(-2)}),c.substr(1)}function h(a){var b={name:a.name,serviceUUIDs:[]};if(a.advertisementData)a.advertisementData.kCBAdvDataLocalName&&(b.name=a.advertisementData.kCBAdvDataLocalName),a.advertisementData.kCBAdvDataServiceUUIDs&&a.advertisementData.kCBAdvDataServiceUUIDs.forEach(function(a){b.serviceUUIDs.push(a.length>8?a.toLowerCase():("00000000"+a.toLowerCase()).slice(-8)+i)});else if(a.scanRecord)for(var c=d(a.scanRecord),h=0;h<c.length;){var j=c[h++];if(0===j)break;j-=1;var k,l=c[h++];if(2==l||3==l)for(k=0;j>k;k+=2)b.serviceUUIDs.push(("0000"+e(c,h+k).toString(16)).slice(-8)+i);else if(4==l||5==l)for(k=0;j>k;k+=4)b.serviceUUIDs.push(("00000000"+f(c,h+k).toString(16)).slice(-8)+i);else if(6==l||7==l)for(k=0;j>k;k+=4)b.serviceUUIDs.push(g(c,h+k));else(8==l||9==l)&&(b.name=evothings.ble.fromUtf8(new Uint8Array(c.buffer,h,j)).replace("\x00",""));h+=j}return b}var i="-0000-1000-8000-00805f9b34fb",j="00002902-0000-1000-8000-00805f9b34fb";if(a.cordova){var k=a.cordova.platformId;b.addAdapter("evothings",{deviceHandles:{},characteristicHandles:{},descriptorHandles:{},notifyCallbacks:{},init:function(b,c){a.evothings&&evothings.ble?b():document.addEventListener("deviceready",b)},scan:function(a,c){evothings.ble.startScan(function(c){var d=h(c),e=new b.Device(c.address,d.name,d.serviceUUIDs);a(e)},c)},stop:function(a){evothings.ble.stopScan()},connect:function(a,c,d,e){evothings.ble.connect(a.address,function(f){0===f.state?(a.connected=!1,a.services={},this.deviceHandles[a.address]&&(evothings.ble.close(this.deviceHandles[a.address]),delete this.deviceHandles[a.address]),d()):2===f.state&&(a.connected=!0,this.deviceHandles[a.address]=f.deviceHandle,evothings.ble.readAllServiceData(f.deviceHandle,function(d){d.forEach(function(c){var d=new b.Service(c.uuid,c.handle,0===c.type);a.services[d.uuid]=d,c.characteristics.forEach(function(a){var c=[],e=new b.Characteristic(a.uuid,a.handle,c);d.characteristics[e.uuid]=e,this.characteristicHandles[a.handle]=f.deviceHandle,a.descriptors.forEach(function(a){var c=new b.Descriptor(a.uuid,a.handle);e.descriptors[c.uuid]=c,this.descriptorHandles[a.handle]=f.deviceHandle},this)},this)},this),c()}.bind(this),e))}.bind(this),e)},disconnect:function(a,b){this.deviceHandles[a.address]&&evothings.ble.close(this.deviceHandles[a.address])},readCharacteristic:function(a,b,c){evothings.ble.readCharacteristic(this.characteristicHandles[a.handle],a.handle,function(d){"ios"===k&&this.notifyCallbacks[a.uuid]&&this.enableNotify(a,this.notifyCallbacks[a.uuid],null,c),b(d)}.bind(this),c)},writeCharacteristic:function(a,b,c,d){evothings.ble.writeCharacteristic(this.characteristicHandles[a.handle],a.handle,b,c,d)},enableNotify:function(a,b,c,d){var e=function(){evothings.ble.enableNotification(this.characteristicHandles[a.handle],a.handle,b,d),"ios"===k&&(this.notifyCallbacks[a.uuid]=b),c&&c()};if("android"===k){var f=a.descriptors[j];if(!f)return void d("cannot find notify descriptor");this.writeDescriptor(f,new Uint8Array([1,0]),e.bind(this),d)}else e.call(this)},disableNotify:function(a,b,c){evothings.ble.disableNotification(this.characteristicHandles[a.handle],a.handle,b,c),"ios"===k&&(delete this.notifyCallbacks[a.uuid],b())},readDescriptor:function(a,b,c){evothings.ble.readDescriptor(this.descriptorHandles[a.handle],a.handle,b,c)},writeDescriptor:function(a,b,c,d){evothings.ble.writeDescriptor(this.descriptorHandles[a.handle],a.handle,b,c,d)}})}}); | ||
function(a,b){"function"==typeof define&&define.amd?define(["bleat"],b.bind(this,a)):"object"==typeof exports?module.exports=b(a,require("./bleat.core")):b(a,a.bleat)}(this,function(a,b){"use strict";function c(a){return a>64&&91>a?a-65:a>96&&123>a?a-71:a>47&&58>a?a+4:43===a?62:47===a?63:0}function d(a,b){for(var d,e,f=a.replace(/[^A-Za-z0-9\+\/]/g,""),g=f.length,h=b?Math.ceil((3*g+1>>2)/b)*b:3*g+1>>2,i=new Uint8Array(h),j=0,k=0,l=0;g>l;l++)if(e=3&l,j|=c(f.charCodeAt(l))<<18-6*e,3===e||g-l===1){for(d=0;3>d&&h>k;d++,k++)i[k]=j>>>(16>>>d&24)&255;j=0}return i}function e(a,b){return(a[b+1]<<8)+a[b]}function f(a,b){return(a[b+3]<<24)+(a[b+2]<<16)+(a[b+1]<<8)+a[b]}function g(a,b){var c="",d=0;return[4,2,2,2,6].forEach(function(e){c+="-";for(var f=0;e>f;f++,d++)c+=("00"+a[b+d].toString(16)).slice(-2)}),c.substr(1)}function h(a){var b={name:a.name,serviceUUIDs:[]};if(a.advertisementData)a.advertisementData.kCBAdvDataLocalName&&(b.name=a.advertisementData.kCBAdvDataLocalName),a.advertisementData.kCBAdvDataServiceUUIDs&&a.advertisementData.kCBAdvDataServiceUUIDs.forEach(function(a){b.serviceUUIDs.push(a.length>8?a.toLowerCase():("00000000"+a.toLowerCase()).slice(-8)+i)});else if(a.scanRecord)for(var c=d(a.scanRecord),h=0;h<c.length;){var j=c[h++];if(0===j)break;j-=1;var k,l=c[h++];if(2==l||3==l)for(k=0;j>k;k+=2)b.serviceUUIDs.push(("0000"+e(c,h+k).toString(16)).slice(-8)+i);else if(4==l||5==l)for(k=0;j>k;k+=4)b.serviceUUIDs.push(("00000000"+f(c,h+k).toString(16)).slice(-8)+i);else if(6==l||7==l)for(k=0;j>k;k+=4)b.serviceUUIDs.push(g(c,h+k));else(8==l||9==l)&&(b.name=evothings.ble.fromUtf8(new Uint8Array(c.buffer,h,j)).replace("\x00",""));h+=j}return b}var i="-0000-1000-8000-00805f9b34fb",j="00002902-0000-1000-8000-00805f9b34fb";if(a.cordova){var k=a.cordova.platformId;b.addAdapter("evothings",{deviceHandles:{},characteristicHandles:{},descriptorHandles:{},notifyCallbacks:{},init:function(b,c){a.evothings&&evothings.ble?b():document.addEventListener("deviceready",b)},scan:function(a,c){evothings.ble.startScan(function(c){var d=h(c),e=new b.Device(c.address,d.name,d.serviceUUIDs);a(e)},c)},stop:function(a){evothings.ble.stopScan()},connect:function(a,c,d,e){evothings.ble.connect(a.address,function(f){0===f.state?(a.connected=!1,a.services={},this.deviceHandles[a.address]&&(evothings.ble.close(this.deviceHandles[a.address]),delete this.deviceHandles[a.address]),d()):2===f.state&&(a.connected=!0,this.deviceHandles[a.address]=f.deviceHandle,evothings.ble.readAllServiceData(f.deviceHandle,function(d){d.forEach(function(c){var d=new b.Service(c.uuid,c.handle,0===c.type);a.services[d.uuid]=d,c.characteristics.forEach(function(a){var c=[],e=new b.Characteristic(a.uuid,a.handle,c);d.characteristics[e.uuid]=e,this.characteristicHandles[a.handle]=f.deviceHandle,a.descriptors.forEach(function(a){var c=new b.Descriptor(a.uuid,a.handle);e.descriptors[c.uuid]=c,this.descriptorHandles[a.handle]=f.deviceHandle},this)},this)},this),c()}.bind(this),e))}.bind(this),e)},disconnect:function(a,b){this.deviceHandles[a.address]&&evothings.ble.close(this.deviceHandles[a.address])},readCharacteristic:function(a,b,c){evothings.ble.readCharacteristic(this.characteristicHandles[a.handle],a.handle,function(d){"ios"===k&&this.notifyCallbacks[a.uuid]&&this.enableNotify(a,this.notifyCallbacks[a.uuid],null,c),b(d)}.bind(this),c)},writeCharacteristic:function(a,b,c,d){evothings.ble.writeCharacteristic(this.characteristicHandles[a.handle],a.handle,b,c,d)},enableNotify:function(a,b,c,d){var e=function(){evothings.ble.enableNotification(this.characteristicHandles[a.handle],a.handle,b,d),"ios"===k&&(this.notifyCallbacks[a.uuid]=b),c&&c()};if("android"===k){var f=a.descriptors[j];if(!f)return void d("cannot find notify descriptor");this.writeDescriptor(f,new Uint8Array([1,0]),e.bind(this),d)}else e.call(this)},disableNotify:function(a,b,c){evothings.ble.disableNotification(this.characteristicHandles[a.handle],a.handle,b,c),"ios"===k&&(delete this.notifyCallbacks[a.uuid],b())},readDescriptor:function(a,b,c){evothings.ble.readDescriptor(this.descriptorHandles[a.handle],a.handle,b,c)},writeDescriptor:function(a,b,c,d){evothings.ble.writeDescriptor(this.descriptorHandles[a.handle],a.handle,b,c,d)}})}}),/* @license | ||
* | ||
* BLE Abstraction Tool: noble adapter | ||
* Version: 0.0.1 | ||
* | ||
* The MIT License (MIT) | ||
* | ||
* Copyright (c) 2015 Rob Moran | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
function(a,b){"function"==typeof define&&define.amd?define(["noble","bleat"],b):"object"==typeof exports?module.exports=b(require("noble"),require("./bleat.core")):b(a.noble,a.bleat)}(this,function(a,b){"use strict";function c(a,b){return function(c){if(c)a(c);else if("function"==typeof b){var d=[].slice.call(arguments,1);b.apply(this,d)}}}function d(a){var b="-0000-1000-8000-00805f9b34fb";return a.length>8?a.toLowerCase():("00000000"+a.toLowerCase()).slice(-8)+b}a&&b.addAdapter("noble",{foundFn:null,deviceHandles:{},characteristicHandles:{},descriptorHandles:{},charNotifies:{},init:function(c,e){function f(f){"poweredOn"===f?(a.on("discover",function(a){if(this.foundFn){var c=a.address&&"unknown"!==a.address?a.address:a.uuid;this.deviceHandles[c]=a;var e=[];a.advertisement.serviceUuids.forEach(function(a){e.push(d(a))});var f=new b.Device(c,a.advertisement.localName||c,e);this.foundFn(f)}}.bind(this)),c()):e("adapter not enabled")}"unknown"===a.state?a.once("stateChange",f.bind(this)):f(a.state)},scan:function(b,d){this.foundFn=b,a.startScanning([],!1,c(d))},stop:function(b){a.stopScanning()},connect:function(a,e,f,g){var h=this.deviceHandles[a.address];h.once("connect",function(){h.discoverAllServicesAndCharacteristics(c(g,function(f,h){f.forEach(function(e){var f=new b.Service(d(e.uuid),d(e.uuid),!1);e.characteristics.forEach(function(a){var e=d(a.uuid);this.characteristicHandles[e]=a;var h=new b.Characteristic(e,e,a.properties);a.on("read",function(a,b){if(b===!0&&"function"==typeof this.charNotifies[e]){var c=new Uint8Array(a).buffer;this.charNotifies[e](c)}}.bind(this)),f.characteristics[h.uuid]=h,a.discoverDescriptors(c(g,function(c){c.forEach(function(c){var e=d(c.uuid);this.descriptorHandles[e]=c;var f=new b.Descriptor(e,e);a.descriptors[f.uuid]=f},this)}.bind(this)))},this),a.services[f.uuid]=f},this),a.connected=!0,e()}.bind(this)))}.bind(this)),h.once("disconnect",function(){a.connected=!1,f()}.bind(this)),h.connect(c(g))},disconnect:function(a,b){this.deviceHandles[a.address].disconnect(c(b))},readCharacteristic:function(a,b,d){this.characteristicHandles[a.handle].read(c(d,function(a){var c=new Uint8Array(a).buffer;b(c)}))},writeCharacteristic:function(a,b,d,e){var f=new Buffer(new Uint8Array(b.buffer));this.characteristicHandles[a.handle].write(f,!0,c(e,d))},enableNotify:function(a,b,d,e){this.characteristicHandles[a.handle].once("notify",function(c){return c!==!0?e("notify failed to enable"):(this.charNotifies[a.uuid]=b,void d())}.bind(this)),this.characteristicHandles[a.handle].notify(!0,c(e))},disableNotify:function(a,b,d){this.characteristicHandles[a.handle].once("notify",function(c){return c!==!1?d("notify failed to disable"):(this.charNotifies[a.uuid]&&delete this.charNotifies[a.uuid],void b())}.bind(this)),this.characteristicHandles[a.handle].notify(!1,c(d))},readDescriptor:function(a,b,d){this.descriptorHandles[a.handle].readValue(c(d,function(a){var c=new Uint8Array(a).buffer;b(c)}))},writeDescriptor:function(a,b,d,e){var f=new Buffer(new Uint8Array(b.buffer));this.descriptorHandles[a.handle].writeValue(f,c(e,d))}})}); |
var gulp = require('gulp'); | ||
var jshint = require('gulp-jshint'); | ||
var concat = require('gulp-concat'); | ||
var uglify = require('gulp-uglifyjs'); | ||
gulp.task('lint', function() { | ||
return gulp.src(['dist/*.js', '!dist/*.min.js']) | ||
return gulp.src(['dist/*.js', '!dist/*.min.js', '!dist/bleat.js']) | ||
.pipe(jshint()) | ||
@@ -11,4 +12,6 @@ .pipe(jshint.reporter('default')); | ||
gulp.task('uglify', function() { | ||
return gulp.src(['dist/bleat.js', 'dist/bleat.*.js', '!dist/*.min.js']) | ||
gulp.task('release', function() { | ||
return gulp.src(['dist/bleat.core.js', 'dist/bleat.*.js', '!dist/*.min.js']) | ||
.pipe(concat('bleat.js')) | ||
.pipe(gulp.dest('dist')) | ||
.pipe(uglify('bleat.min.js', { | ||
@@ -22,2 +25,2 @@ output: { | ||
gulp.task('default', ['lint', 'uglify']); | ||
gulp.task('default', ['lint', 'release']); |
{ | ||
"name": "bleat", | ||
"version": "0.0.10", | ||
"version": "0.0.11", | ||
"description": "Abstraction library for hiding differences in JavaScript BLE APIs", | ||
@@ -8,2 +8,3 @@ "homepage": "https://github.com/thegecko/bleat", | ||
"license": "MIT", | ||
"main": "./index.js", | ||
"repository": { | ||
@@ -19,7 +20,11 @@ "type": "git", | ||
], | ||
"optionalDependencies": { | ||
"noble": "^1.0.1" | ||
}, | ||
"devDependencies": { | ||
"gulp": "^3.8.7", | ||
"gulp-jshint": "^1.9.0", | ||
"gulp-concat": "^2.6.0", | ||
"gulp-uglifyjs": "^0.5.0" | ||
} | ||
} |
@@ -10,28 +10,30 @@ # bleat | ||
## Roadmap | ||
## Implemented | ||
#### Basic BLE layer | ||
* Central Mode | ||
* Device Scan | ||
* Advertised Service UUIDs | ||
* Connect/Disconnect | ||
* List Services/Characteristics/Descriptors | ||
* Read/Write/Notify Characteristics | ||
* Read/Write Descriptors | ||
* <del>Central Mode</del> | ||
* <del>Device Scan</del> | ||
* <del>Advertised Service UUIDs</del> | ||
* <del>Connect/Disconnect</del> | ||
* <del>List Services/Characteristics/Descriptors</del> | ||
* <del>Read/Write/Notify Characteristics</del> | ||
* <del>Read/Write Descriptors</del> | ||
* Central Adapters | ||
* Android/iOS (using Evothings/Cordova/PhoneGap) https://github.com/evothings/cordova-ble/blob/master/ble.js | ||
* Mac/Linux (using noble on Node.js) https://github.com/sandeepmistry/noble | ||
* ChromeOS https://developer.chrome.com/apps/bluetoothLowEnergy | ||
## Roadmap (to be implemented) | ||
* Central Adapters | ||
* Web (following Web Bluetooth) https://www.w3.org/community/web-bluetooth | ||
* Peripheral Mode | ||
* Advertising | ||
#### Adapters | ||
* Peripheral Adapters | ||
* Mac/Linux (using bleno on Node.js) https://github.com/sandeepmistry/bleno | ||
* Tessel https://tessel.io/modules#module-ble | ||
* Espruino http://www.espruino.com/Bluetooth | ||
* Central Mode | ||
* <del>Evothings/Cordova/PhoneGap</del> https://github.com/evothings/cordova-ble/blob/master/ble.js | ||
* <del>ChromeOS</del> https://developer.chrome.com/apps/bluetoothLowEnergy | ||
* <del>noble</del> https://github.com/sandeepmistry/noble | ||
* Web Bluetooth https://www.w3.org/community/web-bluetooth | ||
* Peripheral Mode | ||
* bleno | ||
* Tessel | ||
* Espruino | ||
## Installation | ||
@@ -47,3 +49,4 @@ | ||
The main `bleat.js` file offers the BLE layer while each `bleat.<platform>.js` file represents an adapter. | ||
The main `bleat.core.js` file offers the BLE layer while each `bleat.<platform>.js` file represents an adapter. | ||
The `bleat.js` file contains the BLE layer and all adapters concatenated together (not minified). | ||
The `bleat.min.js` file is a minified version of the BLE layer and all adapters. | ||
@@ -53,8 +56,12 @@ | ||
Include (or require) the `bleat.js` file before the adapter you wish to use or include the minified file offering all adapters. | ||
Refer to the `example_<adapter>` files for simple examples. | ||
### Plain JavaScript (globals) | ||
Include (or require) the `bleat.core.js` file before the adapter you wish to use or include the minified file offering all adapters. | ||
e.g. | ||
``` | ||
<script src="path/to/bleat.js"></script> | ||
<script src="path/to/bleat.core.js"></script> | ||
<script src="path/to/bleat.<adapter>.js"></script> | ||
@@ -65,2 +72,10 @@ -or- | ||
### Node.js | ||
Simply require it up! | ||
``` | ||
var bleat = require('bleat'); | ||
``` | ||
## API | ||
@@ -67,0 +82,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
101778
16
1649
236
1
4