Socket
Socket
Sign inDemoInstall

webbluetooth

Package Overview
Dependencies
116
Maintainers
1
Versions
58
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.1.1-next.f9fb064.0 to 2.2.0

.eslintignore

44

package.json
{
"name": "webbluetooth",
"version": "2.1.1-next.f9fb064.0",
"version": "2.2.0",
"description": "Node.js implementation of the Web Bluetooth Specification",

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

"license": "MIT",
"main": "dist/webbluetooth.umd.js",
"module": "dist/webbluetooth.esm.js",
"types": "types/index.d.ts",
"main": "dist/index.js",
"repository": {

@@ -21,27 +19,25 @@ "type": "git",

],
"engines": {
"node": ">=10.16.0"
},
"scripts": {
"build": "rollup -c && typedoc src",
"watch": "rollup -c -w"
"prepare": "yarn build",
"clean": "git clean -f -x ./dist ./docs",
"build": "tsc && yarn lint && yarn docs",
"watch": "tsc -w --preserveWatchOutput",
"lint": "eslint . --ext .ts,.tsx",
"docs": "typedoc"
},
"engines": {
"node": ">=8.14.0"
"dependencies": {
"@abandonware/noble": "1.9.2-15",
"@types/node": "^10.16.0",
"@types/web-bluetooth": "^0.0.14"
},
"devDependencies": {
"rollup": "^1.10.1",
"rollup-plugin-delete": "^0.2.2",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-sourcemaps": "^0.4.2",
"rollup-plugin-terser": "^5.1.3",
"rollup-plugin-tslint": "^0.2.2",
"rollup-plugin-typescript2": "^0.21.0",
"tslint": "^6.1.1",
"tslint-eslint-rules": "^5.4.0",
"typedoc": "^0.17.4",
"typescript": "^3.8.3"
},
"dependencies": {
"@abandonware/noble": "1.9.2-7",
"@types/node": "^8.10.60",
"@types/web-bluetooth": "^0.0.5"
"@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0",
"eslint": "^7.29.0",
"typedoc": "^0.20",
"typescript": "~4.2.4"
}
}
# Node Web Bluetooth
Node.js implementation of the Web Bluetooth Specification
[![Circle CI](https://circleci.com/gh/thegecko/webbluetooth.svg?style=shield)](https://circleci.com/gh/thegecko/webbluetooth/)
[![Build Status](https://github.com/thegecko/webbluetooth/workflows/ci/badge.svg)](https://github.com/thegecko/webbluetooth/actions)
[![npm](https://img.shields.io/npm/dm/webbluetooth.svg)](https://www.npmjs.com/package/webbluetooth)

@@ -10,3 +10,3 @@ [![Licence MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](http://opensource.org/licenses/MIT)

[Node.js > v8.14.0](https://nodejs.org), which includes `npm`.
[Node.js > v10.16.0](https://nodejs.org), which includes `npm`.

@@ -25,2 +25,54 @@ ## Installation

## Usage
The module exports a default `navigator.bluetooth` instance, the `Bluetooth` class to allow you to instantiate your own bluetooth instances and some helper methods:
- [bluetooth](globals.html#bluetooth)
- [Bluetooth()](classes/bluetooth.html)
- [getCanonicalUUID()](globals.html#getcanonicaluuid)
- [getServiceUUID()](globals.html#getserviceuuid)
- [getCharacteristicUUID()](globals.html#getcharacteristicuuid)
- [getDescriptorUUID()](globals.html#getdescriptoruuid)
### Using the default bluetooth instance
To use existing Web Bluetooth scripts, you can simply use the default `bluetooth` instance in place of the `navigator.bluetooth` object:
```JavaScript
const bluetooth = require("webbluetooth").bluetooth;
const device = await bluetooth.requestDevice({
filters:[{ services:[ "heart_rate" ] }]
});
const server = await device.gatt.connect();
...
```
The first device matching the filters will be returned.
### Creating your own bluetooth instances
You may want to create your own instance of the `Bluetooth` class. For example, to inject a device chooser function or control the referring device:
```JavaScript
const Bluetooth = require("webbluetooth").Bluetooth;
const deviceFound = (device, selectFn) => {
// If device can be automatically selected, do so by returning true
if (device.name === "myName") return true;
// Otherwise store the selectFn somewhere and execute it later to select this device
};
const bluetooth = new Bluetooth({ deviceFound });
const device = await bluetooth.requestDevice({
filters:[{ services:[ "heart_rate" ] }]
});
const server = await device.gatt.connect();
...
```
## Specification

@@ -39,3 +91,3 @@

- [x] requestDevice()
- [ ] getDevices()
- [x] getDevices()
- [x] RequestDeviceOptions.name

@@ -54,2 +106,3 @@ - [x] RequestDeviceOptions.namePrefix

- [x] gatt
- [ ] forget
- [ ] watchingAdvertisements - specification unstable

@@ -56,0 +109,0 @@ - [ ] watchAdvertisements() - specification unstable

@@ -26,9 +26,9 @@ /*

import { platform } from "os";
import { EventEmitter } from "events";
import { getCanonicalUUID } from "./helpers";
import { BluetoothDevice } from "./device";
import { BluetoothRemoteGATTService } from "./service";
import { BluetoothRemoteGATTCharacteristic } from "./characteristic";
import * as noble from "@abandonware/noble";
import { platform } from 'os';
import { EventEmitter } from 'events';
import { getCanonicalUUID } from './helpers';
import { BluetoothDevice } from './device';
import { BluetoothRemoteGATTService } from './service';
import { BluetoothRemoteGATTCharacteristic } from './characteristic';
import * as noble from '@abandonware/noble';

@@ -39,17 +39,17 @@ /**

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;
stopScan: (errorFn?: (errorMsg: string) => void) => void;
connect: (handle: string, connectFn: () => void, disconnectFn: () => void, errorFn?: (errorMsg: string) => void) => void;
disconnect: (handle: string, errorFn?: (errorMsg: string) => void) => void;
discoverServices: (handle: string, serviceUUIDs: Array<string>, completeFn: (services: Array<Partial<BluetoothRemoteGATTService>>) => void, errorFn?: (errorMsg: string) => void) => void;
discoverIncludedServices: (handle: string, serviceUUIDs: Array<string>, completeFn: (services: Array<Partial<BluetoothRemoteGATTService>>) => void, errorFn?: (errorMsg: string) => void) => void;
discoverCharacteristics: (handle: string, characteristicUUIDs: Array<string>, completeFn: (characteristics: Array<Partial<BluetoothRemoteGATTCharacteristic>>) => void, errorFn?: (errorMsg: string) => void) => void;
discoverDescriptors: (handle: string, descriptorUUIDs: Array<string>, completeFn: (descriptors: Array<Partial<BluetoothRemoteGATTDescriptor>>) => void, errorFn?: (errorMsg: string) => void) => void;
readCharacteristic: (handle: string, completeFn: (value: DataView) => void, errorFn?: (errorMsg: string) => void) => void;
writeCharacteristic: (handle: string, value: DataView, completeFn?: () => void, errorFn?: (errorMsg: string) => void, withoutResponse?: boolean) => void;
enableNotify: (handle: string, notifyFn: () => void, completeFn?: () => void, errorFn?: (errorMsg: string) => void) => void;
disableNotify: (handle: string, completeFn?: () => void, errorFn?: (errorMsg: string) => void) => void;
readDescriptor: (handle: string, completeFn: (value: DataView) => void, errorFn?: (errorMsg: string) => void) => void;
writeDescriptor: (handle: string, value: DataView, completeFn?: () => void, errorFn?: (errorMsg: string) => void) => void;
getEnabled: () => Promise<boolean>;
startScan: (serviceUUIDs: Array<string>, foundFn: (device: Partial<BluetoothDevice>) => void) => Promise<void>;
stopScan: () => void;
connect: (handle: string, disconnectFn?: () => void) => Promise<void>;
disconnect: (handle: string) => Promise<void>;
discoverServices: (handle: string, serviceUUIDs?: Array<string>) => Promise<Array<Partial<BluetoothRemoteGATTService>>>;
discoverIncludedServices: (handle: string, serviceUUIDs?: Array<string>) => Promise<Array<Partial<BluetoothRemoteGATTService>>>;
discoverCharacteristics: (handle: string, characteristicUUIDs?: Array<string>) => Promise<Array<Partial<BluetoothRemoteGATTCharacteristic>>>;
discoverDescriptors: (handle: string, descriptorUUIDs?: Array<string>) => Promise<Array<Partial<BluetoothRemoteGATTDescriptor>>>;
readCharacteristic: (handle: string) => Promise<DataView>;
writeCharacteristic: (handle: string, value: DataView, withoutResponse?: boolean) => Promise<void>;
enableNotify: (handle: string, notifyFn: () => void) => Promise<void>;
disableNotify: (handle: string) => Promise<void>;
readDescriptor: (handle: string) => Promise<DataView>;
writeDescriptor: (handle: string, value: DataView) => Promise<void>;
}

@@ -62,12 +62,12 @@

public static EVENT_ENABLED: string = "enabledchanged";
public static EVENT_ENABLED = 'enabledchanged';
private deviceHandles: {} = {};
private serviceHandles: {} = {};
private characteristicHandles: {} = {};
private descriptorHandles: {} = {};
private charNotifies: {} = {};
private discoverFn: (device: noble.Peripheral) => void = null;
private initialised: boolean = false;
private enabled: boolean = false;
private deviceHandles = new Map<string, noble.Peripheral>();
private serviceHandles = new Map<string, noble.Service>();
private characteristicHandles = new Map<string, noble.Characteristic>();
private descriptorHandles = new Map<string, noble.Descriptor>();
private charNotifies = new Map<string, (value: DataView) => void>();
private discoverFn: (device: noble.Peripheral) => void | undefined;
private initialised = false;
private enabled = false;
private os: string = platform();

@@ -78,3 +78,3 @@

this.enabled = this.state;
noble.on("stateChange", () => {
noble.on('stateChange', () => {
if (this.enabled !== this.state) {

@@ -88,25 +88,15 @@ this.enabled = this.state;

private get state(): boolean {
return (noble.state === "poweredOn");
return (noble.state === 'poweredOn');
}
private init(completeFn: () => any): void {
if (this.initialised) return completeFn();
noble.on("discover", deviceInfo => {
private init(): void {
if (this.initialised) {
return;
}
noble.on('discover', deviceInfo => {
if (this.discoverFn) this.discoverFn(deviceInfo);
});
this.initialised = true;
completeFn();
}
private checkForError(errorFn, continueFn?, delay?: number) {
return function(error) {
if (error) errorFn(error);
else if (typeof continueFn === "function") {
const args = [].slice.call(arguments, 1);
if (delay === null) continueFn.apply(this, args);
else setTimeout(() => continueFn.apply(this, args), delay);
}
};
}
private bufferToDataView(buffer: Buffer): DataView {

@@ -146,11 +136,5 @@ // Buffer to ArrayBuffer

private deviceToBluetoothDevice(deviceInfo): Partial<BluetoothDevice> {
const deviceID = (deviceInfo.address && deviceInfo.address !== "unknown") ? deviceInfo.address : deviceInfo.id;
const deviceID = (deviceInfo.address && deviceInfo.address !== 'unknown') ? deviceInfo.address : deviceInfo.id;
const serviceUUIDs = deviceInfo.advertisement.serviceUuids ? deviceInfo.advertisement.serviceUuids.map(serviceUUID => getCanonicalUUID(serviceUUID)) : [];
const serviceUUIDs = [];
if (deviceInfo.advertisement.serviceUuids) {
deviceInfo.advertisement.serviceUuids.forEach(serviceUUID => {
serviceUUIDs.push(getCanonicalUUID(serviceUUID));
});
}
const manufacturerData = new Map();

@@ -163,3 +147,3 @@ if (deviceInfo.advertisement.manufacturerData) {

const buffer = deviceInfo.advertisement.manufacturerData.slice(2);
manufacturerData.set(("0000" + company.toString(16)).slice(-4), this.bufferToDataView(buffer));
manufacturerData.set(('0000' + company.toString(16)).slice(-4), this.bufferToDataView(buffer));
}

@@ -169,5 +153,5 @@

if (deviceInfo.advertisement.serviceData) {
deviceInfo.advertisement.serviceData.forEach(serviceAdvert => {
for (const serviceAdvert of deviceInfo.advertisement.serviceData) {
serviceData.set(getCanonicalUUID(serviceAdvert.uuid), this.bufferToDataView(serviceAdvert.data));
});
}
}

@@ -188,16 +172,11 @@

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

@@ -208,4 +187,4 @@ this.discoverFn = deviceInfo => {

if (!this.deviceHandles[device.id]) {
this.deviceHandles[device.id] = deviceInfo;
if (!this.deviceHandles.has(device.id)) {
this.deviceHandles.set(device.id, deviceInfo);
// Only call the found function the first time we find a valid device

@@ -217,210 +196,239 @@ foundFn(device);

this.init(() => {
this.deviceHandles = {};
function stateCB() {
if (this.state === true) {
// Noble doesn't correctly match short and canonical UUIDs on Linux, so we need to check ourselves
// Continually scan to pick up all advertised UUIDs
noble.startScanning([], true, this.checkForError(errorFn, completeFn));
} else {
errorFn("adapter not enabled");
}
}
this.init();
this.deviceHandles.clear();
if (noble.state === 'unknown' || noble.state === 'poweredOff') {
await new Promise(resolve => noble.once('stateChange', () => resolve(undefined)));
}
if (noble.state === "unknown" || noble.state === "poweredOff") {
// tslint:disable-next-line:no-string-literal
noble["once"]("stateChange", stateCB.bind(this));
} else {
stateCB.call(this);
}
});
if (this.state === false) {
throw new Error('adapter not enabled');
}
// Noble doesn't correctly match short and canonical UUIDs on Linux, so we need to check ourselves
// Continually scan to pick up all advertised UUIDs
await noble.startScanningAsync([], true);
}
public stopScan(_errorFn?: (errorMsg: string) => void): void {
this.discoverFn = null;
this.discoverFn = undefined;
noble.stopScanning();
}
public connect(handle: string, connectFn: () => void, disconnectFn: () => void, errorFn?: (errorMsg: string) => void): void {
const baseDevice = this.deviceHandles[handle];
baseDevice.removeAllListeners("connect");
baseDevice.removeAllListeners("disconnect");
baseDevice.once("connect", connectFn);
baseDevice.once("disconnect", () => {
this.serviceHandles = {};
this.characteristicHandles = {};
this.descriptorHandles = {};
this.charNotifies = {};
disconnectFn();
});
baseDevice.connect(this.checkForError(errorFn));
public connect(handle: string, disconnectFn?: () => void): Promise<void> {
const baseDevice = this.deviceHandles.get(handle);
baseDevice.removeAllListeners('connect');
baseDevice.removeAllListeners('disconnect');
if (disconnectFn) {
baseDevice.once('disconnect', () => {
this.serviceHandles.clear();
this.characteristicHandles.clear();
this.descriptorHandles.clear();
this.charNotifies.clear();
disconnectFn();
});
}
return baseDevice.connectAsync();
}
public disconnect(handle: string, errorFn?: (errorMsg: string) => void): void {
const baseDevice = this.deviceHandles[handle];
baseDevice.disconnect(this.checkForError(errorFn));
public disconnect(handle: string): Promise<void> {
const baseDevice = this.deviceHandles.get(handle);
return baseDevice.disconnectAsync();
}
public discoverServices(handle: string, serviceUUIDs: Array<string>, completeFn: (services: Array<Partial<BluetoothRemoteGATTService>>) => void, errorFn?: (errorMsg: string) => void): void {
const baseDevice = this.deviceHandles[handle];
baseDevice.discoverServices([], this.checkForError(errorFn, services => {
const discovered = [];
services.forEach(serviceInfo => {
const serviceUUID = getCanonicalUUID(serviceInfo.uuid);
public async discoverServices(handle: string, serviceUUIDs?: Array<string>): Promise<Array<Partial<BluetoothRemoteGATTService>>> {
const baseDevice = this.deviceHandles.get(handle);
const services = await baseDevice.discoverServicesAsync();
const discovered = [];
if (serviceUUIDs.length === 0 || serviceUUIDs.indexOf(serviceUUID) >= 0) {
if (!this.serviceHandles[serviceUUID]) this.serviceHandles[serviceUUID] = serviceInfo;
for (const serviceInfo of services) {
const serviceUUID = getCanonicalUUID(serviceInfo.uuid);
discovered.push({
uuid: serviceUUID,
primary: true
});
if (!serviceUUIDs || serviceUUIDs.length === 0 || serviceUUIDs.indexOf(serviceUUID) >= 0) {
if (!this.serviceHandles.has(serviceUUID)) {
this.serviceHandles.set(serviceUUID, serviceInfo);
}
});
completeFn(discovered);
}));
discovered.push({
uuid: serviceUUID,
primary: true
});
}
}
return discovered;
}
public discoverIncludedServices(handle: string, serviceUUIDs: Array<string>, completeFn: (services: Array<Partial<BluetoothRemoteGATTService>>) => void, errorFn?: (errorMsg: string) => void): void {
const serviceInfo = this.serviceHandles[handle];
serviceInfo.discoverIncludedServices([], this.checkForError(errorFn, services => {
public async discoverIncludedServices(handle: string, serviceUUIDs?: Array<string>): Promise<Array<Partial<BluetoothRemoteGATTService>>> {
const serviceInfo = this.serviceHandles.get(handle);
const services = await serviceInfo.discoverIncludedServicesAsync();
const discovered = [];
const discovered = [];
services.forEach(service => {
const serviceUUID = getCanonicalUUID(service.uuid);
// TODO: check retiurn here!
for (const service of services) {
const serviceUUID = getCanonicalUUID(service);
if (serviceUUIDs.length === 0 || serviceUUIDs.indexOf(serviceUUID) >= 0) {
if (!this.serviceHandles[serviceUUID]) this.serviceHandles[serviceUUID] = service;
discovered.push({
uuid: serviceUUID,
primary: false
});
if (!serviceUUIDs || serviceUUIDs.length === 0 || serviceUUIDs.indexOf(serviceUUID) >= 0) {
/*
if (!this.serviceHandles.has(serviceUUID)) {
this.serviceHandles.set(serviceUUID, service);
}
}, this);
*/
completeFn(discovered);
}));
discovered.push({
uuid: serviceUUID,
primary: false
});
}
}
return discovered;
}
public discoverCharacteristics(handle: string, characteristicUUIDs: Array<string>, completeFn: (characteristics: Array<Partial<BluetoothRemoteGATTCharacteristic>>) => void, errorFn?: (errorMsg: string) => void): void {
const serviceInfo = this.serviceHandles[handle];
serviceInfo.discoverCharacteristics([], this.checkForError(errorFn, characteristics => {
public async discoverCharacteristics(handle: string, characteristicUUIDs?: Array<string>): Promise<Array<Partial<BluetoothRemoteGATTCharacteristic>>> {
const serviceInfo = this.serviceHandles.get(handle);
const characteristics = await serviceInfo.discoverCharacteristicsAsync();
const discovered = [];
const discovered = [];
characteristics.forEach(characteristicInfo => {
const charUUID = getCanonicalUUID(characteristicInfo.uuid);
for (const characteristicInfo of characteristics) {
const charUUID = getCanonicalUUID(characteristicInfo.uuid);
if (characteristicUUIDs.length === 0 || characteristicUUIDs.indexOf(charUUID) >= 0) {
if (!this.characteristicHandles[charUUID]) this.characteristicHandles[charUUID] = characteristicInfo;
if (!characteristicUUIDs || characteristicUUIDs.length === 0 || characteristicUUIDs.indexOf(charUUID) >= 0) {
if (!this.characteristicHandles.has(charUUID)) {
this.characteristicHandles.set(charUUID, characteristicInfo);
}
discovered.push({
uuid: charUUID,
properties: {
broadcast: (characteristicInfo.properties.indexOf("broadcast") >= 0),
read: (characteristicInfo.properties.indexOf("read") >= 0),
writeWithoutResponse: (characteristicInfo.properties.indexOf("writeWithoutResponse") >= 0),
write: (characteristicInfo.properties.indexOf("write") >= 0),
notify: (characteristicInfo.properties.indexOf("notify") >= 0),
indicate: (characteristicInfo.properties.indexOf("indicate") >= 0),
authenticatedSignedWrites: (characteristicInfo.properties.indexOf("authenticatedSignedWrites") >= 0),
reliableWrite: (characteristicInfo.properties.indexOf("reliableWrite") >= 0),
writableAuxiliaries: (characteristicInfo.properties.indexOf("writableAuxiliaries") >= 0)
}
});
discovered.push({
uuid: charUUID,
properties: {
broadcast: (characteristicInfo.properties.indexOf('broadcast') >= 0),
read: (characteristicInfo.properties.indexOf('read') >= 0),
writeWithoutResponse: (characteristicInfo.properties.indexOf('writeWithoutResponse') >= 0),
write: (characteristicInfo.properties.indexOf('write') >= 0),
notify: (characteristicInfo.properties.indexOf('notify') >= 0),
indicate: (characteristicInfo.properties.indexOf('indicate') >= 0),
authenticatedSignedWrites: (characteristicInfo.properties.indexOf('authenticatedSignedWrites') >= 0),
reliableWrite: (characteristicInfo.properties.indexOf('reliableWrite') >= 0),
writableAuxiliaries: (characteristicInfo.properties.indexOf('writableAuxiliaries') >= 0)
}
});
characteristicInfo.on("data", (data, isNotification) => {
if (isNotification === true && typeof this.charNotifies[charUUID] === "function") {
const dataView = this.bufferToDataView(data);
this.charNotifies[charUUID](dataView);
}
});
}
}, this);
characteristicInfo.on('data', (data, isNotification) => {
if (isNotification === true && this.charNotifies.has(charUUID)) {
const dataView = this.bufferToDataView(data);
this.charNotifies.get(charUUID)(dataView);
}
});
}
}
completeFn(discovered);
}));
return discovered;
}
public discoverDescriptors(handle: string, descriptorUUIDs: Array<string>, completeFn: (descriptors: Array<Partial<BluetoothRemoteGATTDescriptor>>) => void, errorFn?: (errorMsg: string) => void): void {
const characteristicInfo = this.characteristicHandles[handle];
characteristicInfo.discoverDescriptors(this.checkForError(errorFn, descriptors => {
public async discoverDescriptors(handle: string, descriptorUUIDs?: Array<string>): Promise<Array<Partial<BluetoothRemoteGATTDescriptor>>> {
const characteristicInfo = this.characteristicHandles.get(handle);
const descriptors = await characteristicInfo.discoverDescriptorsAsync();
const discovered = [];
const discovered = [];
descriptors.forEach(descriptorInfo => {
const descUUID = getCanonicalUUID(descriptorInfo.uuid);
for (const descriptorInfo of descriptors) {
const descUUID = getCanonicalUUID(descriptorInfo.uuid);
if (descriptorUUIDs.length === 0 || descriptorUUIDs.indexOf(descUUID) >= 0) {
const descHandle = characteristicInfo.uuid + "-" + descriptorInfo.uuid;
if (!this.descriptorHandles[descHandle]) this.descriptorHandles[descHandle] = descriptorInfo;
discovered.push({
uuid: descUUID
});
if (!descriptorUUIDs || descriptorUUIDs.length === 0 || descriptorUUIDs.indexOf(descUUID) >= 0) {
const descHandle = characteristicInfo.uuid + '-' + descriptorInfo.uuid;
if (!this.descriptorHandles.has(descHandle)) {
this.descriptorHandles.set(descHandle, descriptorInfo);
}
}, this);
completeFn(discovered);
}));
discovered.push({
uuid: descUUID
});
}
}
return discovered;
}
public readCharacteristic(handle: string, completeFn: (value: DataView) => void, errorFn?: (errorMsg: string) => void): void {
this.characteristicHandles[handle].read(this.checkForError(errorFn, data => {
const dataView = this.bufferToDataView(data);
completeFn(dataView);
}));
public async readCharacteristic(handle: string): Promise<DataView> {
const characteristic = this.characteristicHandles.get(handle);
const data = await characteristic.readAsync();
const dataView = this.bufferToDataView(data);
return dataView;
}
public writeCharacteristic(handle: string, value: DataView, completeFn?: () => void, errorFn?: (errorMsg: string) => void, withoutResponse?: boolean): void {
public async writeCharacteristic(handle: string, value: DataView, withoutResponse = false): Promise<void> {
const buffer = this.dataViewToBuffer(value);
const characteristic = this.characteristicHandles[handle];
const characteristic = this.characteristicHandles.get(handle);
if (withoutResponse === undefined) {
// writeWithoutResponse and authenticatedSignedWrites don't require a response
withoutResponse = characteristic.properties.indexOf("writeWithoutResponse") >= 0
|| characteristic.properties.indexOf("authenticatedSignedWrites") >= 0;
withoutResponse = characteristic.properties.indexOf('writeWithoutResponse') >= 0
|| characteristic.properties.indexOf('authenticatedSignedWrites') >= 0;
}
await characteristic.writeAsync(buffer, withoutResponse);
// TODO: check still needed
// Add a small delay for writing without response when not on MacOS
const delay = (this.os !== "darwin" && withoutResponse) ? 25 : null;
characteristic.write(buffer, withoutResponse, this.checkForError(errorFn, completeFn, delay));
if (this.os !== 'darwin' && withoutResponse) {
await new Promise(resolve => setTimeout(resolve, 25));
}
}
public enableNotify(handle: string, notifyFn: (value: DataView) => void, completeFn?: () => void, errorFn?: (errorMsg: string) => void): void {
if (this.charNotifies[handle]) {
this.charNotifies[handle] = notifyFn;
return completeFn();
public enableNotify(handle: string, notifyFn: (value: DataView) => void): Promise<void> {
if (this.charNotifies.has(handle)) {
this.charNotifies.set(handle, notifyFn);
return Promise.resolve();
}
this.characteristicHandles[handle].once("notify", state => {
if (state !== true) return errorFn("notify failed to enable");
this.charNotifies[handle] = notifyFn;
completeFn();
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
const characteristic = this.characteristicHandles.get(handle);
// TODO: check type emitted
characteristic.once('notify', state => {
if (state !== 'true') {
reject('notify failed to enable');
}
this.charNotifies.set(handle, notifyFn);
resolve(undefined);
});
await characteristic.notifyAsync(true);
});
this.characteristicHandles[handle].notify(true, this.checkForError(errorFn));
}
public disableNotify(handle: string, completeFn?: () => void, errorFn?: (errorMsg: string) => void): void {
if (!this.charNotifies[handle]) {
return completeFn();
public disableNotify(handle: string): Promise<void> {
if (!this.charNotifies.has(handle)) {
return Promise.resolve();
}
this.characteristicHandles[handle].once("notify", state => {
if (state !== false) return errorFn("notify failed to disable");
if (this.charNotifies[handle]) delete this.charNotifies[handle];
completeFn();
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
const characteristic = this.characteristicHandles.get(handle);
// TODO: check type emitted
characteristic.once('notify', state => {
if (state !== 'false') {
reject('notify failed to disable');
}
if (this.charNotifies.has(handle)) {
this.charNotifies.delete(handle);
}
resolve(undefined);
});
await characteristic.notifyAsync(false);
});
this.characteristicHandles[handle].notify(false, this.checkForError(errorFn));
}
public readDescriptor(handle: string, completeFn: (value: DataView) => void, errorFn?: (errorMsg: string) => void): void {
this.descriptorHandles[handle].readValue(this.checkForError(errorFn, data => {
const dataView = this.bufferToDataView(data);
completeFn(dataView);
}));
public async readDescriptor(handle: string): Promise<DataView> {
const data = await this.descriptorHandles.get(handle).readValueAsync();
const dataView = this.bufferToDataView(data);
return dataView;
}
public writeDescriptor(handle: string, value: DataView, completeFn?: () => void, errorFn?: (errorMsg: string) => void): void {
public writeDescriptor(handle: string, value: DataView): Promise<void> {
const buffer = this.dataViewToBuffer(value);
this.descriptorHandles[handle].writeValue(buffer, this.checkForError(errorFn, completeFn));
return this.descriptorHandles.get(handle).writeValueAsync(buffer);
}

@@ -427,0 +435,0 @@ }

@@ -26,8 +26,8 @@ /*

import { EventDispatcher, TypedDispatcher } from "./dispatcher";
import { BluetoothDevice, BluetoothDeviceEvents } from "./device";
import { getServiceUUID } from "./helpers";
import { adapter, NobleAdapter } from "./adapter";
import { W3CBluetooth } from "./interfaces";
import { DOMEvent } from "./events";
import { EventDispatcher, TypedDispatcher } from './dispatcher';
import { BluetoothDevice, BluetoothDeviceEvents } from './device';
import { getServiceUUID } from './helpers';
import { adapter, NobleAdapter } from './adapter';
import { W3CBluetooth } from './interfaces';
import { DOMEvent } from './events';

@@ -73,3 +73,3 @@ /**

*/
public static EVENT_AVAILABILITY: string = "availabilitychanged";
public static EVENT_AVAILABILITY = 'availabilitychanged';

@@ -81,5 +81,5 @@ /**

private deviceFound: (device: BluetoothDevice, selectFn: () => void) => boolean = null;
private deviceFound: (device: BluetoothDevice, selectFn: () => void) => boolean = undefined;
private scanTime: number = 10.24 * 1000;
private scanner = null;
private scanner = undefined;

@@ -89,6 +89,6 @@ private _oncharacteristicvaluechanged: (ev: Event) => void;

if (this._oncharacteristicvaluechanged) {
this.removeEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.removeEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}
this._oncharacteristicvaluechanged = fn;
this.addEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.addEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}

@@ -99,6 +99,6 @@

if (this._onserviceadded) {
this.removeEventListener("serviceadded", this._onserviceadded);
this.removeEventListener('serviceadded', this._onserviceadded);
}
this._onserviceadded = fn;
this.addEventListener("serviceadded", this._onserviceadded);
this.addEventListener('serviceadded', this._onserviceadded);
}

@@ -109,6 +109,6 @@

if (this._onservicechanged) {
this.removeEventListener("servicechanged", this._onservicechanged);
this.removeEventListener('servicechanged', this._onservicechanged);
}
this._onservicechanged = fn;
this.addEventListener("servicechanged", this._onservicechanged);
this.addEventListener('servicechanged', this._onservicechanged);
}

@@ -119,6 +119,6 @@

if (this._onserviceremoved) {
this.removeEventListener("serviceremoved", this._onserviceremoved);
this.removeEventListener('serviceremoved', this._onserviceremoved);
}
this._onserviceremoved = fn;
this.addEventListener("serviceremoved", this._onserviceremoved);
this.addEventListener('serviceremoved', this._onserviceremoved);
}

@@ -129,6 +129,6 @@

if (this._ongattserverdisconnected) {
this.removeEventListener("gattserverdisconnected", this._ongattserverdisconnected);
this.removeEventListener('gattserverdisconnected', this._ongattserverdisconnected);
}
this._ongattserverdisconnected = fn;
this.addEventListener("gattserverdisconnected", this._ongattserverdisconnected);
this.addEventListener('gattserverdisconnected', this._ongattserverdisconnected);
}

@@ -139,6 +139,6 @@

if (this._onadvertisementreceived) {
this.removeEventListener("advertisementreceived", this._onadvertisementreceived);
this.removeEventListener('advertisementreceived', this._onadvertisementreceived);
}
this._onadvertisementreceived = fn;
this.addEventListener("advertisementreceived", this._onadvertisementreceived);
this.addEventListener('advertisementreceived', this._onadvertisementreceived);
}

@@ -149,6 +149,6 @@

if (this._onavailabilitychanged) {
this.removeEventListener("availabilitychanged", this._onavailabilitychanged);
this.removeEventListener('availabilitychanged', this._onavailabilitychanged);
}
this._onavailabilitychanged = fn;
this.addEventListener("availabilitychanged", this._onavailabilitychanged);
this.addEventListener('availabilitychanged', this._onavailabilitychanged);
}

@@ -169,7 +169,7 @@

adapter.on(NobleAdapter.EVENT_ENABLED, _value => {
this.dispatchEvent(new DOMEvent(this, "availabilitychanged"));
this.dispatchEvent(new DOMEvent(this, 'availabilitychanged'));
});
}
private filterDevice(filters: Array<BluetoothRequestDeviceFilter>, deviceInfo, validServices) {
private filterDevice(filters: Array<BluetoothLEScanFilter>, deviceInfo, validServices) {
let valid = false;

@@ -210,7 +210,3 @@

public getAvailability(): Promise<boolean> {
return new Promise((resolve, _reject) => {
adapter.getEnabled(enabled => {
resolve(enabled);
});
});
return adapter.getEnabled();
}

@@ -224,68 +220,68 @@

public requestDevice(options: RequestDeviceOptions = { filters: [] }): Promise<BluetoothDevice> {
return new Promise((resolve, reject) => {
if (this.scanner !== undefined) {
throw new Error('requestDevice error: request in progress');
}
if (this.scanner !== null) return reject("requestDevice error: request in progress");
interface Filtered {
filters: Array<BluetoothLEScanFilter>;
optionalServices?: Array<BluetoothServiceUUID>;
}
interface Filtered {
filters: Array<BluetoothRequestDeviceFilter>;
optionalServices?: Array<BluetoothServiceUUID>;
}
interface AcceptAll {
acceptAllDevices: boolean;
optionalServices?: Array<BluetoothServiceUUID>;
}
interface AcceptAll {
acceptAllDevices: boolean;
optionalServices?: Array<BluetoothServiceUUID>;
}
const isFiltered = (maybeFiltered: RequestDeviceOptions): maybeFiltered is Filtered =>
(maybeFiltered as Filtered).filters !== undefined;
const isFiltered = (maybeFiltered: RequestDeviceOptions): maybeFiltered is Filtered =>
(maybeFiltered as Filtered).filters !== undefined;
const isAcceptAll = (maybeAcceptAll: RequestDeviceOptions): maybeAcceptAll is AcceptAll =>
(maybeAcceptAll as AcceptAll).acceptAllDevices === true;
const isAcceptAll = (maybeAcceptAll: RequestDeviceOptions): maybeAcceptAll is AcceptAll =>
(maybeAcceptAll as AcceptAll).acceptAllDevices === true;
let searchUUIDs = [];
let searchUUIDs = [];
if (isFiltered(options)) {
// Must have a filter
if (options.filters.length === 0) {
throw new TypeError('requestDevice error: no filters specified');
}
if (isFiltered(options)) {
// Must have a filter
if (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) {
throw new TypeError('requestDevice error: empty filter 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) {
throw new TypeError('requestDevice error: empty namePrefix 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"));
}
options.filters.forEach(filter => {
if (filter.services) searchUUIDs = searchUUIDs.concat(filter.services.map(getServiceUUID));
options.filters.forEach(filter => {
if (filter.services) searchUUIDs = searchUUIDs.concat(filter.services.map(getServiceUUID));
// Unique-ify
searchUUIDs = searchUUIDs.filter((item, index, array) => {
return array.indexOf(item) === index;
});
// Unique-ify
searchUUIDs = searchUUIDs.filter((item, index, array) => {
return array.indexOf(item) === index;
});
} else if (!isAcceptAll(options)) {
return reject(new TypeError("requestDevice error: specify filters or acceptAllDevices"));
}
});
} else if (!isAcceptAll(options)) {
throw new TypeError('requestDevice error: specify filters or acceptAllDevices');
}
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
let found = false;
adapter.startScan(searchUUIDs, deviceInfo => {
await adapter.startScan(searchUUIDs, deviceInfo => {
let validServices = [];
function complete(bluetoothDevice) {
this.cancelRequest()
.then(() => {
resolve(bluetoothDevice);
});
}
const complete = async bluetoothDevice => {
await this.cancelRequest();
resolve(bluetoothDevice);
};

@@ -316,5 +312,5 @@ // filter devices if filters specified

function selectFn() {
const selectFn = () => {
complete.call(this, bluetoothDevice);
}
};

@@ -326,10 +322,10 @@ if (!this.deviceFound || this.deviceFound(bluetoothDevice, selectFn.bind(this)) === true) {

}
}, () => {
this.scanner = setTimeout(() => {
this.cancelRequest()
.then(() => {
if (!found) reject("requestDevice error: no devices found");
});
}, this.scanTime);
}, error => reject(`requestDevice error: ${error}`));
});
this.scanner = setTimeout(async () => {
await this.cancelRequest();
if (!found) {
reject('requestDevice error: no devices found');
}
}, this.scanTime);
});

@@ -339,14 +335,47 @@ }

/**
* Cancels the scan for devices
* Get all bluetooth devices
*/
public cancelRequest(): Promise<void> {
return new Promise((resolve, _reject) => {
if (this.scanner) {
clearTimeout(this.scanner);
this.scanner = null;
adapter.stopScan();
}
resolve();
public getDevices(): Promise<BluetoothDevice[]> {
if (this.scanner !== undefined) {
throw new Error('getDevices error: request in progress');
}
return new Promise(resolve => {
const devices: BluetoothDevice[] = [];
adapter.startScan([], deviceInfo => {
Object.assign(deviceInfo, {
_bluetooth: this,
_allowedServices: []
});
const bluetoothDevice = new BluetoothDevice(deviceInfo);
devices.push(bluetoothDevice);
});
this.scanner = setTimeout(async () => {
await this.cancelRequest();
resolve(devices);
}, this.scanTime);
});
}
/**
* Cancels the scan for devices
*/
public async cancelRequest(): Promise<void> {
if (this.scanner) {
clearTimeout(this.scanner);
this.scanner = undefined;
adapter.stopScan();
}
}
/**
* @hidden
* Request LE scan (not implemented)
*/
public requestLEScan(_options?: BluetoothLEScanOptions): Promise<BluetoothLEScan> {
throw new Error('requestLEScan error: method not implemented.');
}
}

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

import { EventDispatcher, TypedDispatcher } from "./dispatcher";
import { BluetoothRemoteGATTService } from "./service";
import { BluetoothRemoteGATTDescriptor } from "./descriptor";
import { getDescriptorUUID } from "./helpers";
import { adapter } from "./adapter";
import { W3CBluetoothRemoteGATTCharacteristic } from "./interfaces";
import { DOMEvent } from "./events";
import { EventDispatcher, TypedDispatcher } from './dispatcher';
import { BluetoothRemoteGATTService } from './service';
import { BluetoothRemoteGATTDescriptor } from './descriptor';
import { getDescriptorUUID } from './helpers';
import { adapter } from './adapter';
import { W3CBluetoothRemoteGATTCharacteristic } from './interfaces';
import { DOMEvent } from './events';
const isView = (source: ArrayBuffer | ArrayBufferView): source is ArrayBufferView => (source as ArrayBufferView).buffer !== undefined;
/**

@@ -53,3 +55,3 @@ * @hidden

*/
public readonly service: BluetoothRemoteGATTService = null;
public readonly service: BluetoothRemoteGATTService = undefined;

@@ -59,3 +61,3 @@ /**

*/
public readonly uuid = null;
public readonly uuid = undefined;

@@ -67,3 +69,3 @@ /**

private _value: DataView = null;
private _value: DataView = undefined;
/**

@@ -76,4 +78,4 @@ * The value of the characteristic

private handle: string = null;
private descriptors: Array<BluetoothRemoteGATTDescriptor> = null;
private handle: string = undefined;
private descriptors: Array<BluetoothRemoteGATTDescriptor> = undefined;

@@ -83,6 +85,6 @@ private _oncharacteristicvaluechanged: (ev: Event) => void;

if (this._oncharacteristicvaluechanged) {
this.removeEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.removeEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}
this._oncharacteristicvaluechanged = fn;
this.addEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.addEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}

@@ -108,6 +110,6 @@

if (emit) {
this.dispatchEvent(new DOMEvent(this, "characteristicvaluechanged"));
this.service.dispatchEvent(new DOMEvent(this, "characteristicvaluechanged"));
this.service.device.dispatchEvent(new DOMEvent(this, "characteristicvaluechanged"));
this.service.device._bluetooth.dispatchEvent(new DOMEvent(this, "characteristicvaluechanged"));
this.dispatchEvent(new DOMEvent(this, 'characteristicvaluechanged'));
this.service.dispatchEvent(new DOMEvent(this, 'characteristicvaluechanged'));
this.service.device.dispatchEvent(new DOMEvent(this, 'characteristicvaluechanged'));
this.service.device._bluetooth.dispatchEvent(new DOMEvent(this, 'characteristicvaluechanged'));
}

@@ -121,16 +123,17 @@ }

*/
public getDescriptor(descriptor: string | number): Promise<BluetoothRemoteGATTDescriptor> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("getDescriptor error: device not connected");
if (!descriptor) return reject("getDescriptor error: no descriptor specified");
public async getDescriptor(descriptor: string | number): Promise<BluetoothRemoteGATTDescriptor> {
if (!this.service.device.gatt.connected) {
throw new Error('getDescriptor error: device not connected');
}
this.getDescriptors(descriptor)
.then(descriptors => {
if (descriptors.length !== 1) return reject("getDescriptor error: descriptor not found");
resolve(descriptors[0]);
})
.catch(error => {
reject(`getDescriptor error: ${error}`);
});
});
if (!descriptor) {
throw new Error('getDescriptor error: no descriptor specified');
}
const descriptors = await this.getDescriptors(descriptor);
if (descriptors.length !== 1) {
throw new Error('getDescriptor error: descriptor not found');
}
return descriptors[0];
}

@@ -143,32 +146,27 @@

*/
public getDescriptors(descriptor?: string | number): Promise<Array<BluetoothRemoteGATTDescriptor>> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("getDescriptors error: device not connected");
public async getDescriptors(descriptor?: string | number): Promise<Array<BluetoothRemoteGATTDescriptor>> {
if (!this.service.device.gatt.connected) {
throw new Error('getDescriptors error: device not connected');
}
function complete() {
if (!descriptor) return resolve(this.descriptors);
const filtered = this.descriptors.filter(descriptorObject => {
return (descriptorObject.uuid === getDescriptorUUID(descriptor));
if (!this.descriptors) {
const descriptors = await adapter.discoverDescriptors(this.handle);
this.descriptors = descriptors.map(descriptorInfo => {
Object.assign(descriptorInfo, {
characteristic: this
});
return new BluetoothRemoteGATTDescriptor(descriptorInfo);
});
}
if (filtered.length !== 1) return reject("getDescriptors error: descriptor not found");
resolve(filtered);
}
if (!descriptor) {
return this.descriptors;
}
if (this.descriptors) return complete.call(this);
const filtered = this.descriptors.filter(descriptorObject => descriptorObject.uuid === getDescriptorUUID(descriptor));
adapter.discoverDescriptors(this.handle, [], descriptors => {
this.descriptors = descriptors.map(descriptorInfo => {
Object.assign(descriptorInfo, {
characteristic: this
});
return new BluetoothRemoteGATTDescriptor(descriptorInfo);
});
complete.call(this);
}, error => {
reject(`getDescriptors error: ${error}`);
});
});
if (filtered.length !== 1) {
throw new Error('getDescriptors error: descriptor not found');
}
return filtered;
}

@@ -180,13 +178,10 @@

*/
public readValue(): Promise<DataView> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("readValue error: device not connected");
public async readValue(): Promise<DataView> {
if (!this.service.device.gatt.connected) {
throw new Error('readValue error: device not connected');
}
adapter.readCharacteristic(this.handle, dataView => {
this.setValue(dataView, true);
resolve(dataView);
}, error => {
reject(`readValue error: ${error}`);
});
});
const dataView = await adapter.readCharacteristic(this.handle);
this.setValue(dataView, true);
return dataView;
}

@@ -198,20 +193,12 @@

*/
public writeValue(value: ArrayBuffer | ArrayBufferView): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("writeValue error: device not connected");
public async writeValue(value: ArrayBuffer | ArrayBufferView): Promise<void> {
if (!this.service.device.gatt.connected) {
throw new Error('writeValue error: device not connected');
}
function isView(source: ArrayBuffer | ArrayBufferView): source is ArrayBufferView {
return (source as ArrayBufferView).buffer !== undefined;
}
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
adapter.writeCharacteristic(this.handle, dataView, () => {
this.setValue (dataView);
resolve();
}, error => {
reject(`writeValue error: ${error}`);
});
});
await adapter.writeCharacteristic(this.handle, dataView);
this.setValue(dataView);
}

@@ -223,20 +210,12 @@

*/
public writeValueWithResponse(value: ArrayBuffer | ArrayBufferView): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("writeValue error: device not connected");
public async writeValueWithResponse(value: ArrayBuffer | ArrayBufferView): Promise<void> {
if (!this.service.device.gatt.connected) {
throw new Error('writeValue error: device not connected');
}
function isView(source: ArrayBuffer | ArrayBufferView): source is ArrayBufferView {
return (source as ArrayBufferView).buffer !== undefined;
}
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
adapter.writeCharacteristic(this.handle, dataView, () => {
this.setValue (dataView);
resolve();
}, error => {
reject(`writeValue error: ${error}`);
}, false);
});
await adapter.writeCharacteristic(this.handle, dataView, false);
this.setValue(dataView);
}

@@ -248,20 +227,12 @@

*/
public writeValueWithoutResponse(value: ArrayBuffer | ArrayBufferView): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("writeValue error: device not connected");
public async writeValueWithoutResponse(value: ArrayBuffer | ArrayBufferView): Promise<void> {
if (!this.service.device.gatt.connected) {
throw new Error('writeValue error: device not connected');
}
function isView(source: ArrayBuffer | ArrayBufferView): source is ArrayBufferView {
return (source as ArrayBufferView).buffer !== undefined;
}
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
adapter.writeCharacteristic(this.handle, dataView, () => {
this.setValue (dataView);
resolve();
}, error => {
reject(`writeValue error: ${error}`);
}, true);
});
await adapter.writeCharacteristic(this.handle, dataView, true);
this.setValue(dataView);
}

@@ -273,14 +244,12 @@

*/
public startNotifications(): Promise<W3CBluetoothRemoteGATTCharacteristic> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("startNotifications error: device not connected");
public async startNotifications(): Promise<W3CBluetoothRemoteGATTCharacteristic> {
if (!this.service.device.gatt.connected) {
throw new Error('startNotifications error: device not connected');
}
adapter.enableNotify(this.handle, dataView => {
this.setValue(dataView, true);
}, () => {
resolve(this);
}, error => {
reject(`startNotifications error: ${error}`);
});
await adapter.enableNotify(this.handle, dataView => {
this.setValue(dataView, true);
});
return this;
}

@@ -292,13 +261,10 @@

*/
public stopNotifications(): Promise<BluetoothRemoteGATTCharacteristic> {
return new Promise((resolve, reject) => {
if (!this.service.device.gatt.connected) return reject("stopNotifications error: device not connected");
public async stopNotifications(): Promise<BluetoothRemoteGATTCharacteristic> {
if (!this.service.device.gatt.connected) {
throw new Error('stopNotifications error: device not connected');
}
adapter.disableNotify(this.handle, () => {
resolve(this);
}, error => {
reject(`stopNotifications error: ${error}`);
});
});
await adapter.disableNotify(this.handle);
return this;
}
}

@@ -26,4 +26,4 @@ /*

import { adapter } from "./adapter";
import { W3CBluetoothRemoteGATTDescriptor } from "./interfaces";
import { adapter } from './adapter';
import { W3CBluetoothRemoteGATTDescriptor } from './interfaces';

@@ -38,3 +38,3 @@ /**

*/
public readonly characteristic: BluetoothRemoteGATTCharacteristic = null;
public readonly characteristic: BluetoothRemoteGATTCharacteristic = undefined;

@@ -44,5 +44,5 @@ /**

*/
public readonly uuid: string = null;
public readonly uuid: string = undefined;
private _value: DataView = null;
private _value: DataView = undefined;
/**

@@ -55,3 +55,3 @@ * The value of the descriptor

private handle: string = null;
private handle: string = undefined;

@@ -74,13 +74,10 @@ /**

*/
public readValue(): Promise<DataView> {
return new Promise((resolve, reject) => {
if (!this.characteristic.service.device.gatt.connected) return reject("readValue error: device not connected");
public async readValue(): Promise<DataView> {
if (!this.characteristic.service.device.gatt.connected) {
throw new Error('readValue error: device not connected');
}
adapter.readDescriptor(this.handle, dataView => {
this._value = dataView;
resolve(dataView);
}, error => {
reject(`readValue error: ${error}`);
});
});
const dataView = await adapter.readDescriptor(this.handle);
this._value = dataView;
return dataView;
}

@@ -92,21 +89,14 @@

*/
public writeValue(value: ArrayBuffer | ArrayBufferView): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.characteristic.service.device.gatt.connected) return reject("writeValue error: device not connected");
public async writeValue(value: ArrayBuffer | ArrayBufferView): Promise<void> {
if (!this.characteristic.service.device.gatt.connected) {
throw new Error('writeValue error: device not connected');
}
function isView(source: ArrayBuffer | ArrayBufferView): source is ArrayBufferView {
return (source as ArrayBufferView).buffer !== undefined;
}
const isView = (source: ArrayBuffer | ArrayBufferView): source is ArrayBufferView => (source as ArrayBufferView).buffer !== undefined;
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
const arrayBuffer = isView(value) ? value.buffer : value;
const dataView = new DataView(arrayBuffer);
adapter.writeDescriptor(this.handle, dataView, () => {
this._value = dataView;
resolve();
}, error => {
reject(`writeValue error: ${error}`);
});
});
await adapter.writeDescriptor(this.handle, dataView);
this._value = dataView;
}
}

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

import { EventDispatcher, TypedDispatcher } from "./dispatcher";
import { Bluetooth } from "./bluetooth";
import { BluetoothRemoteGATTServer } from "./server";
import { BluetoothRemoteGATTServiceEvents } from "./service";
import { W3CBluetoothDevice } from "./interfaces";
import { EventDispatcher, TypedDispatcher } from './dispatcher';
import { Bluetooth } from './bluetooth';
import { BluetoothRemoteGATTServer } from './server';
import { BluetoothRemoteGATTServiceEvents } from './service';
import { W3CBluetoothDevice } from './interfaces';

@@ -55,3 +55,3 @@ /**

*/
public readonly id: string = null;
public readonly id: string = undefined;

@@ -61,3 +61,3 @@ /**

*/
public readonly name: string = null;
public readonly name: string = undefined;

@@ -67,3 +67,3 @@ /**

*/
public readonly gatt: BluetoothRemoteGATTServer = null;
public readonly gatt: BluetoothRemoteGATTServer = undefined;

@@ -88,3 +88,3 @@ /**

*/
public readonly _bluetooth: Bluetooth = null;
public readonly _bluetooth: Bluetooth = undefined;

@@ -104,6 +104,6 @@ /**

if (this._oncharacteristicvaluechanged) {
this.removeEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.removeEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}
this._oncharacteristicvaluechanged = fn;
this.addEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.addEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}

@@ -114,6 +114,6 @@

if (this._onserviceadded) {
this.removeEventListener("serviceadded", this._onserviceadded);
this.removeEventListener('serviceadded', this._onserviceadded);
}
this._onserviceadded = fn;
this.addEventListener("serviceadded", this._onserviceadded);
this.addEventListener('serviceadded', this._onserviceadded);
}

@@ -124,6 +124,6 @@

if (this._onservicechanged) {
this.removeEventListener("servicechanged", this._onservicechanged);
this.removeEventListener('servicechanged', this._onservicechanged);
}
this._onservicechanged = fn;
this.addEventListener("servicechanged", this._onservicechanged);
this.addEventListener('servicechanged', this._onservicechanged);
}

@@ -134,6 +134,6 @@

if (this._onserviceremoved) {
this.removeEventListener("serviceremoved", this._onserviceremoved);
this.removeEventListener('serviceremoved', this._onserviceremoved);
}
this._onserviceremoved = fn;
this.addEventListener("serviceremoved", this._onserviceremoved);
this.addEventListener('serviceremoved', this._onserviceremoved);
}

@@ -144,6 +144,6 @@

if (this._ongattserverdisconnected) {
this.removeEventListener("gattserverdisconnected", this._ongattserverdisconnected);
this.removeEventListener('gattserverdisconnected', this._ongattserverdisconnected);
}
this._ongattserverdisconnected = fn;
this.addEventListener("gattserverdisconnected", this._ongattserverdisconnected);
this.addEventListener('gattserverdisconnected', this._ongattserverdisconnected);
}

@@ -154,6 +154,6 @@

if (this._onadvertisementreceived) {
this.removeEventListener("advertisementreceived", this._onadvertisementreceived);
this.removeEventListener('advertisementreceived', this._onadvertisementreceived);
}
this._onadvertisementreceived = fn;
this.addEventListener("advertisementreceived", this._onadvertisementreceived);
this.addEventListener('advertisementreceived', this._onadvertisementreceived);
}

@@ -186,5 +186,3 @@

public watchAdvertisements(): Promise<void> {
return new Promise((_resolve, reject) => {
reject("watchAdvertisements error: method not implemented");
});
throw new Error('watchAdvertisements error: method not implemented');
}

@@ -195,7 +193,12 @@

*/
public unwatchAdvertisements() {
return new Promise((_resolve, reject) => {
reject("unwatchAdvertisements error: method not implemented");
});
public unwatchAdvertisements(): Promise<void> {
throw new Error('unwatchAdvertisements error: method not implemented');
}
/**
* Forget this device (not implemented)
*/
public forget(): Promise<void> {
throw new Error('forget error: method not implemented');
}
}

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

import { EventEmitter } from "events";
import { EventEmitter } from 'events';
// tslint:disable:ban-types
/**

@@ -60,3 +58,5 @@ * @hidden

removeAllListeners<E extends keyof EventListeners<T>>(event?: E): this;
// eslint-disable-next-line @typescript-eslint/ban-types
listeners<K extends keyof T>(event: K): Array<Function>;
// eslint-disable-next-line @typescript-eslint/ban-types
listeners<E extends keyof EventListeners<T>>(event: EventListeners<T>[E]): Array<Function>;

@@ -78,3 +78,3 @@ emit<K extends keyof T>(event: K, data: T[K]): boolean;

public addEventListener(type: string, listener: EventListenerOrEventListenerObject | null) {
public addEventListener(type: string, listener: EventListenerOrEventListenerObject | undefined): void {
if (listener) {

@@ -86,3 +86,3 @@ const handler = this.isEventListenerObject(listener) ? listener.handleEvent : listener;

public removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null) {
public removeEventListener(type: string, callback: EventListenerOrEventListenerObject | undefined): void {
if (callback) {

@@ -89,0 +89,0 @@ const handler = this.isEventListenerObject(callback) ? callback.handleEvent : callback;

@@ -133,3 +133,3 @@ /*

*/
public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {
public initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void {
this.type = type;

@@ -143,3 +143,3 @@ this.bubbles = bubbles;

*/
public preventDefault() {
public preventDefault(): void {
this.defaultPrevented = true;

@@ -151,3 +151,3 @@ }

*/
public stopImmediatePropagation() {
public stopImmediatePropagation(): void {
return;

@@ -159,5 +159,5 @@ }

*/
public stopPropagation() {
public stopPropagation(): void {
return;
}
}

@@ -30,34 +30,34 @@ /*

export enum bluetoothServices {
"alert_notification" = 0x1811,
"automation_io" = 0x1815,
"battery_service" = 0x180F,
"blood_pressure" = 0x1810,
"body_composition" = 0x181B,
"bond_management" = 0x181E,
"continuous_glucose_monitoring" = 0x181F,
"current_time" = 0x1805,
"cycling_power" = 0x1818,
"cycling_speed_and_cadence" = 0x1816,
"device_information" = 0x180A,
"environmental_sensing" = 0x181A,
"generic_access" = 0x1800,
"generic_attribute" = 0x1801,
"glucose" = 0x1808,
"health_thermometer" = 0x1809,
"heart_rate" = 0x180D,
"human_interface_device" = 0x1812,
"immediate_alert" = 0x1802,
"indoor_positioning" = 0x1821,
"internet_protocol_support" = 0x1820,
"link_loss" = 0x1803,
"location_and_navigation" = 0x1819,
"next_dst_change" = 0x1807,
"phone_alert_status" = 0x180E,
"pulse_oximeter" = 0x1822,
"reference_time_update" = 0x1806,
"running_speed_and_cadence" = 0x1814,
"scan_parameters" = 0x1813,
"tx_power" = 0x1804,
"user_data" = 0x181C,
"weight_scale" = 0x181D
'alert_notification' = 0x1811,
'automation_io' = 0x1815,
'battery_service' = 0x180F,
'blood_pressure' = 0x1810,
'body_composition' = 0x181B,
'bond_management' = 0x181E,
'continuous_glucose_monitoring' = 0x181F,
'current_time' = 0x1805,
'cycling_power' = 0x1818,
'cycling_speed_and_cadence' = 0x1816,
'device_information' = 0x180A,
'environmental_sensing' = 0x181A,
'generic_access' = 0x1800,
'generic_attribute' = 0x1801,
'glucose' = 0x1808,
'health_thermometer' = 0x1809,
'heart_rate' = 0x180D,
'human_interface_device' = 0x1812,
'immediate_alert' = 0x1802,
'indoor_positioning' = 0x1821,
'internet_protocol_support' = 0x1820,
'link_loss' = 0x1803,
'location_and_navigation' = 0x1819,
'next_dst_change' = 0x1807,
'phone_alert_status' = 0x180E,
'pulse_oximeter' = 0x1822,
'reference_time_update' = 0x1806,
'running_speed_and_cadence' = 0x1814,
'scan_parameters' = 0x1813,
'tx_power' = 0x1804,
'user_data' = 0x181C,
'weight_scale' = 0x181D
}

@@ -69,163 +69,163 @@

export enum bluetoothCharacteristics {
"aerobic_heart_rate_lower_limit" = 0x2A7E,
"aerobic_heart_rate_upper_limit" = 0x2A84,
"aerobic_threshold" = 0x2A7F,
"age" = 0x2A80,
"aggregate" = 0x2A5A,
"alert_category_id" = 0x2A43,
"alert_category_id_bit_mask" = 0x2A42,
"alert_level" = 0x2A06,
"alert_notification_control_point" = 0x2A44,
"alert_status" = 0x2A3F,
"altitude" = 0x2AB3,
"anaerobic_heart_rate_lower_limit" = 0x2A81,
"anaerobic_heart_rate_upper_limit" = 0x2A82,
"anaerobic_threshold" = 0x2A83,
"analog" = 0x2A58,
"apparent_wind_direction" = 0x2A73,
"apparent_wind_speed" = 0x2A72,
"gap.appearance" = 0x2A01,
"barometric_pressure_trend" = 0x2AA3,
"battery_level" = 0x2A19,
"blood_pressure_feature" = 0x2A49,
"blood_pressure_measurement" = 0x2A35,
"body_composition_feature" = 0x2A9B,
"body_composition_measurement" = 0x2A9C,
"body_sensor_location" = 0x2A38,
"bond_management_control_point" = 0x2AA4,
"bond_management_feature" = 0x2AA5,
"boot_keyboard_input_report" = 0x2A22,
"boot_keyboard_output_report" = 0x2A32,
"boot_mouse_input_report" = 0x2A33,
"gap.central_address_resolution_support" = 0x2AA6,
"cgm_feature" = 0x2AA8,
"cgm_measurement" = 0x2AA7,
"cgm_session_run_time" = 0x2AAB,
"cgm_session_start_time" = 0x2AAA,
"cgm_specific_ops_control_point" = 0x2AAC,
"cgm_status" = 0x2AA9,
"csc_feature" = 0x2A5C,
"csc_measurement" = 0x2A5B,
"current_time" = 0x2A2B,
"cycling_power_control_point" = 0x2A66,
"cycling_power_feature" = 0x2A65,
"cycling_power_measurement" = 0x2A63,
"cycling_power_vector" = 0x2A64,
"database_change_increment" = 0x2A99,
"date_of_birth" = 0x2A85,
"date_of_threshold_assessment" = 0x2A86,
"date_time" = 0x2A08,
"day_date_time" = 0x2A0A,
"day_of_week" = 0x2A09,
"descriptor_value_changed" = 0x2A7D,
"gap.device_name" = 0x2A00,
"dew_point" = 0x2A7B,
"digital" = 0x2A56,
"dst_offset" = 0x2A0D,
"elevation" = 0x2A6C,
"email_address" = 0x2A87,
"exact_time_256" = 0x2A0C,
"fat_burn_heart_rate_lower_limit" = 0x2A88,
"fat_burn_heart_rate_upper_limit" = 0x2A89,
"firmware_revision_string" = 0x2A26,
"first_name" = 0x2A8A,
"five_zone_heart_rate_limits" = 0x2A8B,
"floor_number" = 0x2AB2,
"gender" = 0x2A8C,
"glucose_feature" = 0x2A51,
"glucose_measurement" = 0x2A18,
"glucose_measurement_context" = 0x2A34,
"gust_factor" = 0x2A74,
"hardware_revision_string" = 0x2A27,
"heart_rate_control_point" = 0x2A39,
"heart_rate_max" = 0x2A8D,
"heart_rate_measurement" = 0x2A37,
"heat_index" = 0x2A7A,
"height" = 0x2A8E,
"hid_control_point" = 0x2A4C,
"hid_information" = 0x2A4A,
"hip_circumference" = 0x2A8F,
"humidity" = 0x2A6F,
"ieee_11073-20601_regulatory_certification_data_list" = 0x2A2A,
"indoor_positioning_configuration" = 0x2AAD,
"intermediate_blood_pressure" = 0x2A36,
"intermediate_temperature" = 0x2A1E,
"irradiance" = 0x2A77,
"language" = 0x2AA2,
"last_name" = 0x2A90,
"latitude" = 0x2AAE,
"ln_control_point" = 0x2A6B,
"ln_feature" = 0x2A6A,
"local_east_coordinate.xml" = 0x2AB1,
"local_north_coordinate" = 0x2AB0,
"local_time_information" = 0x2A0F,
"location_and_speed" = 0x2A67,
"location_name" = 0x2AB5,
"longitude" = 0x2AAF,
"magnetic_declination" = 0x2A2C,
"magnetic_flux_density_2D" = 0x2AA0,
"magnetic_flux_density_3D" = 0x2AA1,
"manufacturer_name_string" = 0x2A29,
"maximum_recommended_heart_rate" = 0x2A91,
"measurement_interval" = 0x2A21,
"model_number_string" = 0x2A24,
"navigation" = 0x2A68,
"new_alert" = 0x2A46,
"gap.peripheral_preferred_connection_parameters" = 0x2A04,
"gap.peripheral_privacy_flag" = 0x2A02,
"plx_continuous_measurement" = 0x2A5F,
"plx_features" = 0x2A60,
"plx_spot_check_measurement" = 0x2A5E,
"pnp_id" = 0x2A50,
"pollen_concentration" = 0x2A75,
"position_quality" = 0x2A69,
"pressure" = 0x2A6D,
"protocol_mode" = 0x2A4E,
"rainfall" = 0x2A78,
"gap.reconnection_address" = 0x2A03,
"record_access_control_point" = 0x2A52,
"reference_time_information" = 0x2A14,
"report" = 0x2A4D,
"report_map" = 0x2A4B,
"resting_heart_rate" = 0x2A92,
"ringer_control_point" = 0x2A40,
"ringer_setting" = 0x2A41,
"rsc_feature" = 0x2A54,
"rsc_measurement" = 0x2A53,
"sc_control_point" = 0x2A55,
"scan_interval_window" = 0x2A4F,
"scan_refresh" = 0x2A31,
"sensor_location" = 0x2A5D,
"serial_number_string" = 0x2A25,
"gatt.service_changed" = 0x2A05,
"software_revision_string" = 0x2A28,
"sport_type_for_aerobic_and_anaerobic_thresholds" = 0x2A93,
"supported_new_alert_category" = 0x2A47,
"supported_unread_alert_category" = 0x2A48,
"system_id" = 0x2A23,
"temperature" = 0x2A6E,
"temperature_measurement" = 0x2A1C,
"temperature_type" = 0x2A1D,
"three_zone_heart_rate_limits" = 0x2A94,
"time_accuracy" = 0x2A12,
"time_source" = 0x2A13,
"time_update_control_point" = 0x2A16,
"time_update_state" = 0x2A17,
"time_with_dst" = 0x2A11,
"time_zone" = 0x2A0E,
"true_wind_direction" = 0x2A71,
"true_wind_speed" = 0x2A70,
"two_zone_heart_rate_limit" = 0x2A95,
"tx_power_level" = 0x2A07,
"uncertainty" = 0x2AB4,
"unread_alert_status" = 0x2A45,
"user_control_point" = 0x2A9F,
"user_index" = 0x2A9A,
"uv_index" = 0x2A76,
"vo2_max" = 0x2A96,
"waist_circumference" = 0x2A97,
"weight" = 0x2A98,
"weight_measurement" = 0x2A9D,
"weight_scale_feature" = 0x2A9E,
"wind_chill" = 0x2A79
'aerobic_heart_rate_lower_limit' = 0x2A7E,
'aerobic_heart_rate_upper_limit' = 0x2A84,
'aerobic_threshold' = 0x2A7F,
'age' = 0x2A80,
'aggregate' = 0x2A5A,
'alert_category_id' = 0x2A43,
'alert_category_id_bit_mask' = 0x2A42,
'alert_level' = 0x2A06,
'alert_notification_control_point' = 0x2A44,
'alert_status' = 0x2A3F,
'altitude' = 0x2AB3,
'anaerobic_heart_rate_lower_limit' = 0x2A81,
'anaerobic_heart_rate_upper_limit' = 0x2A82,
'anaerobic_threshold' = 0x2A83,
'analog' = 0x2A58,
'apparent_wind_direction' = 0x2A73,
'apparent_wind_speed' = 0x2A72,
'gap.appearance' = 0x2A01,
'barometric_pressure_trend' = 0x2AA3,
'battery_level' = 0x2A19,
'blood_pressure_feature' = 0x2A49,
'blood_pressure_measurement' = 0x2A35,
'body_composition_feature' = 0x2A9B,
'body_composition_measurement' = 0x2A9C,
'body_sensor_location' = 0x2A38,
'bond_management_control_point' = 0x2AA4,
'bond_management_feature' = 0x2AA5,
'boot_keyboard_input_report' = 0x2A22,
'boot_keyboard_output_report' = 0x2A32,
'boot_mouse_input_report' = 0x2A33,
'gap.central_address_resolution_support' = 0x2AA6,
'cgm_feature' = 0x2AA8,
'cgm_measurement' = 0x2AA7,
'cgm_session_run_time' = 0x2AAB,
'cgm_session_start_time' = 0x2AAA,
'cgm_specific_ops_control_point' = 0x2AAC,
'cgm_status' = 0x2AA9,
'csc_feature' = 0x2A5C,
'csc_measurement' = 0x2A5B,
'current_time' = 0x2A2B,
'cycling_power_control_point' = 0x2A66,
'cycling_power_feature' = 0x2A65,
'cycling_power_measurement' = 0x2A63,
'cycling_power_vector' = 0x2A64,
'database_change_increment' = 0x2A99,
'date_of_birth' = 0x2A85,
'date_of_threshold_assessment' = 0x2A86,
'date_time' = 0x2A08,
'day_date_time' = 0x2A0A,
'day_of_week' = 0x2A09,
'descriptor_value_changed' = 0x2A7D,
'gap.device_name' = 0x2A00,
'dew_point' = 0x2A7B,
'digital' = 0x2A56,
'dst_offset' = 0x2A0D,
'elevation' = 0x2A6C,
'email_address' = 0x2A87,
'exact_time_256' = 0x2A0C,
'fat_burn_heart_rate_lower_limit' = 0x2A88,
'fat_burn_heart_rate_upper_limit' = 0x2A89,
'firmware_revision_string' = 0x2A26,
'first_name' = 0x2A8A,
'five_zone_heart_rate_limits' = 0x2A8B,
'floor_number' = 0x2AB2,
'gender' = 0x2A8C,
'glucose_feature' = 0x2A51,
'glucose_measurement' = 0x2A18,
'glucose_measurement_context' = 0x2A34,
'gust_factor' = 0x2A74,
'hardware_revision_string' = 0x2A27,
'heart_rate_control_point' = 0x2A39,
'heart_rate_max' = 0x2A8D,
'heart_rate_measurement' = 0x2A37,
'heat_index' = 0x2A7A,
'height' = 0x2A8E,
'hid_control_point' = 0x2A4C,
'hid_information' = 0x2A4A,
'hip_circumference' = 0x2A8F,
'humidity' = 0x2A6F,
'ieee_11073-20601_regulatory_certification_data_list' = 0x2A2A,
'indoor_positioning_configuration' = 0x2AAD,
'intermediate_blood_pressure' = 0x2A36,
'intermediate_temperature' = 0x2A1E,
'irradiance' = 0x2A77,
'language' = 0x2AA2,
'last_name' = 0x2A90,
'latitude' = 0x2AAE,
'ln_control_point' = 0x2A6B,
'ln_feature' = 0x2A6A,
'local_east_coordinate.xml' = 0x2AB1,
'local_north_coordinate' = 0x2AB0,
'local_time_information' = 0x2A0F,
'location_and_speed' = 0x2A67,
'location_name' = 0x2AB5,
'longitude' = 0x2AAF,
'magnetic_declination' = 0x2A2C,
'magnetic_flux_density_2D' = 0x2AA0,
'magnetic_flux_density_3D' = 0x2AA1,
'manufacturer_name_string' = 0x2A29,
'maximum_recommended_heart_rate' = 0x2A91,
'measurement_interval' = 0x2A21,
'model_number_string' = 0x2A24,
'navigation' = 0x2A68,
'new_alert' = 0x2A46,
'gap.peripheral_preferred_connection_parameters' = 0x2A04,
'gap.peripheral_privacy_flag' = 0x2A02,
'plx_continuous_measurement' = 0x2A5F,
'plx_features' = 0x2A60,
'plx_spot_check_measurement' = 0x2A5E,
'pnp_id' = 0x2A50,
'pollen_concentration' = 0x2A75,
'position_quality' = 0x2A69,
'pressure' = 0x2A6D,
'protocol_mode' = 0x2A4E,
'rainfall' = 0x2A78,
'gap.reconnection_address' = 0x2A03,
'record_access_control_point' = 0x2A52,
'reference_time_information' = 0x2A14,
'report' = 0x2A4D,
'report_map' = 0x2A4B,
'resting_heart_rate' = 0x2A92,
'ringer_control_point' = 0x2A40,
'ringer_setting' = 0x2A41,
'rsc_feature' = 0x2A54,
'rsc_measurement' = 0x2A53,
'sc_control_point' = 0x2A55,
'scan_interval_window' = 0x2A4F,
'scan_refresh' = 0x2A31,
'sensor_location' = 0x2A5D,
'serial_number_string' = 0x2A25,
'gatt.service_changed' = 0x2A05,
'software_revision_string' = 0x2A28,
'sport_type_for_aerobic_and_anaerobic_thresholds' = 0x2A93,
'supported_new_alert_category' = 0x2A47,
'supported_unread_alert_category' = 0x2A48,
'system_id' = 0x2A23,
'temperature' = 0x2A6E,
'temperature_measurement' = 0x2A1C,
'temperature_type' = 0x2A1D,
'three_zone_heart_rate_limits' = 0x2A94,
'time_accuracy' = 0x2A12,
'time_source' = 0x2A13,
'time_update_control_point' = 0x2A16,
'time_update_state' = 0x2A17,
'time_with_dst' = 0x2A11,
'time_zone' = 0x2A0E,
'true_wind_direction' = 0x2A71,
'true_wind_speed' = 0x2A70,
'two_zone_heart_rate_limit' = 0x2A95,
'tx_power_level' = 0x2A07,
'uncertainty' = 0x2AB4,
'unread_alert_status' = 0x2A45,
'user_control_point' = 0x2A9F,
'user_index' = 0x2A9A,
'uv_index' = 0x2A76,
'vo2_max' = 0x2A96,
'waist_circumference' = 0x2A97,
'weight' = 0x2A98,
'weight_measurement' = 0x2A9D,
'weight_scale_feature' = 0x2A9E,
'wind_chill' = 0x2A79
}

@@ -237,17 +237,17 @@

export enum bluetoothDescriptors {
"gatt.characteristic_extended_properties" = 0x2900,
"gatt.characteristic_user_description" = 0x2901,
"gatt.client_characteristic_configuration" = 0x2902,
"gatt.server_characteristic_configuration" = 0x2903,
"gatt.characteristic_presentation_format" = 0x2904,
"gatt.characteristic_aggregate_format" = 0x2905,
"valid_range" = 0x2906,
"external_report_reference" = 0x2907,
"report_reference" = 0x2908,
"number_of_digitals" = 0x2909,
"value_trigger_setting" = 0x290A,
"es_configuration" = 0x290B,
"es_measurement" = 0x290C,
"es_trigger_setting" = 0x290D,
"time_trigger_setting" = 0x290E
'gatt.characteristic_extended_properties' = 0x2900,
'gatt.characteristic_user_description' = 0x2901,
'gatt.client_characteristic_configuration' = 0x2902,
'gatt.server_characteristic_configuration' = 0x2903,
'gatt.characteristic_presentation_format' = 0x2904,
'gatt.characteristic_aggregate_format' = 0x2905,
'valid_range' = 0x2906,
'external_report_reference' = 0x2907,
'report_reference' = 0x2908,
'number_of_digitals' = 0x2909,
'value_trigger_setting' = 0x290A,
'es_configuration' = 0x290B,
'es_measurement' = 0x290C,
'es_trigger_setting' = 0x290D,
'time_trigger_setting' = 0x290E
}

@@ -260,9 +260,9 @@

*/
export function getCanonicalUUID(uuid: string | number): string {
if (typeof uuid === "number") uuid = uuid.toString(16);
export const getCanonicalUUID = (uuid: string | number): string => {
if (typeof uuid === 'number') uuid = uuid.toString(16);
uuid = uuid.toLowerCase();
if (uuid.length <= 8) uuid = ("00000000" + uuid).slice(-8) + "-0000-1000-8000-00805f9b34fb";
if (uuid.length === 32) uuid = uuid.match(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/).splice(1).join("-");
if (uuid.length <= 8) uuid = ('00000000' + uuid).slice(-8) + '-0000-1000-8000-00805f9b34fb';
if (uuid.length === 32) uuid = uuid.match(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/).splice(1).join('-');
return uuid;
}
};

@@ -274,5 +274,5 @@ /**

*/
export function getServiceUUID(service: string | number): string {
export const getServiceUUID = (service: string | number): string => {
// Check for string as enums also allow a reverse lookup which will match any numbers passed in
if (typeof service === "string" && bluetoothServices[service]) {
if (typeof service === 'string' && bluetoothServices[service]) {
service = bluetoothServices[service];

@@ -282,3 +282,3 @@ }

return getCanonicalUUID(service);
}
};

@@ -290,5 +290,5 @@ /**

*/
export function getCharacteristicUUID(characteristic: string | number): string {
export const getCharacteristicUUID = (characteristic: string | number): string => {
// Check for string as enums also allow a reverse lookup which will match any numbers passed in
if (typeof characteristic === "string" && bluetoothCharacteristics[characteristic]) {
if (typeof characteristic === 'string' && bluetoothCharacteristics[characteristic]) {
characteristic = bluetoothCharacteristics[characteristic];

@@ -298,3 +298,3 @@ }

return getCanonicalUUID(characteristic);
}
};

@@ -306,5 +306,5 @@ /**

*/
export function getDescriptorUUID(descriptor: string | number): string {
export const getDescriptorUUID = (descriptor: string | number): string => {
// Check for string as enums also allow a reverse lookup which will match any numbers passed in
if (typeof descriptor === "string" && bluetoothDescriptors[descriptor]) {
if (typeof descriptor === 'string' && bluetoothDescriptors[descriptor]) {
descriptor = bluetoothDescriptors[descriptor];

@@ -314,2 +314,2 @@ }

return getCanonicalUUID(descriptor);
}
};

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

import { Bluetooth, BluetoothOptions } from "./bluetooth";
import { Bluetooth, BluetoothOptions } from './bluetooth';

@@ -42,2 +42,2 @@ /**

*/
export * from "./helpers";
export * from './helpers';

@@ -29,3 +29,3 @@ /*

*/
export interface W3CBluetooth extends Bluetooth {}
export type W3CBluetooth = Bluetooth

@@ -35,3 +35,3 @@ /**

*/
export interface W3CBluetoothDevice extends BluetoothDevice {}
export type W3CBluetoothDevice = BluetoothDevice

@@ -41,3 +41,3 @@ /**

*/
export interface W3CBluetoothRemoteGATTServer extends BluetoothRemoteGATTServer {}
export type W3CBluetoothRemoteGATTServer = BluetoothRemoteGATTServer

@@ -47,3 +47,3 @@ /**

*/
export interface W3CBluetoothRemoteGATTService extends BluetoothRemoteGATTService {}
export type W3CBluetoothRemoteGATTService = BluetoothRemoteGATTService

@@ -53,3 +53,3 @@ /**

*/
export interface W3CBluetoothRemoteGATTCharacteristic extends BluetoothRemoteGATTCharacteristic {}
export type W3CBluetoothRemoteGATTCharacteristic = BluetoothRemoteGATTCharacteristic

@@ -59,2 +59,2 @@ /**

*/
export interface W3CBluetoothRemoteGATTDescriptor extends BluetoothRemoteGATTDescriptor {}
export type W3CBluetoothRemoteGATTDescriptor = BluetoothRemoteGATTDescriptor

@@ -26,8 +26,8 @@ /*

import { BluetoothDevice } from "./device";
import { getServiceUUID } from "./helpers";
import { adapter } from "./adapter";
import { W3CBluetoothRemoteGATTServer } from "./interfaces";
import { BluetoothRemoteGATTService } from "./service";
import { DOMEvent } from "./events";
import { BluetoothDevice } from './device';
import { getServiceUUID } from './helpers';
import { adapter } from './adapter';
import { W3CBluetoothRemoteGATTServer } from './interfaces';
import { BluetoothRemoteGATTService } from './service';
import { DOMEvent } from './events';

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

*/
public readonly device: BluetoothDevice = null;
public readonly device: BluetoothDevice = undefined;
private _connected: boolean = false;
private _connected = false;
/**

@@ -53,4 +53,4 @@ * Whether the gatt server is connected

private handle: string = null;
private services: Array<BluetoothRemoteGATTService> = null;
private handle: string = undefined;
private services: Array<BluetoothRemoteGATTService> = undefined;

@@ -70,18 +70,16 @@ /**

*/
public connect(): Promise<BluetoothRemoteGATTServer> {
return new Promise((resolve, reject) => {
if (this.connected) return reject("connect error: device already connected");
public async connect(): Promise<BluetoothRemoteGATTServer> {
if (this.connected) {
throw new Error('connect error: device already connected');
}
adapter.connect(this.handle, () => {
this._connected = true;
resolve(this);
}, () => {
this.services = null;
this._connected = false;
this.device.dispatchEvent(new DOMEvent(this.device, "gattserverdisconnected"));
this.device._bluetooth.dispatchEvent(new DOMEvent(this.device, "gattserverdisconnected"));
}, error => {
reject(`connect Error: ${error}`);
});
await adapter.connect(this.handle, () => {
this.services = undefined;
this._connected = false;
this.device.dispatchEvent(new DOMEvent(this.device, 'gattserverdisconnected'));
this.device._bluetooth.dispatchEvent(new DOMEvent(this.device, 'gattserverdisconnected'));
});
this._connected = true;
return this;
}

@@ -92,3 +90,3 @@

*/
public disconnect() {
public disconnect(): void {
adapter.disconnect(this.handle);

@@ -103,16 +101,17 @@ this._connected = false;

*/
public getPrimaryService(service: string | number): Promise<BluetoothRemoteGATTService> {
return new Promise((resolve, reject) => {
if (!this.connected) return reject("getPrimaryService error: device not connected");
if (!service) return reject("getPrimaryService error: no service specified");
public async getPrimaryService(service: BluetoothServiceUUID): Promise<BluetoothRemoteGATTService> {
if (!this.connected) {
throw new Error('getPrimaryService error: device not connected');
}
this.getPrimaryServices(service)
.then(services => {
if (services.length !== 1) return reject("getPrimaryService error: service not found");
resolve(services[0]);
})
.catch(error => {
reject(`getPrimaryService error: ${error}`);
});
});
if (!service) {
throw new Error('getPrimaryService error: no service specified');
}
const services = await this.getPrimaryServices(service);
if (services.length !== 1) {
throw new Error('getPrimaryService error: service not found');
}
return services[0];
}

@@ -125,33 +124,29 @@

*/
public getPrimaryServices(service?: string | number): Promise<Array<BluetoothRemoteGATTService>> {
return new Promise((resolve, reject) => {
if (!this.connected) return reject("getPrimaryServices error: device not connected");
public async getPrimaryServices(service?: BluetoothServiceUUID): Promise<Array<BluetoothRemoteGATTService>> {
if (!this.connected) {
throw new Error('getPrimaryServices error: device not connected');
}
function complete() {
if (!service) return resolve(this.services);
const filtered = this.services.filter(serviceObject => {
return (serviceObject.uuid === getServiceUUID(service));
if (!this.services) {
const services = await adapter.discoverServices(this.handle, this.device._allowedServices);
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new BluetoothRemoteGATTService(serviceInfo);
});
}
if (filtered.length !== 1) return reject("getPrimaryServices error: service not found");
resolve(filtered);
}
if (!service) {
return this.services;
}
if (this.services) return complete.call(this);
const filtered = this.services.filter(serviceObject => serviceObject.uuid === getServiceUUID(service));
adapter.discoverServices(this.handle, this.device._allowedServices, services => {
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new BluetoothRemoteGATTService(serviceInfo);
});
if (filtered.length !== 1) {
throw new Error('getPrimaryServices error: service not found');
}
complete.call(this);
}, error => {
reject(`getPrimaryServices error: ${error}`);
});
});
return filtered;
}
}

@@ -26,9 +26,9 @@ /*

import { EventDispatcher, TypedDispatcher } from "./dispatcher";
import { BluetoothDevice } from "./device";
import { BluetoothRemoteGATTCharacteristic, BluetoothRemoteGATTCharacteristicEvents } from "./characteristic";
import { getCharacteristicUUID, getServiceUUID } from "./helpers";
import { adapter } from "./adapter";
import { W3CBluetoothRemoteGATTService } from "./interfaces";
import { DOMEvent } from "./events";
import { EventDispatcher, TypedDispatcher } from './dispatcher';
import { BluetoothDevice } from './device';
import { BluetoothRemoteGATTCharacteristic, BluetoothRemoteGATTCharacteristicEvents } from './characteristic';
import { getCharacteristicUUID, getServiceUUID } from './helpers';
import { adapter } from './adapter';
import { W3CBluetoothRemoteGATTService } from './interfaces';
import { DOMEvent } from './events';

@@ -61,3 +61,3 @@ /**

*/
public readonly device: BluetoothDevice = null;
public readonly device: BluetoothDevice = undefined;

@@ -67,3 +67,3 @@ /**

*/
public readonly uuid: string = null;
public readonly uuid: string = undefined;

@@ -75,5 +75,5 @@ /**

private handle: string = null;
private services: Array<BluetoothRemoteGATTService> = null;
private characteristics: Array<BluetoothRemoteGATTCharacteristic> = null;
private handle: string = undefined;
private services: Array<BluetoothRemoteGATTService> = undefined;
private characteristics: Array<BluetoothRemoteGATTCharacteristic> = undefined;

@@ -83,6 +83,6 @@ private _oncharacteristicvaluechanged: (ev: Event) => void;

if (this._oncharacteristicvaluechanged) {
this.removeEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.removeEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}
this._oncharacteristicvaluechanged = fn;
this.addEventListener("characteristicvaluechanged", this._oncharacteristicvaluechanged);
this.addEventListener('characteristicvaluechanged', this._oncharacteristicvaluechanged);
}

@@ -93,6 +93,6 @@

if (this._onserviceadded) {
this.removeEventListener("serviceadded", this._onserviceadded);
this.removeEventListener('serviceadded', this._onserviceadded);
}
this._onserviceadded = fn;
this.addEventListener("serviceadded", this._onserviceadded);
this.addEventListener('serviceadded', this._onserviceadded);
}

@@ -103,6 +103,6 @@

if (this._onservicechanged) {
this.removeEventListener("servicechanged", this._onservicechanged);
this.removeEventListener('servicechanged', this._onservicechanged);
}
this._onservicechanged = fn;
this.addEventListener("servicechanged", this._onservicechanged);
this.addEventListener('servicechanged', this._onservicechanged);
}

@@ -113,6 +113,6 @@

if (this._onserviceremoved) {
this.removeEventListener("serviceremoved", this._onserviceremoved);
this.removeEventListener('serviceremoved', this._onserviceremoved);
}
this._onserviceremoved = fn;
this.addEventListener("serviceremoved", this._onserviceremoved);
this.addEventListener('serviceremoved', this._onserviceremoved);
}

@@ -133,5 +133,5 @@

this.dispatchEvent(new DOMEvent(this, "serviceadded"));
this.device.dispatchEvent(new DOMEvent(this, "serviceadded"));
this.device._bluetooth.dispatchEvent(new DOMEvent(this, "serviceadded"));
this.dispatchEvent(new DOMEvent(this, 'serviceadded'));
this.device.dispatchEvent(new DOMEvent(this, 'serviceadded'));
this.device._bluetooth.dispatchEvent(new DOMEvent(this, 'serviceadded'));
}

@@ -144,16 +144,17 @@

*/
public getCharacteristic(characteristic: string | number): Promise<BluetoothRemoteGATTCharacteristic> {
return new Promise((resolve, reject) => {
if (!this.device.gatt.connected) return reject("getCharacteristic error: device not connected");
if (!characteristic) return reject("getCharacteristic error: no characteristic specified");
public async getCharacteristic(characteristic: BluetoothCharacteristicUUID): Promise<BluetoothRemoteGATTCharacteristic> {
if (!this.device.gatt.connected) {
throw new Error('getCharacteristic error: device not connected');
}
this.getCharacteristics(characteristic)
.then(characteristics => {
if (characteristics.length !== 1) return reject("getCharacteristic error: characteristic not found");
resolve(characteristics[0]);
})
.catch(error => {
reject(`getCharacteristic error: ${error}`);
});
});
if (!characteristic) {
throw new Error('getCharacteristic error: no characteristic specified');
}
const characteristics = await this.getCharacteristics(characteristic);
if (characteristics.length !== 1) {
throw new Error('getCharacteristic error: characteristic not found');
}
return characteristics[0];
}

@@ -166,35 +167,31 @@

*/
public getCharacteristics(characteristic?: string | number): Promise<Array<BluetoothRemoteGATTCharacteristic>> {
return new Promise((resolve, reject) => {
if (!this.device.gatt.connected) return reject("getCharacteristics error: device not connected");
public async getCharacteristics(characteristic?: BluetoothCharacteristicUUID): Promise<Array<BluetoothRemoteGATTCharacteristic>> {
if (!this.device.gatt.connected) {
throw new Error('getCharacteristics error: device not connected');
}
function complete() {
if (!characteristic) return resolve(this.characteristics);
if (!this.characteristics) {
const characteristics = await adapter.discoverCharacteristics(this.handle);
this.characteristics = characteristics.map(characteristicInfo => {
Object.assign(characteristicInfo, {
service: this
});
return new BluetoothRemoteGATTCharacteristic(characteristicInfo);
});
}
// Canonical-ize characteristic
characteristic = getCharacteristicUUID(characteristic);
if (!characteristic) {
return this.characteristics;
}
const filtered = this.characteristics.filter(characteristicObject => {
return (characteristicObject.uuid === characteristic);
});
// Canonical-ize characteristic
characteristic = getCharacteristicUUID(characteristic);
if (filtered.length !== 1) return reject("getCharacteristics error: characteristic not found");
resolve(filtered);
}
const filtered = this.characteristics.filter(characteristicObject => characteristicObject.uuid === characteristic);
if (this.characteristics) return complete.call(this);
if (filtered.length !== 1) {
throw new Error('getCharacteristics error: characteristic not found');
}
adapter.discoverCharacteristics(this.handle, [], characteristics => {
this.characteristics = characteristics.map(characteristicInfo => {
Object.assign(characteristicInfo, {
service: this
});
return new BluetoothRemoteGATTCharacteristic(characteristicInfo);
});
complete.call(this);
}, error => {
reject(`getCharacteristics error: ${error}`);
});
});
return filtered;
}

@@ -207,16 +204,17 @@

*/
public getIncludedService(service: string | number): Promise<BluetoothRemoteGATTService> {
return new Promise((resolve, reject) => {
if (!this.device.gatt.connected) return reject("getIncludedService error: device not connected");
if (!service) return reject("getIncludedService error: no service specified");
public async getIncludedService(service: BluetoothServiceUUID): Promise<BluetoothRemoteGATTService> {
if (!this.device.gatt.connected) {
throw new Error('getIncludedService error: device not connected');
}
this.getIncludedServices(service)
.then(services => {
if (services.length !== 1) return reject("getIncludedService error: service not found");
resolve(services[0]);
})
.catch(error => {
reject(`getIncludedService error: ${error}`);
});
});
if (!service) {
throw new Error('getIncludedService error: no service specified');
}
const services = await this.getIncludedServices(service);
if (services.length !== 1) {
throw new Error('getIncludedService error: service not found');
}
return services[0];
}

@@ -229,33 +227,29 @@

*/
public getIncludedServices(service?: string | number): Promise<Array<BluetoothRemoteGATTService>> {
return new Promise((resolve, reject) => {
if (!this.device.gatt.connected) return reject("getIncludedServices error: device not connected");
public async getIncludedServices(service?: BluetoothServiceUUID): Promise<Array<BluetoothRemoteGATTService>> {
if (!this.device.gatt.connected) {
throw new Error('getIncludedServices error: device not connected');
}
function complete() {
if (!service) return resolve(this.services);
const filtered = this.services.filter(serviceObject => {
return (serviceObject.uuid === getServiceUUID(service));
if (!this.services) {
const services = await adapter.discoverIncludedServices(this.handle, this.device._allowedServices);
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new BluetoothRemoteGATTService(serviceInfo);
});
}
if (filtered.length !== 1) return reject("getIncludedServices error: service not found");
resolve(filtered);
}
if (!service) {
return this.services;
}
if (this.services) return complete.call(this);
const filtered = this.services.filter(serviceObject => serviceObject.uuid === getServiceUUID(service));
adapter.discoverIncludedServices(this.handle, this.device._allowedServices, services => {
this.services = services.map(serviceInfo => {
Object.assign(serviceInfo, {
device: this.device
});
return new BluetoothRemoteGATTService(serviceInfo);
});
if (filtered.length !== 1) {
throw new Error('getIncludedServices error: service not found');
}
complete.call(this);
}, error => {
reject(`getIncludedServices error: ${error}`);
});
});
return filtered;
}
}
{
"compilerOptions": {
"target": "es6",
"target": "es5",
"alwaysStrict": true,
"noImplicitReturns": true,
"noEmitOnError": true,

@@ -10,4 +11,7 @@ "noUnusedLocals": true,

"declaration": true,
"declarationDir": "types"
}
"outDir": "dist"
},
"include": [
"src"
]
}
{
"name": "Node Web Bluetooth",
"readme": "./documentation.md",
"theme": "./docs-theme",
"module": "commonjs",
"target": "es6",
"mode": "file",
"out": "docs",
"entryPoints": [
"src"
],
"excludeExternals": true,
"excludePrivate": true,
"hideGenerator": true
}
}

Sorry, the diff of this file is not supported yet

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

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc