Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

webbluetooth

Package Overview
Dependencies
Maintainers
1
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webbluetooth - npm Package Compare versions

Comparing version 0.0.4 to 0.0.5

docs/classes/bluetooth.html

5

examples/heartrate.js

@@ -26,3 +26,3 @@ /*

var bluetooth = require('../index');
var bluetooth = require('../').bluetooth;
var gattServer;

@@ -66,2 +66,5 @@ var heartChar;

}).then(value => {
if (!value.buffer.byteLength) {
return log('No value');
}
log('Value: ' + value.getUint16(0));

@@ -68,0 +71,0 @@ });

57

lib/adapter.js

@@ -27,2 +27,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const helpers_1 = require("./helpers");

@@ -33,4 +34,5 @@ const noble = require("noble");

*/
class NobleAdapter {
class NobleAdapter extends events_1.EventEmitter {
constructor() {
super();
this.deviceHandles = {};

@@ -43,3 +45,14 @@ this.serviceHandles = {};

this.initialised = false;
this.enabled = false;
this.enabled = this.state;
noble.on("stateChange", () => {
if (this.enabled !== this.state) {
this.enabled = this.state;
this.emit(NobleAdapter.EVENT_ENABLED, this.enabled);
}
});
}
get state() {
return (noble.state === "poweredOn");
}
init(completeFn) {

@@ -90,3 +103,3 @@ if (this.initialised)

const buffer = deviceInfo.advertisement.manufacturerData.slice(2);
manufacturerData[company] = this.bufferToDataView(buffer);
manufacturerData.set(company, this.bufferToDataView(buffer));
}

@@ -96,19 +109,28 @@ const serviceData = new Map();

deviceInfo.advertisement.serviceData.forEach(serviceAdvert => {
serviceData[helpers_1.getCanonicalUUID(serviceAdvert.uuid)] = this.bufferToDataView(serviceAdvert.data);
serviceData.set(helpers_1.getCanonicalUUID(serviceAdvert.uuid), this.bufferToDataView(serviceAdvert.data));
});
}
this.foundFn({
_handle: deviceID,
id: deviceID,
name: deviceInfo.advertisement.localName,
uuids: serviceUUIDs
// adData: {
// manufacturerData: manufacturerData,
// serviceData: serviceData,
// txPower: deviceInfo.advertisement.txPowerLevel,
// rssi: deviceInfo.rssi
// }
_serviceUUIDs: serviceUUIDs,
adData: {
rssi: deviceInfo.rssi,
txPower: deviceInfo.advertisement.txPowerLevel,
serviceData: serviceData,
manufacturerData: manufacturerData
}
});
}
}
getEnabled(completeFn) {
function stateCB() {
completeFn(this.state);
}
// tslint:disable-next-line:no-string-literal
if (noble.state === "unknown")
noble["once"]("stateChange", stateCB.bind(this));
else
stateCB.call(this);
}
startScan(serviceUUIDs, foundFn, completeFn, errorFn) {

@@ -121,3 +143,3 @@ if (serviceUUIDs.length === 0) {

serviceUUIDs.forEach(serviceUUID => {
if (device.uuids.indexOf(serviceUUID) >= 0) {
if (device._serviceUUIDs.indexOf(serviceUUID) >= 0) {
foundFn(device);

@@ -131,4 +153,4 @@ return;

this.deviceHandles = {};
function stateCB(state) {
if (state === "poweredOn") {
function stateCB() {
if (this.state === true) {
noble.startScanning([], false, this.checkForError(errorFn, completeFn));

@@ -144,3 +166,3 @@ }

else
stateCB.call(this, noble.state);
stateCB.call(this);
});

@@ -177,3 +199,2 @@ }

discovered.push({
_handle: serviceUUID,
uuid: serviceUUID,

@@ -197,3 +218,2 @@ primary: true

discovered.push({
_handle: serviceUUID,
uuid: serviceUUID,

@@ -217,3 +237,2 @@ primary: false

discovered.push({
_handle: charUUID,
uuid: charUUID,

@@ -254,3 +273,2 @@ properties: {

discovered.push({
_handle: descHandle,
uuid: descUUID

@@ -310,2 +328,3 @@ });

}
NobleAdapter.EVENT_ENABLED = "enabledchanged";
exports.NobleAdapter = NobleAdapter;

@@ -312,0 +331,0 @@ /**

@@ -27,136 +27,152 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const dispatcher_1 = require("./dispatcher");
const device_1 = require("./device");
const helpers_1 = require("./helpers");
const adapter_1 = require("./adapter");
/**
* @hidden
*/
const defaultScanTime = 10.24 * 1000;
/**
* @hidden
*/
let scanner = null;
/**
* @hidden
*/
function filterDevice(options, deviceInfo, validServices) {
let valid = false;
options.filters.forEach(filter => {
// Name
if (filter.name && filter.name !== deviceInfo.name)
return;
// NamePrefix
if (filter.namePrefix) {
if (filter.namePrefix.length > deviceInfo.name.length)
class Bluetooth extends dispatcher_1.EventDispatcher {
constructor(referringDevice) {
super();
this.referringDevice = referringDevice;
this.defaultScanTime = 10.24 * 1000;
this.scanner = null;
adapter_1.adapter.on(adapter_1.NobleAdapter.EVENT_ENABLED, value => {
this.dispatchEvent(Bluetooth.EVENT_AVAILABILITY, value);
});
}
filterDevice(options, deviceInfo, validServices) {
let valid = false;
options.filters.forEach(filter => {
// Name
if (filter.name && filter.name !== deviceInfo.name)
return;
if (filter.namePrefix !== deviceInfo.name.substr(0, filter.namePrefix.length))
return;
}
// Services
if (filter.services) {
const serviceUUIDs = filter.services.map(helpers_1.getServiceUUID);
const servicesValid = serviceUUIDs.every(serviceUUID => {
return (deviceInfo.uuids.indexOf(serviceUUID) > -1);
});
if (!servicesValid)
return;
validServices = validServices.concat(serviceUUIDs);
}
valid = true;
});
if (!valid)
return false;
return deviceInfo;
}
function requestDevice(options) {
return new Promise((resolve, reject) => {
if (scanner !== null)
return reject("requestDevice error: request in progress");
if (!options.acceptAllDevices && !options.deviceFound) {
// Must have a filter
if (!options.filters || options.filters.length === 0) {
return reject(new TypeError("requestDevice error: no filters specified"));
// NamePrefix
if (filter.namePrefix) {
if (filter.namePrefix.length > deviceInfo.name.length)
return;
if (filter.namePrefix !== deviceInfo.name.substr(0, filter.namePrefix.length))
return;
}
// Don't allow empty filters
const emptyFilter = options.filters.some(filter => {
return (Object.keys(filter).length === 0);
});
if (emptyFilter) {
return reject(new TypeError("requestDevice error: empty filter specified"));
// Services
if (filter.services) {
const serviceUUIDs = filter.services.map(helpers_1.getServiceUUID);
const servicesValid = serviceUUIDs.every(serviceUUID => {
return (deviceInfo._serviceUUIDs.indexOf(serviceUUID) > -1);
});
if (!servicesValid)
return;
validServices = validServices.concat(serviceUUIDs);
}
// Don't allow empty namePrefix
const emptyPrefix = options.filters.some(filter => {
return (typeof filter.namePrefix !== "undefined" && filter.namePrefix === "");
valid = true;
});
if (!valid)
return false;
return deviceInfo;
}
getAvailability() {
return new Promise((resolve, _reject) => {
adapter_1.adapter.getEnabled(enabled => {
resolve(enabled);
});
if (emptyPrefix) {
return reject(new TypeError("requestDevice error: empty namePrefix specified"));
}
}
let searchUUIDs = [];
if (options.filters) {
options.filters.forEach(filter => {
if (filter.services)
searchUUIDs = searchUUIDs.concat(filter.services.map(helpers_1.getServiceUUID));
});
}
// Unique-ify
searchUUIDs = searchUUIDs.filter((item, index, array) => {
return array.indexOf(item) === index;
});
let found = false;
adapter_1.adapter.startScan(searchUUIDs, deviceInfo => {
let validServices = [];
function complete(bluetoothDevice) {
cancelRequest()
.then(() => {
resolve(bluetoothDevice);
}
requestDevice(options) {
return new Promise((resolve, reject) => {
if (this.scanner !== null)
return reject("requestDevice error: request in progress");
if (!options.acceptAllDevices && !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 filters
const emptyFilter = options.filters.some(filter => {
return (Object.keys(filter).length === 0);
});
if (emptyFilter) {
return reject(new TypeError("requestDevice error: empty filter specified"));
}
// Don't allow empty namePrefix
const emptyPrefix = options.filters.some(filter => {
return (typeof filter.namePrefix !== "undefined" && filter.namePrefix === "");
});
if (emptyPrefix) {
return reject(new TypeError("requestDevice error: empty namePrefix specified"));
}
}
// filter devices if filters specified
let searchUUIDs = [];
if (options.filters) {
deviceInfo = filterDevice(options, deviceInfo, validServices);
options.filters.forEach(filter => {
if (filter.services)
searchUUIDs = searchUUIDs.concat(filter.services.map(helpers_1.getServiceUUID));
});
}
if (deviceInfo) {
found = true;
// Add additional services
if (options.optionalServices) {
validServices = validServices.concat(options.optionalServices.map(helpers_1.getServiceUUID));
// Unique-ify
searchUUIDs = searchUUIDs.filter((item, index, array) => {
return array.indexOf(item) === index;
});
let found = false;
adapter_1.adapter.startScan(searchUUIDs, deviceInfo => {
let validServices = [];
function complete(bluetoothDevice) {
this.cancelRequest()
.then(() => {
resolve(bluetoothDevice);
});
}
// Set unique list of allowed services
deviceInfo._allowedServices = validServices.filter((item, index, array) => {
return array.indexOf(item) === index;
});
const bluetoothDevice = new device_1.BluetoothDevice(deviceInfo);
function selectFn() {
complete(bluetoothDevice);
// filter devices if filters specified
if (options.filters) {
deviceInfo = this.filterDevice(options, deviceInfo, validServices);
}
if (!options.deviceFound || options.deviceFound(bluetoothDevice, selectFn)) {
// If no deviceFound function, or deviceFound returns true, resolve with this device immediately
complete(bluetoothDevice);
if (deviceInfo) {
found = true;
// Add additional services
if (options.optionalServices) {
validServices = validServices.concat(options.optionalServices.map(helpers_1.getServiceUUID));
}
// Set unique list of allowed services
const allowedServices = validServices.filter((item, index, array) => {
return array.indexOf(item) === index;
});
Object.assign(deviceInfo, {
_bluetooth: this,
_allowedServices: allowedServices
});
const bluetoothDevice = new device_1.BluetoothDevice(deviceInfo);
function selectFn() {
complete.call(this, bluetoothDevice);
}
if (!options.deviceFound || options.deviceFound(bluetoothDevice, selectFn.bind(this)) === true) {
// If no deviceFound function, or deviceFound returns true, resolve with this device immediately
complete.call(this, bluetoothDevice);
}
}
}, () => {
this.scanner = setTimeout(() => {
this.cancelRequest()
.then(() => {
if (!found)
reject("requestDevice error: no devices found");
});
}, options.scanTime || this.defaultScanTime);
}, error => reject(`requestDevice error: ${error}`));
});
}
cancelRequest() {
return new Promise((resolve, _reject) => {
if (this.scanner) {
clearTimeout(this.scanner);
this.scanner = null;
adapter_1.adapter.stopScan();
}
}, () => {
scanner = setTimeout(() => {
cancelRequest()
.then(() => {
if (!found)
reject("requestDevice error: no devices found");
});
}, options.scanTime || defaultScanTime);
}, error => reject(`requestDevice error: ${error}`));
});
resolve();
});
}
}
exports.requestDevice = requestDevice;
function cancelRequest() {
return new Promise((resolve, _reject) => {
if (scanner) {
clearTimeout(scanner);
scanner = null;
adapter_1.adapter.stopScan();
}
resolve();
});
}
exports.cancelRequest = cancelRequest;
/**
* Bluetooth Availability Changed event
* @event
*/
Bluetooth.EVENT_AVAILABILITY = "availabilitychanged";
exports.Bluetooth = Bluetooth;
//# sourceMappingURL=bluetooth.js.map

@@ -27,23 +27,11 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const emitter_1 = require("./emitter");
const dispatcher_1 = require("./dispatcher");
const descriptor_1 = require("./descriptor");
const helpers_1 = require("./helpers");
const adapter_1 = require("./adapter");
class BluetoothRemoteGATTCharacteristic extends emitter_1.Emitter {
/**
* @hidden
*/
class BluetoothRemoteGATTCharacteristic extends dispatcher_1.EventDispatcher {
constructor(init) {
super();
/**
* @hidden
*/
this._handle = null;
/**
* @hidden
*/
this._descriptors = null;
this.service = null;
this.uuid = null;
this.value = null;
this.properties = {

@@ -60,6 +48,18 @@ broadcast: false,

};
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
this._value = null;
this.handle = null;
this.descriptors = null;
Object.assign(this, init);
this.handle = this.uuid;
}
get value() {
return this._value;
}
setValue(value, emit) {
this._value = value;
if (emit) {
this.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
this.service.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
this.service.device.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
this.service.device._bluetooth.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
}

@@ -90,4 +90,4 @@ }

if (!descriptorUUID)
return resolve(this._descriptors);
const filtered = this._descriptors.filter(descriptor => {
return resolve(this.descriptors);
const filtered = this.descriptors.filter(descriptor => {
return (descriptor.uuid === helpers_1.getDescriptorUUID(descriptorUUID));

@@ -99,7 +99,9 @@ });

}
if (this._descriptors)
if (this.descriptors)
return complete.call(this);
adapter_1.adapter.discoverDescriptors(this._handle, [], descriptors => {
this._descriptors = descriptors.map(descriptorInfo => {
descriptorInfo.characteristic = this;
adapter_1.adapter.discoverDescriptors(this.handle, [], descriptors => {
this.descriptors = descriptors.map(descriptorInfo => {
Object.assign(descriptorInfo, {
characteristic: this
});
return new descriptor_1.BluetoothRemoteGATTDescriptor(descriptorInfo);

@@ -117,6 +119,5 @@ });

return reject("readValue error: device not connected");
adapter_1.adapter.readCharacteristic(this._handle, dataView => {
this.value = dataView;
adapter_1.adapter.readCharacteristic(this.handle, dataView => {
this.setValue(dataView, true);
resolve(dataView);
this.emit(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED);
}, error => {

@@ -136,4 +137,4 @@ reject(`readValue error: ${error}`);

const dataView = new DataView(arrayBuffer);
adapter_1.adapter.writeCharacteristic(this._handle, dataView, () => {
this.value = dataView;
adapter_1.adapter.writeCharacteristic(this.handle, dataView, () => {
this.setValue(dataView);
resolve();

@@ -149,5 +150,4 @@ }, error => {

return reject("startNotifications error: device not connected");
adapter_1.adapter.enableNotify(this._handle, dataView => {
this.value = dataView;
this.emit(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED);
adapter_1.adapter.enableNotify(this.handle, dataView => {
this.setValue(dataView, true);
}, () => {

@@ -164,3 +164,3 @@ resolve(this);

return reject("stopNotifications error: device not connected");
adapter_1.adapter.disableNotify(this._handle, () => {
adapter_1.adapter.disableNotify(this.handle, () => {
resolve(this);

@@ -167,0 +167,0 @@ }, error => {

@@ -29,19 +29,13 @@ "use strict";

class BluetoothRemoteGATTDescriptor {
/**
* @hidden
*/
constructor(init) {
/**
* @hidden
*/
this._handle = null;
this.characteristic = null;
this.uuid = null;
this.value = null;
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
}
this._value = null;
this.handle = null;
Object.assign(this, init);
this.handle = `${this.characteristic.uuid}-${this.uuid}`;
}
get value() {
return this._value;
}
readValue() {

@@ -51,4 +45,4 @@ return new Promise((resolve, reject) => {

return reject("readValue error: device not connected");
adapter_1.adapter.readDescriptor(this._handle, dataView => {
this.value = dataView;
adapter_1.adapter.readDescriptor(this.handle, dataView => {
this._value = dataView;
resolve(dataView);

@@ -69,4 +63,4 @@ }, error => {

const dataView = new DataView(arrayBuffer);
adapter_1.adapter.writeDescriptor(this._handle, dataView, () => {
this.value = dataView;
adapter_1.adapter.writeDescriptor(this.handle, dataView, () => {
this._value = dataView;
resolve();

@@ -73,0 +67,0 @@ }, error => {

@@ -27,14 +27,15 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const emitter_1 = require("./emitter");
const dispatcher_1 = require("./dispatcher");
const server_1 = require("./server");
class BluetoothDevice extends emitter_1.Emitter {
/**
* @hidden
*/
class BluetoothDevice extends dispatcher_1.EventDispatcher {
constructor(init) {
super();
this.id = "unknown";
this.name = null;
this.gatt = null;
this.watchingAdvertisements = false;
/**
* @hidden
*/
this._handle = null;
this._bluetooth = null;
/**

@@ -44,19 +45,8 @@ * @hidden

this._allowedServices = [];
this.id = "unknown";
this.name = null;
// public adData: {
// public appearance?: null;
// public txPower?: null;
// rssi?: number;
// manufacturerData = new Map();
// serviceData = new Map();
// }
this.gatt = new server_1.BluetoothRemoteGATTServer();
this.uuids = [];
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
}
this.gatt.device = this;
/**
* @hidden
*/
this._serviceUUIDs = [];
Object.assign(this, init);
this.gatt = new server_1.BluetoothRemoteGATTServer(this);
}

@@ -63,0 +53,0 @@ }

@@ -30,12 +30,13 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./bluetooth"));
const bluetooth_1 = require("./bluetooth");
exports.Bluetooth = bluetooth_1.Bluetooth;
/**
* Default bluetooth instance synonymous with `navigator.bluetooth`
*/
exports.bluetooth = new bluetooth_1.Bluetooth();
/**
* Helper methods and enums
*/
__export(require("./helpers"));
/*
import { WebBluetooth } from "./bluetooth";
import { NobleAdapter } from "./adapter";
export const bluetooth = new WebBluetooth(new NobleAdapter());
export * from "./helpers";
*/
//# sourceMappingURL=index.js.map

@@ -32,9 +32,8 @@ "use strict";

class BluetoothRemoteGATTServer {
constructor() {
/**
* @hidden
*/
this._services = null;
this.device = null;
constructor(device) {
this.device = device;
this.connected = false;
this.handle = null;
this.services = null;
this.handle = this.device.id;
}

@@ -45,9 +44,10 @@ connect() {

return reject("connect error: device already connected");
adapter_1.adapter.connect(this.device._handle, () => {
adapter_1.adapter.connect(this.handle, () => {
this.connected = true;
resolve(this);
}, () => {
this._services = null;
this.services = null;
this.connected = false;
this.device.emit(device_1.BluetoothDevice.EVENT_DISCONNECTED);
this.device.dispatchEvent(device_1.BluetoothDevice.EVENT_DISCONNECTED);
this.device._bluetooth.dispatchEvent(device_1.BluetoothDevice.EVENT_DISCONNECTED);
}, error => {

@@ -59,3 +59,3 @@ reject(`connect Error: ${error}`);

disconnect() {
adapter_1.adapter.disconnect(this.device._handle);
adapter_1.adapter.disconnect(this.handle);
this.connected = false;

@@ -86,4 +86,4 @@ }

if (!serviceUUID)
return resolve(this._services);
const filtered = this._services.filter(service => {
return resolve(this.services);
const filtered = this.services.filter(service => {
return (service.uuid === helpers_1.getServiceUUID(serviceUUID));

@@ -95,7 +95,9 @@ });

}
if (this._services)
if (this.services)
return complete.call(this);
adapter_1.adapter.discoverServices(this.device._handle, this.device._allowedServices, services => {
this._services = services.map(serviceInfo => {
serviceInfo.device = this.device;
adapter_1.adapter.discoverServices(this.handle, this.device._allowedServices, services => {
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new service_1.BluetoothRemoteGATTService(serviceInfo);

@@ -102,0 +104,0 @@ });

@@ -27,33 +27,20 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const emitter_1 = require("./emitter");
const dispatcher_1 = require("./dispatcher");
const characteristic_1 = require("./characteristic");
const helpers_1 = require("./helpers");
const adapter_1 = require("./adapter");
class BluetoothRemoteGATTService extends emitter_1.Emitter {
/**
* @hidden
*/
class BluetoothRemoteGATTService extends dispatcher_1.EventDispatcher {
constructor(init) {
super();
/**
* @hidden
*/
this._handle = null;
/**
* @hidden
*/
this._services = null;
/**
* @hidden
*/
this._characteristics = null;
this.device = null;
this.uuid = null;
this.isPrimary = false;
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
}
this.emit(BluetoothRemoteGATTService.EVENT_ADDED);
this.handle = null;
this.services = null;
this.characteristics = null;
Object.assign(this, init);
this.handle = this.uuid;
this.dispatchEvent(BluetoothRemoteGATTService.EVENT_ADDED);
this.device.dispatchEvent(BluetoothRemoteGATTService.EVENT_ADDED);
this.device._bluetooth.dispatchEvent(BluetoothRemoteGATTService.EVENT_ADDED);
}

@@ -83,4 +70,4 @@ getCharacteristic(characteristicUUID) {

if (!characteristicUUID)
return resolve(this._characteristics);
const filtered = this._characteristics.filter(characteristic => {
return resolve(this.characteristics);
const filtered = this.characteristics.filter(characteristic => {
return (characteristic.uuid === helpers_1.getCharacteristicUUID(characteristicUUID));

@@ -92,7 +79,9 @@ });

}
if (this._characteristics)
if (this.characteristics)
return complete.call(this);
adapter_1.adapter.discoverCharacteristics(this._handle, [], characteristics => {
this._characteristics = characteristics.map(characteristicInfo => {
characteristicInfo.service = this;
adapter_1.adapter.discoverCharacteristics(this.handle, [], characteristics => {
this.characteristics = characteristics.map(characteristicInfo => {
Object.assign(characteristicInfo, {
service: this
});
return new characteristic_1.BluetoothRemoteGATTCharacteristic(characteristicInfo);

@@ -129,4 +118,4 @@ });

if (!serviceUUID)
return resolve(this._services);
const filtered = this._services.filter(service => {
return resolve(this.services);
const filtered = this.services.filter(service => {
return (service.uuid === helpers_1.getServiceUUID(serviceUUID));

@@ -138,7 +127,9 @@ });

}
if (this._services)
if (this.services)
return complete.call(this);
adapter_1.adapter.discoverIncludedServices(this._handle, this.device._allowedServices, services => {
this._services = services.map(serviceInfo => {
serviceInfo.device = this.device;
adapter_1.adapter.discoverIncludedServices(this.handle, this.device._allowedServices, services => {
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new BluetoothRemoteGATTService(serviceInfo);

@@ -145,0 +136,0 @@ });

{
"name": "webbluetooth",
"version": "0.0.4",
"version": "0.0.5",
"description": "Node.js implementation of the Web Bluetooth Specification",

@@ -5,0 +5,0 @@ "homepage": "https://github.com/thegecko/webbluetooth",

@@ -18,4 +18,73 @@ # Node Web Bluetooth

See the examples in [examples/](https://github.com/thegecko/webbluetooth/tree/master/examples/) or view the API documentation at:
See the examples in [examples](https://github.com/thegecko/webbluetooth/tree/master/examples/) or view the API documentation at:
https://thegecko.github.io/webbluetooth/
## Specification
The Web Bluetooth specification can be found here:
https://webbluetoothcg.github.io/web-bluetooth/
## Implementation Status
### Bluetooth
- [x] referringDevice - specification unstable
- [x] getAvailability() - specification unstable
- [x] requestDevice()
### Bluetooth Device
- [x] BluetoothDevice
- [ ] BluetoothDevice.watchAdvertisements() - specification unstable
- [ ] BluetoothDevice.unwatchAdvertisements() - specification unstable
### Bluetooth Server
- [x] BluetoothRemoteGATTServer
- [x] BluetoothRemoteGATTServer.connect()
- [x] BluetoothRemoteGATTServer.disconnect()
- [x] BluetoothRemoteGATTServer.getPrimaryService()
- [x] BluetoothRemoteGATTServer.getPrimaryServices()
### Bluetooth Services
- [x] BluetoothRemoteGATTService
- [x] BluetoothRemoteGATTService.getCharacteristic()
- [x] BluetoothRemoteGATTService.getCharacteristics()
- [x] BluetoothRemoteGATTService.getIncludedService()
- [x] BluetoothRemoteGATTService.getIncludedServices()
### Bluetooth Characteristics
- [x] BluetoothRemoteGATTCharacteristic
- [x] BluetoothRemoteGATTCharacteristic.getDescriptor()
- [x] BluetoothRemoteGATTCharacteristic.getDescriptors()
- [x] BluetoothRemoteGATTCharacteristic.readValue()
- [x] BluetoothRemoteGATTCharacteristic.writeValue()
- [x] BluetoothRemoteGATTCharacteristic.startNotifications()
- [x] BluetoothRemoteGATTCharacteristic.stopNotifications()
### Bluetooth Descriptors
- [x] BluetoothRemoteGATTDescriptor
- [x] BluetoothRemoteGATTDescriptor.readValue()
- [x] BluetoothRemoteGATTDescriptor.writeValue()
### Bluetooth Events
- [x] availabilitychanged - specification unstable
- [x] gattserverdisconnected
- [x] characteristicvaluechanged
- [x] serviceadded
- [ ] servicechanged - unsupported in noble
- [ ] serviceremoved - unsupported in noble
### Other
- [x] Device selector hook
- [x] Lookups for known services, characteristics and descriptors
- [x] Canonical UUID helper
- [x] Examples
- [ ] API Documentation

@@ -26,2 +26,3 @@ /*

import { EventEmitter } from "events";
import { getCanonicalUUID } from "./helpers";

@@ -37,3 +38,4 @@ import { BluetoothDevice } from "./device";

*/
export interface Adapter {
export interface Adapter extends EventEmitter {
getEnabled: (completeFn: (enabled: boolean) => void) => void;
startScan: (serviceUUIDs: Array<string>, foundFn: (device: Partial<BluetoothDevice>) => void, completeFn?: () => void, errorFn?: (errorMsg: string) => void) => void;

@@ -58,4 +60,6 @@ stopScan: (errorFn?: (errorMsg: string) => void) => void;

*/
export class NobleAdapter implements Adapter {
export class NobleAdapter extends EventEmitter implements Adapter {
public static EVENT_ENABLED: string = "enabledchanged";
private deviceHandles: {} = {};

@@ -68,3 +72,19 @@ private serviceHandles: {} = {};

private initialised: boolean = false;
private enabled: boolean = false;
constructor() {
super();
this.enabled = this.state;
noble.on("stateChange", () => {
if (this.enabled !== this.state) {
this.enabled = this.state;
this.emit(NobleAdapter.EVENT_ENABLED, this.enabled);
}
});
}
private get state(): boolean {
return (noble.state === "poweredOn");
}
private init(completeFn: () => any) {

@@ -118,3 +138,3 @@ if (this.initialised) return completeFn();

const buffer = deviceInfo.advertisement.manufacturerData.slice(2);
manufacturerData[company] = this.bufferToDataView(buffer);
manufacturerData.set(company, this.bufferToDataView(buffer));
}

@@ -125,3 +145,3 @@

deviceInfo.advertisement.serviceData.forEach(serviceAdvert => {
serviceData[getCanonicalUUID(serviceAdvert.uuid)] = this.bufferToDataView(serviceAdvert.data);
serviceData.set(getCanonicalUUID(serviceAdvert.uuid), this.bufferToDataView(serviceAdvert.data));
});

@@ -131,12 +151,11 @@ }

this.foundFn({
_handle: deviceID,
id: deviceID,
name: deviceInfo.advertisement.localName,
uuids: serviceUUIDs
// adData: {
// manufacturerData: manufacturerData,
// serviceData: serviceData,
// txPower: deviceInfo.advertisement.txPowerLevel,
// rssi: deviceInfo.rssi
// }
_serviceUUIDs: serviceUUIDs,
adData: {
rssi: deviceInfo.rssi,
txPower: deviceInfo.advertisement.txPowerLevel,
serviceData: serviceData,
manufacturerData: manufacturerData
}
});

@@ -146,2 +165,12 @@ }

public getEnabled(completeFn: (enabled: boolean) => void) {
function stateCB() {
completeFn(this.state);
}
// tslint:disable-next-line:no-string-literal
if (noble.state === "unknown") noble["once"]("stateChange", stateCB.bind(this));
else stateCB.call(this);
}
public startScan(serviceUUIDs: Array<string>, foundFn: (device: Partial<BluetoothDevice>) => void, completeFn?: () => void, errorFn?: (errorMsg: string) => void): void {

@@ -154,3 +183,3 @@

serviceUUIDs.forEach(serviceUUID => {
if (device.uuids.indexOf(serviceUUID) >= 0) {
if (device._serviceUUIDs.indexOf(serviceUUID) >= 0) {
foundFn(device);

@@ -165,4 +194,4 @@ return;

this.deviceHandles = {};
function stateCB(state) {
if (state === "poweredOn") {
function stateCB() {
if (this.state === true) {
noble.startScanning([], false, this.checkForError(errorFn, completeFn));

@@ -175,3 +204,3 @@ } else {

if (noble.state === "unknown") noble["once"]("stateChange", stateCB.bind(this));
else stateCB.call(this, noble.state);
else stateCB.call(this);
});

@@ -213,3 +242,2 @@ }

discovered.push({
_handle: serviceUUID,
uuid: serviceUUID,

@@ -237,3 +265,2 @@ primary: true

discovered.push({
_handle: serviceUUID,
uuid: serviceUUID,

@@ -261,3 +288,2 @@ primary: false

discovered.push({
_handle: charUUID,
uuid: charUUID,

@@ -303,3 +329,2 @@ properties: {

discovered.push({
_handle: descHandle,
uuid: descUUID

@@ -306,0 +331,0 @@ });

@@ -26,156 +26,187 @@ /*

import { EventDispatcher } from "./dispatcher";
import { BluetoothDevice } from "./device";
import { getServiceUUID } from "./helpers";
import { adapter } from "./adapter";
import { adapter, NobleAdapter } from "./adapter";
export interface BluetoothLEScanFilterInit {
services?: Array<string>;
name?: string;
namePrefix?: string;
// Maps unsigned shorts to BluetoothDataFilters.
// object manufacturerData;
// Maps BluetoothServiceUUIDs to BluetoothDataFilters.
// object serviceData;
}
export interface RequestDeviceOptions {
acceptAllDevices: boolean;
deviceFound: (device: BluetoothDevice, selectFn: any) => void;
filters: Array<any>;
optionalServices: Array<any>;
scanTime: any;
acceptAllDevices?: boolean;
deviceFound?: (device: BluetoothDevice, selectFn: any) => boolean;
filters?: Array<BluetoothLEScanFilterInit>;
optionalServices?: Array<any>;
scanTime?: any;
}
/**
* @hidden
*/
const defaultScanTime = 10.24 * 1000;
export class Bluetooth extends EventDispatcher {
/**
* @hidden
*/
let scanner = null;
/**
* Bluetooth Availability Changed event
* @event
*/
public static EVENT_AVAILABILITY: string = "availabilitychanged";
/**
* @hidden
*/
function filterDevice(options: RequestDeviceOptions, deviceInfo, validServices) {
let valid = false;
private defaultScanTime = 10.24 * 1000;
private scanner = null;
options.filters.forEach(filter => {
// Name
if (filter.name && filter.name !== deviceInfo.name) return;
constructor(public readonly referringDevice?: BluetoothDevice) {
super();
adapter.on(NobleAdapter.EVENT_ENABLED, value => {
this.dispatchEvent(Bluetooth.EVENT_AVAILABILITY, value);
});
}
// NamePrefix
if (filter.namePrefix) {
if (filter.namePrefix.length > deviceInfo.name.length) return;
if (filter.namePrefix !== deviceInfo.name.substr(0, filter.namePrefix.length)) return;
}
private filterDevice(options: RequestDeviceOptions, deviceInfo, validServices) {
let valid = false;
// Services
if (filter.services) {
const serviceUUIDs = filter.services.map(getServiceUUID);
const servicesValid = serviceUUIDs.every(serviceUUID => {
return (deviceInfo.uuids.indexOf(serviceUUID) > -1);
});
options.filters.forEach(filter => {
// Name
if (filter.name && filter.name !== deviceInfo.name) return;
if (!servicesValid) return;
validServices = validServices.concat(serviceUUIDs);
}
// NamePrefix
if (filter.namePrefix) {
if (filter.namePrefix.length > deviceInfo.name.length) return;
if (filter.namePrefix !== deviceInfo.name.substr(0, filter.namePrefix.length)) return;
}
valid = true;
});
// Services
if (filter.services) {
const serviceUUIDs = filter.services.map(getServiceUUID);
const servicesValid = serviceUUIDs.every(serviceUUID => {
return (deviceInfo._serviceUUIDs.indexOf(serviceUUID) > -1);
});
if (!valid) return false;
return deviceInfo;
}
export function requestDevice(options: RequestDeviceOptions): Promise<BluetoothDevice> {
return new Promise((resolve, reject) => {
if (scanner !== null) return reject("requestDevice error: request in progress");
if (!options.acceptAllDevices && !options.deviceFound) {
// Must have a filter
if (!options.filters || options.filters.length === 0) {
return reject(new TypeError("requestDevice error: no filters specified"));
if (!servicesValid) return;
validServices = validServices.concat(serviceUUIDs);
}
// Don't allow empty filters
const emptyFilter = options.filters.some(filter => {
return (Object.keys(filter).length === 0);
});
if (emptyFilter) {
return reject(new TypeError("requestDevice error: empty filter specified"));
}
valid = true;
});
// Don't allow empty namePrefix
const emptyPrefix = options.filters.some(filter => {
return (typeof filter.namePrefix !== "undefined" && filter.namePrefix === "");
});
if (emptyPrefix) {
return reject(new TypeError("requestDevice error: empty namePrefix specified"));
}
}
if (!valid) return false;
return deviceInfo;
}
let searchUUIDs = [];
if (options.filters) {
options.filters.forEach(filter => {
if (filter.services) searchUUIDs = searchUUIDs.concat(filter.services.map(getServiceUUID));
public getAvailability(): Promise<boolean> {
return new Promise((resolve, _reject) => {
adapter.getEnabled(enabled => {
resolve(enabled);
});
}
// Unique-ify
searchUUIDs = searchUUIDs.filter((item, index, array) => {
return array.indexOf(item) === index;
});
}
let found = false;
adapter.startScan(searchUUIDs, deviceInfo => {
let validServices = [];
public requestDevice(options: RequestDeviceOptions): Promise<BluetoothDevice> {
return new Promise((resolve, reject) => {
if (this.scanner !== null) return reject("requestDevice error: request in progress");
function complete(bluetoothDevice) {
cancelRequest()
.then(() => {
resolve(bluetoothDevice);
if (!options.acceptAllDevices && !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 filters
const emptyFilter = options.filters.some(filter => {
return (Object.keys(filter).length === 0);
});
if (emptyFilter) {
return reject(new TypeError("requestDevice error: empty filter specified"));
}
// Don't allow empty namePrefix
const emptyPrefix = options.filters.some(filter => {
return (typeof filter.namePrefix !== "undefined" && filter.namePrefix === "");
});
if (emptyPrefix) {
return reject(new TypeError("requestDevice error: empty namePrefix specified"));
}
}
// filter devices if filters specified
let searchUUIDs = [];
if (options.filters) {
deviceInfo = filterDevice(options, deviceInfo, validServices);
options.filters.forEach(filter => {
if (filter.services) searchUUIDs = searchUUIDs.concat(filter.services.map(getServiceUUID));
});
}
if (deviceInfo) {
found = true;
// Unique-ify
searchUUIDs = searchUUIDs.filter((item, index, array) => {
return array.indexOf(item) === index;
});
// Add additional services
if (options.optionalServices) {
validServices = validServices.concat(options.optionalServices.map(getServiceUUID));
let found = false;
adapter.startScan(searchUUIDs, deviceInfo => {
let validServices = [];
function complete(bluetoothDevice) {
this.cancelRequest()
.then(() => {
resolve(bluetoothDevice);
});
}
// Set unique list of allowed services
deviceInfo._allowedServices = validServices.filter((item, index, array) => {
return array.indexOf(item) === index;
});
// filter devices if filters specified
if (options.filters) {
deviceInfo = this.filterDevice(options, deviceInfo, validServices);
}
const bluetoothDevice = new BluetoothDevice(deviceInfo);
if (deviceInfo) {
found = true;
function selectFn() {
complete(bluetoothDevice);
// Add additional services
if (options.optionalServices) {
validServices = validServices.concat(options.optionalServices.map(getServiceUUID));
}
// Set unique list of allowed services
const allowedServices = validServices.filter((item, index, array) => {
return array.indexOf(item) === index;
});
Object.assign(deviceInfo, {
_bluetooth: this,
_allowedServices: allowedServices
});
const bluetoothDevice = new BluetoothDevice(deviceInfo);
function selectFn() {
complete.call(this, bluetoothDevice);
}
if (!options.deviceFound || options.deviceFound(bluetoothDevice, selectFn.bind(this)) === true) {
// If no deviceFound function, or deviceFound returns true, resolve with this device immediately
complete.call(this, bluetoothDevice);
}
}
}, () => {
this.scanner = setTimeout(() => {
this.cancelRequest()
.then(() => {
if (!found) reject("requestDevice error: no devices found");
});
}, options.scanTime || this.defaultScanTime);
}, error => reject(`requestDevice error: ${error}`));
});
}
if (!options.deviceFound || options.deviceFound(bluetoothDevice, selectFn)) {
// If no deviceFound function, or deviceFound returns true, resolve with this device immediately
complete(bluetoothDevice);
}
public cancelRequest(): Promise<void> {
return new Promise((resolve, _reject) => {
if (this.scanner) {
clearTimeout(this.scanner);
this.scanner = null;
adapter.stopScan();
}
}, () => {
scanner = setTimeout(() => {
cancelRequest()
.then(() => {
if (!found) reject("requestDevice error: no devices found");
});
}, options.scanTime || defaultScanTime);
}, error => reject(`requestDevice error: ${error}`));
});
resolve();
});
}
}
export function cancelRequest(): Promise<void> {
return new Promise((resolve, _reject) => {
if (scanner) {
clearTimeout(scanner);
scanner = null;
adapter.stopScan();
}
resolve();
});
}

@@ -26,3 +26,3 @@ /*

import { Emitter } from "./emitter";
import { EventDispatcher } from "./dispatcher";
import { BluetoothRemoteGATTService } from "./service";

@@ -33,3 +33,3 @@ import { BluetoothRemoteGATTDescriptor } from "./descriptor";

export class BluetoothRemoteGATTCharacteristic extends Emitter {
export class BluetoothRemoteGATTCharacteristic extends EventDispatcher {

@@ -42,17 +42,5 @@ /**

/**
* @hidden
*/
public _handle: string = null;
/**
* @hidden
*/
public _descriptors: Array<BluetoothRemoteGATTDescriptor> = null;
public service: BluetoothRemoteGATTService = null;
public uuid = null;
public value = null;
public properties = {
public readonly service: BluetoothRemoteGATTService = null;
public readonly uuid = null;
public readonly properties = {
broadcast: false,

@@ -69,11 +57,23 @@ read: false,

/**
* @hidden
*/
private _value: DataView = null;
public get value(): DataView {
return this._value;
}
private handle: string = null;
private descriptors: Array<BluetoothRemoteGATTDescriptor> = null;
constructor(init?: Partial<BluetoothRemoteGATTCharacteristic>) {
super();
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
Object.assign(this, init);
this.handle = this.uuid;
}
private setValue(value?: DataView, emit?: boolean) {
this._value = value;
if (emit) {
this.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
this.service.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
this.service.device.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
this.service.device._bluetooth.dispatchEvent(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED, value);
}

@@ -103,5 +103,5 @@ }

function complete() {
if (!descriptorUUID) return resolve(this._descriptors);
if (!descriptorUUID) return resolve(this.descriptors);
const filtered = this._descriptors.filter(descriptor => {
const filtered = this.descriptors.filter(descriptor => {
return (descriptor.uuid === getDescriptorUUID(descriptorUUID));

@@ -114,7 +114,9 @@ });

if (this._descriptors) return complete.call(this);
if (this.descriptors) return complete.call(this);
adapter.discoverDescriptors(this._handle, [], descriptors => {
this._descriptors = descriptors.map(descriptorInfo => {
descriptorInfo.characteristic = this;
adapter.discoverDescriptors(this.handle, [], descriptors => {
this.descriptors = descriptors.map(descriptorInfo => {
Object.assign(descriptorInfo, {
characteristic: this
});
return new BluetoothRemoteGATTDescriptor(descriptorInfo);

@@ -134,6 +136,5 @@ });

adapter.readCharacteristic(this._handle, dataView => {
this.value = dataView;
adapter.readCharacteristic(this.handle, dataView => {
this.setValue(dataView, true);
resolve(dataView);
this.emit(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED);
}, error => {

@@ -145,3 +146,3 @@ reject(`readValue error: ${error}`);

public writeValue(bufferSource: ArrayBuffer | ArrayBufferView) {
public writeValue(bufferSource: ArrayBuffer | ArrayBufferView): Promise<void> {
return new Promise((resolve, reject) => {

@@ -157,4 +158,4 @@ if (!this.service.device.gatt.connected) return reject("writeValue error: device not connected");

adapter.writeCharacteristic(this._handle, dataView, () => {
this.value = dataView;
adapter.writeCharacteristic(this.handle, dataView, () => {
this.setValue (dataView);
resolve();

@@ -171,5 +172,4 @@ }, error => {

adapter.enableNotify(this._handle, dataView => {
this.value = dataView;
this.emit(BluetoothRemoteGATTCharacteristic.EVENT_CHANGED);
adapter.enableNotify(this.handle, dataView => {
this.setValue(dataView, true);
}, () => {

@@ -187,3 +187,3 @@ resolve(this);

adapter.disableNotify(this._handle, () => {
adapter.disableNotify(this.handle, () => {
resolve(this);

@@ -190,0 +190,0 @@ }, error => {

@@ -30,20 +30,16 @@ /*

export class BluetoothRemoteGATTDescriptor {
/**
* @hidden
*/
public _handle: string = null;
public characteristic: BluetoothRemoteGATTCharacteristic = null;
public uuid = null;
public value = null;
public readonly characteristic: BluetoothRemoteGATTCharacteristic = null;
public readonly uuid: string = null;
/**
* @hidden
*/
private _value: DataView = null;
public get value(): DataView {
return this._value;
}
private handle: string = null;
constructor(init?: Partial<BluetoothRemoteGATTDescriptor>) {
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
}
Object.assign(this, init);
this.handle = `${this.characteristic.uuid}-${this.uuid}`;
}

@@ -55,4 +51,4 @@

adapter.readDescriptor(this._handle, dataView => {
this.value = dataView;
adapter.readDescriptor(this.handle, dataView => {
this._value = dataView;
resolve(dataView);

@@ -65,3 +61,3 @@ }, error => {

public writeValue(bufferSource: ArrayBuffer | ArrayBufferView) {
public writeValue(bufferSource: ArrayBuffer | ArrayBufferView): Promise<void> {
return new Promise((resolve, reject) => {

@@ -77,4 +73,4 @@ if (!this.characteristic.service.device.gatt.connected) return reject("writeValue error: device not connected");

adapter.writeDescriptor(this._handle, dataView, () => {
this.value = dataView;
adapter.writeDescriptor(this.handle, dataView, () => {
this._value = dataView;
resolve();

@@ -81,0 +77,0 @@ }, error => {

@@ -26,6 +26,7 @@ /*

import { Emitter } from "./emitter";
import { EventDispatcher } from "./dispatcher";
import { Bluetooth } from "./bluetooth";
import { BluetoothRemoteGATTServer } from "./server";
export class BluetoothDevice extends Emitter {
export class BluetoothDevice extends EventDispatcher {

@@ -38,6 +39,16 @@ /**

public readonly id: string = "unknown";
public readonly name: string = null;
public readonly gatt: BluetoothRemoteGATTServer = null;
public readonly watchingAdvertisements: boolean = false;
/**
* @hidden
*/
public _handle: string = null;
public readonly adData: {
rssi?: number;
txPower?: null;
serviceData?: Map<string, DataView>;
manufacturerData?: Map<string, DataView>;
};

@@ -47,30 +58,26 @@ /**

*/
public _allowedServices: Array<string> = [];
public readonly _bluetooth: Bluetooth = null;
public id: string = "unknown";
/**
* @hidden
*/
public readonly _allowedServices: Array<string> = [];
public name: string = null;
// public adData: {
// public appearance?: null;
// public txPower?: null;
// rssi?: number;
// manufacturerData = new Map();
// serviceData = new Map();
// }
public gatt: BluetoothRemoteGATTServer = new BluetoothRemoteGATTServer();
public uuids: Array<string> = [];
/**
* @hidden
*/
public _serviceUUIDs: Array<string> = [];
constructor(init?: Partial<BluetoothDevice>) {
super();
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
}
Object.assign(this, init);
this.gatt = new BluetoothRemoteGATTServer(this);
}
/*
public watchAdvertisements(): Promise<void> {
}
this.gatt.device = this;
public unwatchAdvertisements() {
}
*/
}

@@ -13,1 +13,7 @@ # Node Web Bluetooth

```
## Specification
The Web Bluetooth specification can be found here:
https://webbluetoothcg.github.io/web-bluetooth/

@@ -243,3 +243,3 @@ /*

export function getCanonicalUUID(uuid) {
export function getCanonicalUUID(uuid: string | number): string {
if (typeof uuid === "number") uuid = uuid.toString(16);

@@ -252,3 +252,3 @@ uuid = uuid.toLowerCase();

export function getServiceUUID(uuid) {
export function getServiceUUID(uuid: string | number): string {
if (bluetoothServices[uuid]) uuid = bluetoothServices[uuid];

@@ -258,3 +258,3 @@ return getCanonicalUUID(uuid);

export function getCharacteristicUUID(uuid) {
export function getCharacteristicUUID(uuid: string | number): string {
if (bluetoothCharacteristics[uuid]) uuid = bluetoothCharacteristics[uuid];

@@ -264,5 +264,5 @@ return getCanonicalUUID(uuid);

export function getDescriptorUUID(uuid) {
export function getDescriptorUUID(uuid: string | number): string {
if (bluetoothDescriptors[uuid]) uuid = bluetoothDescriptors[uuid];
return getCanonicalUUID(uuid);
}

@@ -26,11 +26,17 @@ /*

export * from "./bluetooth";
export * from "./helpers";
import { Bluetooth } from "./bluetooth";
/*
import { WebBluetooth } from "./bluetooth";
import { NobleAdapter } from "./adapter";
/**
* Default bluetooth instance synonymous with `navigator.bluetooth`
*/
export const bluetooth = new Bluetooth();
export const bluetooth = new WebBluetooth(new NobleAdapter());
/**
* Bluetooth class for creating new instances
*/
export { Bluetooth };
/**
* Helper methods and enums
*/
export * from "./helpers";
*/

@@ -32,10 +32,12 @@ /*

export class BluetoothRemoteGATTServer {
/**
* @hidden
*/
public _services: Array<BluetoothRemoteGATTService> = null;
public device: BluetoothDevice = null;
public connected: boolean = false;
private handle: string = null;
private services: Array<BluetoothRemoteGATTService> = null;
constructor(public device: BluetoothDevice) {
this.handle = this.device.id;
}
public connect(): Promise<BluetoothRemoteGATTServer> {

@@ -45,9 +47,10 @@ return new Promise((resolve, reject) => {

adapter.connect(this.device._handle, () => {
adapter.connect(this.handle, () => {
this.connected = true;
resolve(this);
}, () => {
this._services = null;
this.services = null;
this.connected = false;
this.device.emit(BluetoothDevice.EVENT_DISCONNECTED);
this.device.dispatchEvent(BluetoothDevice.EVENT_DISCONNECTED);
this.device._bluetooth.dispatchEvent(BluetoothDevice.EVENT_DISCONNECTED);
}, error => {

@@ -60,3 +63,3 @@ reject(`connect Error: ${error}`);

public disconnect() {
adapter.disconnect(this.device._handle);
adapter.disconnect(this.handle);
this.connected = false;

@@ -86,5 +89,5 @@ }

function complete() {
if (!serviceUUID) return resolve(this._services);
if (!serviceUUID) return resolve(this.services);
const filtered = this._services.filter(service => {
const filtered = this.services.filter(service => {
return (service.uuid === getServiceUUID(serviceUUID));

@@ -97,7 +100,9 @@ });

if (this._services) return complete.call(this);
if (this.services) return complete.call(this);
adapter.discoverServices(this.device._handle, this.device._allowedServices, services => {
this._services = services.map(serviceInfo => {
serviceInfo.device = this.device;
adapter.discoverServices(this.handle, this.device._allowedServices, services => {
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new BluetoothRemoteGATTService(serviceInfo);

@@ -104,0 +109,0 @@ });

@@ -26,3 +26,3 @@ /*

import { Emitter } from "./emitter";
import { EventDispatcher } from "./dispatcher";
import { BluetoothDevice } from "./device";

@@ -33,3 +33,3 @@ import { BluetoothRemoteGATTCharacteristic } from "./characteristic";

export class BluetoothRemoteGATTService extends Emitter {
export class BluetoothRemoteGATTService extends EventDispatcher {

@@ -54,32 +54,17 @@ /**

/**
* @hidden
*/
public _handle: string = null;
public readonly device: BluetoothDevice = null;
public readonly uuid: string = null;
public readonly isPrimary: boolean = false;
/**
* @hidden
*/
public _services: Array<BluetoothRemoteGATTService> = null;
private handle: string = null;
private services: Array<BluetoothRemoteGATTService> = null;
private characteristics: Array<BluetoothRemoteGATTCharacteristic> = null;
/**
* @hidden
*/
public _characteristics: Array<BluetoothRemoteGATTCharacteristic> = null;
public device: BluetoothDevice = null;
public uuid = null;
public isPrimary = false;
/**
* @hidden
*/
constructor(init?: Partial<BluetoothRemoteGATTService>) {
super();
for (const key in init) {
if (init.hasOwnProperty(key)) {
this[key] = init[key];
}
}
this.emit(BluetoothRemoteGATTService.EVENT_ADDED);
Object.assign(this, init);
this.handle = this.uuid;
this.dispatchEvent(BluetoothRemoteGATTService.EVENT_ADDED);
this.device.dispatchEvent(BluetoothRemoteGATTService.EVENT_ADDED);
this.device._bluetooth.dispatchEvent(BluetoothRemoteGATTService.EVENT_ADDED);
}

@@ -108,5 +93,5 @@

function complete() {
if (!characteristicUUID) return resolve(this._characteristics);
if (!characteristicUUID) return resolve(this.characteristics);
const filtered = this._characteristics.filter(characteristic => {
const filtered = this.characteristics.filter(characteristic => {
return (characteristic.uuid === getCharacteristicUUID(characteristicUUID));

@@ -119,7 +104,9 @@ });

if (this._characteristics) return complete.call(this);
if (this.characteristics) return complete.call(this);
adapter.discoverCharacteristics(this._handle, [], characteristics => {
this._characteristics = characteristics.map(characteristicInfo => {
characteristicInfo.service = this;
adapter.discoverCharacteristics(this.handle, [], characteristics => {
this.characteristics = characteristics.map(characteristicInfo => {
Object.assign(characteristicInfo, {
service: this
});
return new BluetoothRemoteGATTCharacteristic(characteristicInfo);

@@ -156,5 +143,5 @@ });

function complete() {
if (!serviceUUID) return resolve(this._services);
if (!serviceUUID) return resolve(this.services);
const filtered = this._services.filter(service => {
const filtered = this.services.filter(service => {
return (service.uuid === getServiceUUID(serviceUUID));

@@ -167,7 +154,9 @@ });

if (this._services) return complete.call(this);
if (this.services) return complete.call(this);
adapter.discoverIncludedServices(this._handle, this.device._allowedServices, services => {
this._services = services.map(serviceInfo => {
serviceInfo.device = this.device;
adapter.discoverIncludedServices(this.handle, this.device._allowedServices, services => {
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new BluetoothRemoteGATTService(serviceInfo);

@@ -174,0 +163,0 @@ });

@@ -0,1 +1,3 @@

/// <reference types="node" />
import { EventEmitter } from "events";
import { BluetoothDevice } from "./device";

@@ -8,3 +10,4 @@ import { BluetoothRemoteGATTService } from "./service";

*/
export interface Adapter {
export interface Adapter extends EventEmitter {
getEnabled: (completeFn: (enabled: boolean) => void) => void;
startScan: (serviceUUIDs: Array<string>, foundFn: (device: Partial<BluetoothDevice>) => void, completeFn?: () => void, errorFn?: (errorMsg: string) => void) => void;

@@ -28,3 +31,4 @@ stopScan: (errorFn?: (errorMsg: string) => void) => void;

*/
export declare class NobleAdapter implements Adapter {
export declare class NobleAdapter extends EventEmitter implements Adapter {
static EVENT_ENABLED: string;
private deviceHandles;

@@ -37,2 +41,5 @@ private serviceHandles;

private initialised;
private enabled;
constructor();
private readonly state;
private init(completeFn);

@@ -43,2 +50,3 @@ private checkForError(errorFn, continueFn?);

private discover(deviceInfo);
getEnabled(completeFn: (enabled: boolean) => void): void;
startScan(serviceUUIDs: Array<string>, foundFn: (device: Partial<BluetoothDevice>) => void, completeFn?: () => void, errorFn?: (errorMsg: string) => void): void;

@@ -45,0 +53,0 @@ stopScan(_errorFn?: (errorMsg: string) => void): void;

@@ -0,10 +1,29 @@

import { EventDispatcher } from "./dispatcher";
import { BluetoothDevice } from "./device";
export interface BluetoothLEScanFilterInit {
services?: Array<string>;
name?: string;
namePrefix?: string;
}
export interface RequestDeviceOptions {
acceptAllDevices: boolean;
deviceFound: (device: BluetoothDevice, selectFn: any) => void;
filters: Array<any>;
optionalServices: Array<any>;
scanTime: any;
acceptAllDevices?: boolean;
deviceFound?: (device: BluetoothDevice, selectFn: any) => boolean;
filters?: Array<BluetoothLEScanFilterInit>;
optionalServices?: Array<any>;
scanTime?: any;
}
export declare function requestDevice(options: RequestDeviceOptions): Promise<BluetoothDevice>;
export declare function cancelRequest(): Promise<void>;
export declare class Bluetooth extends EventDispatcher {
readonly referringDevice: BluetoothDevice;
/**
* Bluetooth Availability Changed event
* @event
*/
static EVENT_AVAILABILITY: string;
private defaultScanTime;
private scanner;
constructor(referringDevice?: BluetoothDevice);
private filterDevice(options, deviceInfo, validServices);
getAvailability(): Promise<boolean>;
requestDevice(options: RequestDeviceOptions): Promise<BluetoothDevice>;
cancelRequest(): Promise<void>;
}

@@ -1,5 +0,5 @@

import { Emitter } from "./emitter";
import { EventDispatcher } from "./dispatcher";
import { BluetoothRemoteGATTService } from "./service";
import { BluetoothRemoteGATTDescriptor } from "./descriptor";
export declare class BluetoothRemoteGATTCharacteristic extends Emitter {
export declare class BluetoothRemoteGATTCharacteristic extends EventDispatcher {
/**

@@ -10,14 +10,5 @@ * Characteristic Value Changed event

static EVENT_CHANGED: string;
/**
* @hidden
*/
_handle: string;
/**
* @hidden
*/
_descriptors: Array<BluetoothRemoteGATTDescriptor>;
service: BluetoothRemoteGATTService;
uuid: any;
value: any;
properties: {
readonly service: BluetoothRemoteGATTService;
readonly uuid: any;
readonly properties: {
broadcast: boolean;

@@ -33,12 +24,14 @@ read: boolean;

};
/**
* @hidden
*/
private _value;
readonly value: DataView;
private handle;
private descriptors;
constructor(init?: Partial<BluetoothRemoteGATTCharacteristic>);
private setValue(value?, emit?);
getDescriptor(descriptorUUID: any): Promise<BluetoothRemoteGATTDescriptor>;
getDescriptors(descriptorUUID: any): Promise<Array<BluetoothRemoteGATTDescriptor>>;
readValue(): Promise<DataView>;
writeValue(bufferSource: ArrayBuffer | ArrayBufferView): Promise<{}>;
writeValue(bufferSource: ArrayBuffer | ArrayBufferView): Promise<void>;
startNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;
stopNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;
}
import { BluetoothRemoteGATTCharacteristic } from "./characteristic";
export declare class BluetoothRemoteGATTDescriptor {
/**
* @hidden
*/
_handle: string;
characteristic: BluetoothRemoteGATTCharacteristic;
uuid: any;
value: any;
/**
* @hidden
*/
readonly characteristic: BluetoothRemoteGATTCharacteristic;
readonly uuid: string;
private _value;
readonly value: DataView;
private handle;
constructor(init?: Partial<BluetoothRemoteGATTDescriptor>);
readValue(): Promise<DataView>;
writeValue(bufferSource: ArrayBuffer | ArrayBufferView): Promise<{}>;
writeValue(bufferSource: ArrayBuffer | ArrayBufferView): Promise<void>;
}

@@ -1,4 +0,5 @@

import { Emitter } from "./emitter";
import { EventDispatcher } from "./dispatcher";
import { Bluetooth } from "./bluetooth";
import { BluetoothRemoteGATTServer } from "./server";
export declare class BluetoothDevice extends Emitter {
export declare class BluetoothDevice extends EventDispatcher {
/**

@@ -9,18 +10,28 @@ * Server Disconnected event

static EVENT_DISCONNECTED: string;
readonly id: string;
readonly name: string;
readonly gatt: BluetoothRemoteGATTServer;
readonly watchingAdvertisements: boolean;
/**
* @hidden
*/
_handle: string;
readonly adData: {
rssi?: number;
txPower?: null;
serviceData?: Map<string, DataView>;
manufacturerData?: Map<string, DataView>;
};
/**
* @hidden
*/
_allowedServices: Array<string>;
id: string;
name: string;
gatt: BluetoothRemoteGATTServer;
uuids: Array<string>;
readonly _bluetooth: Bluetooth;
/**
* @hidden
*/
readonly _allowedServices: Array<string>;
/**
* @hidden
*/
_serviceUUIDs: Array<string>;
constructor(init?: Partial<BluetoothDevice>);
}

@@ -215,5 +215,5 @@ export declare enum bluetoothServices {

}
export declare function getCanonicalUUID(uuid: any): any;
export declare function getServiceUUID(uuid: any): any;
export declare function getCharacteristicUUID(uuid: any): any;
export declare function getDescriptorUUID(uuid: any): any;
export declare function getCanonicalUUID(uuid: string | number): string;
export declare function getServiceUUID(uuid: string | number): string;
export declare function getCharacteristicUUID(uuid: string | number): string;
export declare function getDescriptorUUID(uuid: string | number): string;

@@ -1,2 +0,13 @@

export * from "./bluetooth";
import { Bluetooth } from "./bluetooth";
/**
* Default bluetooth instance synonymous with `navigator.bluetooth`
*/
export declare const bluetooth: Bluetooth;
/**
* Bluetooth class for creating new instances
*/
export { Bluetooth };
/**
* Helper methods and enums
*/
export * from "./helpers";
import { BluetoothDevice } from "./device";
import { BluetoothRemoteGATTService } from "./service";
export declare class BluetoothRemoteGATTServer {
/**
* @hidden
*/
_services: Array<BluetoothRemoteGATTService>;
device: BluetoothDevice;
connected: boolean;
private handle;
private services;
constructor(device: BluetoothDevice);
connect(): Promise<BluetoothRemoteGATTServer>;

@@ -11,0 +10,0 @@ disconnect(): void;

@@ -1,5 +0,5 @@

import { Emitter } from "./emitter";
import { EventDispatcher } from "./dispatcher";
import { BluetoothDevice } from "./device";
import { BluetoothRemoteGATTCharacteristic } from "./characteristic";
export declare class BluetoothRemoteGATTService extends Emitter {
export declare class BluetoothRemoteGATTService extends EventDispatcher {
/**

@@ -20,20 +20,8 @@ * Service Added event

static EVENT_REMOVED: string;
/**
* @hidden
*/
_handle: string;
/**
* @hidden
*/
_services: Array<BluetoothRemoteGATTService>;
/**
* @hidden
*/
_characteristics: Array<BluetoothRemoteGATTCharacteristic>;
device: BluetoothDevice;
uuid: any;
isPrimary: boolean;
/**
* @hidden
*/
readonly device: BluetoothDevice;
readonly uuid: string;
readonly isPrimary: boolean;
private handle;
private services;
private characteristics;
constructor(init?: Partial<BluetoothRemoteGATTService>);

@@ -40,0 +28,0 @@ getCharacteristic(characteristicUUID: any): Promise<BluetoothRemoteGATTCharacteristic>;

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc