webbluetooth
Advanced tools
Comparing version 2.1.1-next.f9fb064.0 to 2.2.0
{ | ||
"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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1552391
5
89
9238
0
178
0
0
+ Added@abandonware/noble@1.9.2-15(transitive)
+ Added@types/node@10.17.60(transitive)
+ Added@types/web-bluetooth@0.0.14(transitive)
+ Addednode-addon-api@3.2.1(transitive)
- Removed@abandonware/noble@1.9.2-7(transitive)
- Removed@types/node@8.10.66(transitive)
- Removed@types/web-bluetooth@0.0.5(transitive)
- Removednapi-thread-safe-callback@0.0.6(transitive)
- Removednode-addon-api@2.0.2(transitive)
Updated@abandonware/noble@1.9.2-15
Updated@types/node@^10.16.0
Updated@types/web-bluetooth@^0.0.14