Skip to content

Commit

Permalink
Updated Bleat.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikael Kindborg committed Mar 10, 2016
1 parent ff845ab commit 8a9c313
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 92 deletions.
2 changes: 1 addition & 1 deletion libs/bleat/VERSION
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Bleat version 2016-03-08 commit 436c24c6e93874b595f430779506bc50770af21a
Bleat version 2016-03-09 commit 66a9910dd8171824f5d2408f5f5fac60e646f0a6
https://github.com/thegecko/bleat
216 changes: 126 additions & 90 deletions libs/bleat/api.web-bluetooth.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,30 @@
}
}

var events = {};
function createListenerFn(eventTypes) {
return function(type, callback, capture) {
if (eventTypes.indexOf(type) < 0) return; //error
if (!events[this]) events[this] = {};
if (!events[this][type]) events[this][type] = [];
events[this][type].push(callback);
};
}
function removeEventListener(type, callback, capture) {
if (!events[this] || !events[this][type]) return; //error
var i = events[this][type].indexOf(callback);
if (i >= 0) events[this][type].splice(i, 1);
if (events[this][type].length === 0) delete events[this][type];
if (Object.keys(events[this]).length === 0) delete events[this];
}
function dispatchEvent(event) {
if (!events[this] || !events[this][event.type]) return; //error
event.target = this;
events[this][event.type].forEach(function(callback) {
if (typeof callback === "function") callback(event);
});
}

function filterDevice(options, deviceInfo) {
var valid = false;
var validServices = [];
Expand Down Expand Up @@ -116,76 +140,80 @@
return deviceInfo;
}

function scan(options, foundFn, completeFn, errorFn) {
// Must have a filter
if (!options.filters || options.filters.length === 0) {
return errorFn("no filters specified");
}
var scanner = null;
function requestDevice(options) {
return new Promise(function(resolve, reject) {
if (scanner !== null) return reject("requestDevice error: request in progress");

// Don't allow empty filters
var emptyFilter = options.filters.some(function(filter) {
return (Object.keys(filter).length === 0);
});
if (emptyFilter) {
return errorFn("empty filter specified");
}
if (!options.deviceFound) {
// Must have a filter
if (!options.filters || options.filters.length === 0) {
return reject(new TypeError("requestDevice error: no filters specified"));
}

// Don't allow empty namePrefix
var emptyPrefix = options.filters.some(function(filter) {
return (typeof filter.namePrefix !== "undefined" && filter.namePrefix === "");
});
if (emptyPrefix) {
return errorFn("empty namePrefix specified");
}
// Don't allow empty filters
var emptyFilter = options.filters.some(function(filter) {
return (Object.keys(filter).length === 0);
});
if (emptyFilter) {
return reject(new TypeError("requestDevice error: empty filter specified"));
}

var searchUUIDs = [];
if (options.filters) {
options.filters.forEach(function(filter) {
if (filter.services) searchUUIDs = searchUUIDs.concat(filter.services.map(helpers.getServiceUUID));
});
}
// Unique-ify
searchUUIDs = searchUUIDs.filter(function(item, index, array) {
return array.indexOf(item) === index;
});
// Don't allow empty namePrefix
var emptyPrefix = options.filters.some(function(filter) {
return (typeof filter.namePrefix !== "undefined" && filter.namePrefix === "");
});
if (emptyPrefix) {
return reject(new TypeError("requestDevice error: empty namePrefix specified"));
}
}

var scanTime = options.scanTime || defaultScanTime;
var scanTimeout;
adapter.startScan(searchUUIDs, function(deviceInfo) {
deviceInfo = filterDevice(options, deviceInfo);
if (deviceInfo) foundFn(deviceInfo, scanTimeout);
}, function() {
scanTimeout = setTimeout(function() {
adapter.stopScan();
completeFn();
}, scanTime);
}, errorFn);
}
var searchUUIDs = [];
if (options.filters) {
options.filters.forEach(function(filter) {
if (filter.services) searchUUIDs = searchUUIDs.concat(filter.services.map(helpers.getServiceUUID));
});
}
// Unique-ify
searchUUIDs = searchUUIDs.filter(function(item, index, array) {
return array.indexOf(item) === index;
});

var events = {};
var scanTime = options.scanTime || defaultScanTime;
var completeFn = options.deviceFound ? resolve : function() {
reject("requestDevice error: no devices found");
};

function createListenerFn(eventTypes) {
return function(type, callback, capture) {
if (eventTypes.indexOf(type) < 0) return; //error
if (!events[this]) events[this] = {};
if (!events[this][type]) events[this][type] = [];
events[this][type].push(callback);
};
}
adapter.startScan(searchUUIDs, function(deviceInfo) {
if (!options.deviceFound) {
deviceInfo = filterDevice(options, deviceInfo);
}

function removeEventListener(type, callback, capture) {
if (!events[this] || !events[this][type]) return; //error
var i = events[this][type].indexOf(callback);
if (i >= 0) events[this][type].splice(i, 1);
if (events[this][type].length === 0) delete events[this][type];
if (Object.keys(events[this]).length === 0) delete events[this];
if (deviceInfo) {
var bluetoothDevice = new BluetoothDevice(deviceInfo);
if (!options.deviceFound || options.deviceFound(bluetoothDevice)) {
cancelRequest()
.then(function() {
resolve(bluetoothDevice);
});
}
}
}, function() {
scanner = setTimeout(function() {
cancelRequest()
.then(completeFn);
}, scanTime);
}, wrapReject(reject, "requestDevice error"));
});
}

function dispatchEvent(event) {
if (!events[this] || !events[this][event.type]) return; //error
event.target = this;
events[this][event.type].forEach(function(callback) {
if (typeof callback === "function") callback(event);
function cancelRequest() {
return new Promise(function(resolve, reject) {
if (scanner) {
clearTimeout(scanner);
scanner = null;
adapter.stopScan();
}
resolve();
});
}

Expand Down Expand Up @@ -229,6 +257,8 @@
};
BluetoothRemoteGATTServer.prototype.connect = function() {
return new Promise(function(resolve, reject) {
if (this.connected) return reject("connect error: device already connected");

adapter.connect(this.device._handle, function() {
this.connected = true;
resolve(this);
Expand All @@ -244,7 +274,9 @@
};
BluetoothRemoteGATTServer.prototype.getPrimaryService = function(serviceUUID) {
return new Promise(function(resolve, reject) {
if (!this.connected) return reject("getPrimaryService error: device not connected");
if (!serviceUUID) return reject("getPrimaryService error: no service specified");

this.getPrimaryServices(serviceUUID)
.then(function(services) {
if (services.length !== 1) return reject("getPrimaryService error: service not found");
Expand All @@ -257,6 +289,8 @@
};
BluetoothRemoteGATTServer.prototype.getPrimaryServices = function(serviceUUID) {
return new Promise(function(resolve, reject) {
if (!this.connected) return reject("getPrimaryServices error: device not connected");

function complete() {
if (!serviceUUID) return resolve(this._services);
var filtered = this._services.filter(function(service) {
Expand Down Expand Up @@ -291,7 +325,9 @@
};
BluetoothRemoteGATTService.prototype.getCharacteristic = function(characteristicUUID) {
return new Promise(function(resolve, reject) {
if (!this.device.gatt.connected) return reject("getCharacteristic error: device not connected");
if (!characteristicUUID) return reject("getCharacteristic error: no characteristic specified");

this.getCharacteristics(characteristicUUID)
.then(function(characteristics) {
if (characteristics.length !== 1) return reject("getCharacteristic error: characteristic not found");
Expand All @@ -304,6 +340,8 @@
};
BluetoothRemoteGATTService.prototype.getCharacteristics = function(characteristicUUID) {
return new Promise(function(resolve, reject) {
if (!this.device.gatt.connected) return reject("getCharacteristics error: device not connected");

function complete() {
if (!characteristicUUID) return resolve(this._characteristics);
var filtered = this._characteristics.filter(function(characteristic) {
Expand All @@ -324,7 +362,9 @@
};
BluetoothRemoteGATTService.prototype.getIncludedService = function(serviceUUID) {
return new Promise(function(resolve, reject) {
if (!this.device.gatt.connected) return reject("getIncludedService error: device not connected");
if (!serviceUUID) return reject("getIncludedService error: no service specified");

this.getIncludedServices(serviceUUID)
.then(function(services) {
if (services.length !== 1) return reject("getIncludedService error: service not found");
Expand All @@ -337,6 +377,8 @@
};
BluetoothRemoteGATTService.prototype.getIncludedServices = function(serviceUUID) {
return new Promise(function(resolve, reject) {
if (!this.device.gatt.connected) return reject("getIncludedServices error: device not connected");

function complete() {
if (!serviceUUID) return resolve(this._services);
var filtered = this._services.filter(function(service) {
Expand Down Expand Up @@ -387,7 +429,9 @@
};
BluetoothRemoteGATTCharacteristic.prototype.getDescriptor = function(descriptorUUID) {
return new Promise(function(resolve, reject) {
if (!this.service.device.gatt.connected) return reject("getDescriptor error: device not connected");
if (!descriptorUUID) return reject("getDescriptor error: no descriptor specified");

this.getDescriptors(descriptorUUID)
.then(function(descriptors) {
if (descriptors.length !== 1) return reject("getDescriptor error: descriptor not found");
Expand All @@ -400,6 +444,8 @@
};
BluetoothRemoteGATTCharacteristic.prototype.getDescriptors = function(descriptorUUID) {
return new Promise(function(resolve, reject) {
if (!this.service.device.gatt.connected) return reject("getDescriptors error: device not connected");

function complete() {
if (!descriptorUUID) return resolve(this._descriptors);
var filtered = this._descriptors.filter(function(descriptor) {
Expand All @@ -420,6 +466,8 @@
};
BluetoothRemoteGATTCharacteristic.prototype.readValue = function() {
return new Promise(function(resolve, reject) {
if (!this.service.device.gatt.connected) return reject("readValue error: device not connected");

adapter.readCharacteristic(this._handle, function(dataView) {
this.value = dataView;
resolve(dataView);
Expand All @@ -428,9 +476,11 @@
}.bind(this));
};
BluetoothRemoteGATTCharacteristic.prototype.writeValue = function(bufferSource) {
var arrayBuffer = bufferSource.buffer || bufferSource;
var dataView = new DataView(arrayBuffer);
return new Promise(function(resolve, reject) {
if (!this.service.device.gatt.connected) return reject("writeValue error: device not connected");

var arrayBuffer = bufferSource.buffer || bufferSource;
var dataView = new DataView(arrayBuffer);
adapter.writeCharacteristic(this._handle, dataView, function() {
this.value = dataView;
resolve();
Expand All @@ -439,6 +489,8 @@
};
BluetoothRemoteGATTCharacteristic.prototype.startNotifications = function() {
return new Promise(function(resolve, reject) {
if (!this.service.device.gatt.connected) return reject("startNotifications error: device not connected");

adapter.enableNotify(this._handle, function(dataView) {
this.value = dataView;
this.dispatchEvent({ type: "characteristicvaluechanged", bubbles: true });
Expand All @@ -447,6 +499,8 @@
};
BluetoothRemoteGATTCharacteristic.prototype.stopNotifications = function() {
return new Promise(function(resolve, reject) {
if (!this.service.device.gatt.connected) return reject("stopNotifications error: device not connected");

adapter.disableNotify(this._handle, resolve, wrapReject(reject, "stopNotifications error"));
}.bind(this));
};
Expand All @@ -468,16 +522,20 @@
};
BluetoothRemoteGATTDescriptor.prototype.readValue = function() {
return new Promise(function(resolve, reject) {
if (!this.characteristic.service.device.gatt.connected) return reject("readValue error: device not connected");

adapter.readDescriptor(this._handle, function(dataView) {
this.value = dataView;
resolve(dataView);
}.bind(this), wrapReject(reject, "readValue error"));
}.bind(this));
};
BluetoothRemoteGATTDescriptor.prototype.writeValue = function(bufferSource) {
var arrayBuffer = bufferSource.buffer || bufferSource;
var dataView = new DataView(arrayBuffer);
return new Promise(function(resolve, reject) {
if (!this.characteristic.service.device.gatt.connected) return reject("writeValue error: device not connected");

var arrayBuffer = bufferSource.buffer || bufferSource;
var dataView = new DataView(arrayBuffer);
adapter.writeDescriptor(this._handle, dataView, function() {
this.value = dataView;
resolve();
Expand All @@ -491,29 +549,7 @@
adapters[adapterName] = definition;
adapter = definition;
},
requestDevice: function(options) {
return new Promise(function(resolve, reject) {
scan(options, function(deviceInfo, scanTimeout) {
if (scanTimeout) clearTimeout(scanTimeout);
adapter.stopScan();
resolve(new BluetoothDevice(deviceInfo));
}, function() {
reject("requestDevice error: no devices found");
}, wrapReject(reject, "requestDevice error"));
});
},
requestDevices: function(options) {
return new Promise(function(resolve, reject) {
var devices = [];
scan(options, function(deviceInfo) {
devices.push(deviceInfo);
}, function() {
if (devices.length === 0) reject("requestDevices error: no devices found");
else resolve(devices.map(function(deviceInfo) {
return new BluetoothDevice(deviceInfo);
}));
}, wrapReject(reject, "requestDevices error"));
});
}
requestDevice: requestDevice,
cancelRequest: cancelRequest
};
}));
Loading

0 comments on commit 8a9c313

Please sign in to comment.