@capacitor-community/bluetooth-le 8.0.0-0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapacitorCommunityBluetoothLe.podspec +17 -17
- package/LICENSE +21 -21
- package/Package.swift +27 -27
- package/README.md +5 -3
- package/android/build.gradle +73 -71
- package/android/src/main/AndroidManifest.xml +22 -22
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt +1094 -1094
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Conversion.kt +51 -51
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt +771 -771
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/DeviceList.kt +28 -28
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/DeviceScanner.kt +189 -189
- package/dist/docs.json +43 -43
- package/dist/esm/bleClient.d.ts +278 -278
- package/dist/esm/bleClient.js +361 -361
- package/dist/esm/bleClient.js.map +1 -1
- package/dist/esm/config.d.ts +53 -53
- package/dist/esm/config.js +2 -2
- package/dist/esm/conversion.d.ts +56 -56
- package/dist/esm/conversion.js +134 -134
- package/dist/esm/conversion.js.map +1 -1
- package/dist/esm/definitions.d.ts +352 -352
- package/dist/esm/definitions.js +42 -42
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +5 -5
- package/dist/esm/index.js +5 -5
- package/dist/esm/plugin.d.ts +2 -2
- package/dist/esm/plugin.js +4 -4
- package/dist/esm/queue.d.ts +3 -3
- package/dist/esm/queue.js +17 -17
- package/dist/esm/queue.js.map +1 -1
- package/dist/esm/timeout.d.ts +1 -1
- package/dist/esm/timeout.js +9 -9
- package/dist/esm/validators.d.ts +1 -1
- package/dist/esm/validators.js +11 -11
- package/dist/esm/validators.js.map +1 -1
- package/dist/esm/web.d.ts +57 -57
- package/dist/esm/web.js +403 -403
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +964 -964
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +964 -964
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/BluetoothLe/Conversion.swift +83 -83
- package/ios/Sources/BluetoothLe/Device.swift +423 -423
- package/ios/Sources/BluetoothLe/DeviceListView.swift +121 -121
- package/ios/Sources/BluetoothLe/DeviceManager.swift +415 -503
- package/ios/Sources/BluetoothLe/Logging.swift +8 -8
- package/ios/Sources/BluetoothLe/Plugin.swift +763 -775
- package/ios/Sources/BluetoothLe/ScanFilters.swift +114 -0
- package/ios/Sources/BluetoothLe/ThreadSafeDictionary.swift +15 -15
- package/ios/Tests/BluetoothLeTests/ConversionTests.swift +55 -55
- package/ios/Tests/BluetoothLeTests/PluginTests.swift +27 -27
- package/ios/Tests/BluetoothLeTests/ScanFiltersTests.swift +153 -0
- package/package.json +114 -115
package/dist/esm/web.js
CHANGED
|
@@ -1,404 +1,404 @@
|
|
|
1
|
-
import { WebPlugin } from '@capacitor/core';
|
|
2
|
-
import { hexStringToDataView, mapToObject, toArrayBufferDataView, webUUIDToString } from './conversion';
|
|
3
|
-
import { runWithTimeout } from './timeout';
|
|
4
|
-
export class BluetoothLeWeb extends WebPlugin {
|
|
5
|
-
constructor() {
|
|
6
|
-
super(...arguments);
|
|
7
|
-
this.deviceMap = new Map();
|
|
8
|
-
this.discoveredDevices = new Map();
|
|
9
|
-
this.scan = null;
|
|
10
|
-
this.DEFAULT_CONNECTION_TIMEOUT = 10000;
|
|
11
|
-
this.onAdvertisementReceivedCallback = this.onAdvertisementReceived.bind(this);
|
|
12
|
-
this.onDisconnectedCallback = this.onDisconnected.bind(this);
|
|
13
|
-
this.onCharacteristicValueChangedCallback = this.onCharacteristicValueChanged.bind(this);
|
|
14
|
-
}
|
|
15
|
-
async initialize() {
|
|
16
|
-
if (typeof navigator === 'undefined' || !navigator.bluetooth) {
|
|
17
|
-
throw this.unavailable('Web Bluetooth API not available in this browser.');
|
|
18
|
-
}
|
|
19
|
-
const isAvailable = await navigator.bluetooth.getAvailability();
|
|
20
|
-
if (!isAvailable) {
|
|
21
|
-
throw this.unavailable('No Bluetooth radio available.');
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
async isEnabled() {
|
|
25
|
-
// not available on web
|
|
26
|
-
return { value: true };
|
|
27
|
-
}
|
|
28
|
-
async requestEnable() {
|
|
29
|
-
throw this.unavailable('requestEnable is not available on web.');
|
|
30
|
-
}
|
|
31
|
-
async enable() {
|
|
32
|
-
throw this.unavailable('enable is not available on web.');
|
|
33
|
-
}
|
|
34
|
-
async disable() {
|
|
35
|
-
throw this.unavailable('disable is not available on web.');
|
|
36
|
-
}
|
|
37
|
-
async startEnabledNotifications() {
|
|
38
|
-
// not available on web
|
|
39
|
-
}
|
|
40
|
-
async stopEnabledNotifications() {
|
|
41
|
-
// not available on web
|
|
42
|
-
}
|
|
43
|
-
async isLocationEnabled() {
|
|
44
|
-
throw this.unavailable('isLocationEnabled is not available on web.');
|
|
45
|
-
}
|
|
46
|
-
async openLocationSettings() {
|
|
47
|
-
throw this.unavailable('openLocationSettings is not available on web.');
|
|
48
|
-
}
|
|
49
|
-
async openBluetoothSettings() {
|
|
50
|
-
throw this.unavailable('openBluetoothSettings is not available on web.');
|
|
51
|
-
}
|
|
52
|
-
async openAppSettings() {
|
|
53
|
-
throw this.unavailable('openAppSettings is not available on web.');
|
|
54
|
-
}
|
|
55
|
-
async setDisplayStrings() {
|
|
56
|
-
// not available on web
|
|
57
|
-
}
|
|
58
|
-
async requestDevice(options) {
|
|
59
|
-
const filters = this.getFilters(options);
|
|
60
|
-
const device = await navigator.bluetooth.requestDevice({
|
|
61
|
-
filters: filters.length ? filters : undefined,
|
|
62
|
-
optionalServices: options === null || options === void 0 ? void 0 : options.optionalServices,
|
|
63
|
-
acceptAllDevices: filters.length === 0,
|
|
64
|
-
});
|
|
65
|
-
this.deviceMap.set(device.id, device);
|
|
66
|
-
const bleDevice = this.getBleDevice(device);
|
|
67
|
-
return bleDevice;
|
|
68
|
-
}
|
|
69
|
-
async requestLEScan(options) {
|
|
70
|
-
this.requestBleDeviceOptions = options;
|
|
71
|
-
const filters = this.getFilters(options);
|
|
72
|
-
await this.stopLEScan();
|
|
73
|
-
this.discoveredDevices = new Map();
|
|
74
|
-
navigator.bluetooth.removeEventListener('advertisementreceived', this.onAdvertisementReceivedCallback);
|
|
75
|
-
navigator.bluetooth.addEventListener('advertisementreceived', this.onAdvertisementReceivedCallback);
|
|
76
|
-
this.scan = await navigator.bluetooth.requestLEScan({
|
|
77
|
-
filters: filters.length ? filters : undefined,
|
|
78
|
-
acceptAllAdvertisements: filters.length === 0,
|
|
79
|
-
keepRepeatedDevices: options === null || options === void 0 ? void 0 : options.allowDuplicates,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
onAdvertisementReceived(event) {
|
|
83
|
-
var _a, _b, _c;
|
|
84
|
-
const deviceId = event.device.id;
|
|
85
|
-
this.deviceMap.set(deviceId, event.device);
|
|
86
|
-
const isNew = !this.discoveredDevices.has(deviceId);
|
|
87
|
-
// Apply service data filtering client-side (Web Bluetooth API doesn't support it in scan filters)
|
|
88
|
-
if (((_a = this.requestBleDeviceOptions) === null || _a === void 0 ? void 0 : _a.serviceData) && !this.matchesServiceDataFilter(event)) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (isNew || ((_b = this.requestBleDeviceOptions) === null || _b === void 0 ? void 0 : _b.allowDuplicates)) {
|
|
92
|
-
this.discoveredDevices.set(deviceId, true);
|
|
93
|
-
const device = this.getBleDevice(event.device);
|
|
94
|
-
const result = {
|
|
95
|
-
device,
|
|
96
|
-
localName: device.name,
|
|
97
|
-
rssi: event.rssi,
|
|
98
|
-
txPower: event.txPower,
|
|
99
|
-
manufacturerData: mapToObject(event.manufacturerData),
|
|
100
|
-
serviceData: mapToObject(event.serviceData),
|
|
101
|
-
uuids: (_c = event.uuids) === null || _c === void 0 ? void 0 : _c.map(webUUIDToString),
|
|
102
|
-
};
|
|
103
|
-
this.notifyListeners('onScanResult', result);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
async stopLEScan() {
|
|
107
|
-
var _a;
|
|
108
|
-
if ((_a = this.scan) === null || _a === void 0 ? void 0 : _a.active) {
|
|
109
|
-
this.scan.stop();
|
|
110
|
-
}
|
|
111
|
-
this.scan = null;
|
|
112
|
-
}
|
|
113
|
-
async getDevices(options) {
|
|
114
|
-
const devices = await navigator.bluetooth.getDevices();
|
|
115
|
-
const bleDevices = devices
|
|
116
|
-
.filter((device) => options.deviceIds.includes(device.id))
|
|
117
|
-
.map((device) => {
|
|
118
|
-
this.deviceMap.set(device.id, device);
|
|
119
|
-
const bleDevice = this.getBleDevice(device);
|
|
120
|
-
return bleDevice;
|
|
121
|
-
});
|
|
122
|
-
return { devices: bleDevices };
|
|
123
|
-
}
|
|
124
|
-
async getConnectedDevices(_options) {
|
|
125
|
-
const devices = await navigator.bluetooth.getDevices();
|
|
126
|
-
const bleDevices = devices
|
|
127
|
-
.filter((device) => {
|
|
128
|
-
var _a;
|
|
129
|
-
return (_a = device.gatt) === null || _a === void 0 ? void 0 : _a.connected;
|
|
130
|
-
})
|
|
131
|
-
.map((device) => {
|
|
132
|
-
this.deviceMap.set(device.id, device);
|
|
133
|
-
const bleDevice = this.getBleDevice(device);
|
|
134
|
-
return bleDevice;
|
|
135
|
-
});
|
|
136
|
-
return { devices: bleDevices };
|
|
137
|
-
}
|
|
138
|
-
async getBondedDevices() {
|
|
139
|
-
return {};
|
|
140
|
-
}
|
|
141
|
-
async connect(options) {
|
|
142
|
-
var _a, _b;
|
|
143
|
-
const device = this.getDeviceFromMap(options.deviceId);
|
|
144
|
-
device.removeEventListener('gattserverdisconnected', this.onDisconnectedCallback);
|
|
145
|
-
device.addEventListener('gattserverdisconnected', this.onDisconnectedCallback);
|
|
146
|
-
const timeoutError = Symbol();
|
|
147
|
-
if (device.gatt === undefined) {
|
|
148
|
-
throw new Error('No gatt server available.');
|
|
149
|
-
}
|
|
150
|
-
try {
|
|
151
|
-
const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : this.DEFAULT_CONNECTION_TIMEOUT;
|
|
152
|
-
await runWithTimeout(device.gatt.connect(), timeout, timeoutError);
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
// cancel pending connect call, does not work yet in chromium because of a bug:
|
|
156
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=684073
|
|
157
|
-
await ((_b = device.gatt) === null || _b === void 0 ? void 0 : _b.disconnect());
|
|
158
|
-
if (error === timeoutError) {
|
|
159
|
-
throw new Error('Connection timeout');
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
throw error;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
onDisconnected(event) {
|
|
167
|
-
const deviceId = event.target.id;
|
|
168
|
-
const key = `disconnected|${deviceId}`;
|
|
169
|
-
this.notifyListeners(key, null);
|
|
170
|
-
}
|
|
171
|
-
async createBond(_options) {
|
|
172
|
-
throw this.unavailable('createBond is not available on web.');
|
|
173
|
-
}
|
|
174
|
-
async isBonded(_options) {
|
|
175
|
-
throw this.unavailable('isBonded is not available on web.');
|
|
176
|
-
}
|
|
177
|
-
async disconnect(options) {
|
|
178
|
-
var _a;
|
|
179
|
-
(_a = this.getDeviceFromMap(options.deviceId).gatt) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
180
|
-
}
|
|
181
|
-
async getServices(options) {
|
|
182
|
-
var _a, _b;
|
|
183
|
-
const services = (_b = (await ((_a = this.getDeviceFromMap(options.deviceId).gatt) === null || _a === void 0 ? void 0 : _a.getPrimaryServices()))) !== null && _b !== void 0 ? _b : [];
|
|
184
|
-
const bleServices = [];
|
|
185
|
-
for (const service of services) {
|
|
186
|
-
const characteristics = await service.getCharacteristics();
|
|
187
|
-
const bleCharacteristics = [];
|
|
188
|
-
for (const characteristic of characteristics) {
|
|
189
|
-
bleCharacteristics.push({
|
|
190
|
-
uuid: characteristic.uuid,
|
|
191
|
-
properties: this.getProperties(characteristic),
|
|
192
|
-
descriptors: await this.getDescriptors(characteristic),
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
bleServices.push({ uuid: service.uuid, characteristics: bleCharacteristics });
|
|
196
|
-
}
|
|
197
|
-
return { services: bleServices };
|
|
198
|
-
}
|
|
199
|
-
async getDescriptors(characteristic) {
|
|
200
|
-
try {
|
|
201
|
-
const descriptors = await characteristic.getDescriptors();
|
|
202
|
-
return descriptors.map((descriptor) => ({
|
|
203
|
-
uuid: descriptor.uuid,
|
|
204
|
-
}));
|
|
205
|
-
}
|
|
206
|
-
catch (_a) {
|
|
207
|
-
return [];
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
getProperties(characteristic) {
|
|
211
|
-
return {
|
|
212
|
-
broadcast: characteristic.properties.broadcast,
|
|
213
|
-
read: characteristic.properties.read,
|
|
214
|
-
writeWithoutResponse: characteristic.properties.writeWithoutResponse,
|
|
215
|
-
write: characteristic.properties.write,
|
|
216
|
-
notify: characteristic.properties.notify,
|
|
217
|
-
indicate: characteristic.properties.indicate,
|
|
218
|
-
authenticatedSignedWrites: characteristic.properties.authenticatedSignedWrites,
|
|
219
|
-
reliableWrite: characteristic.properties.reliableWrite,
|
|
220
|
-
writableAuxiliaries: characteristic.properties.writableAuxiliaries,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
async getCharacteristic(options) {
|
|
224
|
-
var _a;
|
|
225
|
-
const service = await ((_a = this.getDeviceFromMap(options.deviceId).gatt) === null || _a === void 0 ? void 0 : _a.getPrimaryService(options === null || options === void 0 ? void 0 : options.service));
|
|
226
|
-
return service === null || service === void 0 ? void 0 : service.getCharacteristic(options === null || options === void 0 ? void 0 : options.characteristic);
|
|
227
|
-
}
|
|
228
|
-
async getDescriptor(options) {
|
|
229
|
-
const characteristic = await this.getCharacteristic(options);
|
|
230
|
-
return characteristic === null || characteristic === void 0 ? void 0 : characteristic.getDescriptor(options === null || options === void 0 ? void 0 : options.descriptor);
|
|
231
|
-
}
|
|
232
|
-
async discoverServices(_options) {
|
|
233
|
-
throw this.unavailable('discoverServices is not available on web.');
|
|
234
|
-
}
|
|
235
|
-
async getMtu(_options) {
|
|
236
|
-
throw this.unavailable('getMtu is not available on web.');
|
|
237
|
-
}
|
|
238
|
-
async requestConnectionPriority(_options) {
|
|
239
|
-
throw this.unavailable('requestConnectionPriority is not available on web.');
|
|
240
|
-
}
|
|
241
|
-
async readRssi(_options) {
|
|
242
|
-
throw this.unavailable('readRssi is not available on web.');
|
|
243
|
-
}
|
|
244
|
-
async read(options) {
|
|
245
|
-
const characteristic = await this.getCharacteristic(options);
|
|
246
|
-
const value = await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.readValue());
|
|
247
|
-
return { value };
|
|
248
|
-
}
|
|
249
|
-
async write(options) {
|
|
250
|
-
const characteristic = await this.getCharacteristic(options);
|
|
251
|
-
let dataView;
|
|
252
|
-
if (typeof options.value === 'string') {
|
|
253
|
-
dataView = hexStringToDataView(options.value);
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
dataView = options.value;
|
|
257
|
-
}
|
|
258
|
-
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.writeValueWithResponse(toArrayBufferDataView(dataView)));
|
|
259
|
-
}
|
|
260
|
-
async writeWithoutResponse(options) {
|
|
261
|
-
const characteristic = await this.getCharacteristic(options);
|
|
262
|
-
let dataView;
|
|
263
|
-
if (typeof options.value === 'string') {
|
|
264
|
-
dataView = hexStringToDataView(options.value);
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
dataView = options.value;
|
|
268
|
-
}
|
|
269
|
-
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.writeValueWithoutResponse(toArrayBufferDataView(dataView)));
|
|
270
|
-
}
|
|
271
|
-
async readDescriptor(options) {
|
|
272
|
-
const descriptor = await this.getDescriptor(options);
|
|
273
|
-
const value = await (descriptor === null || descriptor === void 0 ? void 0 : descriptor.readValue());
|
|
274
|
-
return { value };
|
|
275
|
-
}
|
|
276
|
-
async writeDescriptor(options) {
|
|
277
|
-
const descriptor = await this.getDescriptor(options);
|
|
278
|
-
let dataView;
|
|
279
|
-
if (typeof options.value === 'string') {
|
|
280
|
-
dataView = hexStringToDataView(options.value);
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
dataView = options.value;
|
|
284
|
-
}
|
|
285
|
-
await (descriptor === null || descriptor === void 0 ? void 0 : descriptor.writeValue(toArrayBufferDataView(dataView)));
|
|
286
|
-
}
|
|
287
|
-
async startNotifications(options) {
|
|
288
|
-
const characteristic = await this.getCharacteristic(options);
|
|
289
|
-
characteristic === null || characteristic === void 0 ? void 0 : characteristic.removeEventListener('characteristicvaluechanged', this.onCharacteristicValueChangedCallback);
|
|
290
|
-
characteristic === null || characteristic === void 0 ? void 0 : characteristic.addEventListener('characteristicvaluechanged', this.onCharacteristicValueChangedCallback);
|
|
291
|
-
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.startNotifications());
|
|
292
|
-
}
|
|
293
|
-
onCharacteristicValueChanged(event) {
|
|
294
|
-
var _a, _b;
|
|
295
|
-
const characteristic = event.target;
|
|
296
|
-
const key = `notification|${(_a = characteristic.service) === null || _a === void 0 ? void 0 : _a.device.id}|${(_b = characteristic.service) === null || _b === void 0 ? void 0 : _b.uuid}|${characteristic.uuid}`;
|
|
297
|
-
this.notifyListeners(key, {
|
|
298
|
-
value: characteristic.value,
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
async stopNotifications(options) {
|
|
302
|
-
const characteristic = await this.getCharacteristic(options);
|
|
303
|
-
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.stopNotifications());
|
|
304
|
-
}
|
|
305
|
-
getFilters(options) {
|
|
306
|
-
var _a, _b;
|
|
307
|
-
const filters = [];
|
|
308
|
-
for (const service of (_a = options === null || options === void 0 ? void 0 : options.services) !== null && _a !== void 0 ? _a : []) {
|
|
309
|
-
filters.push({
|
|
310
|
-
services: [service],
|
|
311
|
-
name: options === null || options === void 0 ? void 0 : options.name,
|
|
312
|
-
namePrefix: options === null || options === void 0 ? void 0 : options.namePrefix,
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
if (((options === null || options === void 0 ? void 0 : options.name) || (options === null || options === void 0 ? void 0 : options.namePrefix)) && filters.length === 0) {
|
|
316
|
-
filters.push({
|
|
317
|
-
name: options.name,
|
|
318
|
-
namePrefix: options.namePrefix,
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
for (const manufacturerData of (_b = options === null || options === void 0 ? void 0 : options.manufacturerData) !== null && _b !== void 0 ? _b : []) {
|
|
322
|
-
// Cast to any to avoid type incompatibility - conversion is handled in bleClient.ts
|
|
323
|
-
filters.push({
|
|
324
|
-
manufacturerData: [manufacturerData],
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
// Note: Web Bluetooth API does not support service data in scan filters.
|
|
328
|
-
// Service data filtering will be done client-side in onAdvertisementReceived.
|
|
329
|
-
// We still accept serviceData in options for API consistency across platforms.
|
|
330
|
-
return filters;
|
|
331
|
-
}
|
|
332
|
-
matchesServiceDataFilter(event) {
|
|
333
|
-
var _a;
|
|
334
|
-
const filters = (_a = this.requestBleDeviceOptions) === null || _a === void 0 ? void 0 : _a.serviceData;
|
|
335
|
-
if (!filters || filters.length === 0) {
|
|
336
|
-
return true; // No filters, accept all
|
|
337
|
-
}
|
|
338
|
-
if (!event.serviceData) {
|
|
339
|
-
return false; // No service data in advertisement
|
|
340
|
-
}
|
|
341
|
-
// Check if any filter matches (OR logic)
|
|
342
|
-
for (const filter of filters) {
|
|
343
|
-
const serviceData = event.serviceData.get(filter.serviceUuid);
|
|
344
|
-
if (!serviceData) {
|
|
345
|
-
continue; // This filter doesn't match, try next
|
|
346
|
-
}
|
|
347
|
-
// If we have service data for this UUID
|
|
348
|
-
if (!filter.dataPrefix) {
|
|
349
|
-
return true; // Filter matched by service UUID alone
|
|
350
|
-
}
|
|
351
|
-
// Check data prefix (DataView on web)
|
|
352
|
-
const data = new Uint8Array(serviceData.buffer);
|
|
353
|
-
const prefixView = filter.dataPrefix;
|
|
354
|
-
if (data.length < prefixView.byteLength) {
|
|
355
|
-
continue; // Data too short
|
|
356
|
-
}
|
|
357
|
-
// Apply mask if provided
|
|
358
|
-
if (filter.mask) {
|
|
359
|
-
const maskView = filter.mask;
|
|
360
|
-
let matches = true;
|
|
361
|
-
for (let i = 0; i < prefixView.byteLength; i++) {
|
|
362
|
-
if ((data[i] & maskView.getUint8(i)) !== (prefixView.getUint8(i) & maskView.getUint8(i))) {
|
|
363
|
-
matches = false;
|
|
364
|
-
break;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
if (matches) {
|
|
368
|
-
return true;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
// Check if data starts with prefix
|
|
373
|
-
let matches = true;
|
|
374
|
-
for (let i = 0; i < prefixView.byteLength; i++) {
|
|
375
|
-
if (data[i] !== prefixView.getUint8(i)) {
|
|
376
|
-
matches = false;
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
if (matches) {
|
|
381
|
-
return true;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return false; // No filter matched
|
|
386
|
-
}
|
|
387
|
-
getDeviceFromMap(deviceId) {
|
|
388
|
-
const device = this.deviceMap.get(deviceId);
|
|
389
|
-
if (device === undefined) {
|
|
390
|
-
throw new Error('Device not found. Call "requestDevice", "requestLEScan" or "getDevices" first.');
|
|
391
|
-
}
|
|
392
|
-
return device;
|
|
393
|
-
}
|
|
394
|
-
getBleDevice(device) {
|
|
395
|
-
var _a;
|
|
396
|
-
const bleDevice = {
|
|
397
|
-
deviceId: device.id,
|
|
398
|
-
// use undefined instead of null if name is not available
|
|
399
|
-
name: (_a = device.name) !== null && _a !== void 0 ? _a : undefined,
|
|
400
|
-
};
|
|
401
|
-
return bleDevice;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import { hexStringToDataView, mapToObject, toArrayBufferDataView, webUUIDToString } from './conversion';
|
|
3
|
+
import { runWithTimeout } from './timeout';
|
|
4
|
+
export class BluetoothLeWeb extends WebPlugin {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.deviceMap = new Map();
|
|
8
|
+
this.discoveredDevices = new Map();
|
|
9
|
+
this.scan = null;
|
|
10
|
+
this.DEFAULT_CONNECTION_TIMEOUT = 10000;
|
|
11
|
+
this.onAdvertisementReceivedCallback = this.onAdvertisementReceived.bind(this);
|
|
12
|
+
this.onDisconnectedCallback = this.onDisconnected.bind(this);
|
|
13
|
+
this.onCharacteristicValueChangedCallback = this.onCharacteristicValueChanged.bind(this);
|
|
14
|
+
}
|
|
15
|
+
async initialize() {
|
|
16
|
+
if (typeof navigator === 'undefined' || !navigator.bluetooth) {
|
|
17
|
+
throw this.unavailable('Web Bluetooth API not available in this browser.');
|
|
18
|
+
}
|
|
19
|
+
const isAvailable = await navigator.bluetooth.getAvailability();
|
|
20
|
+
if (!isAvailable) {
|
|
21
|
+
throw this.unavailable('No Bluetooth radio available.');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async isEnabled() {
|
|
25
|
+
// not available on web
|
|
26
|
+
return { value: true };
|
|
27
|
+
}
|
|
28
|
+
async requestEnable() {
|
|
29
|
+
throw this.unavailable('requestEnable is not available on web.');
|
|
30
|
+
}
|
|
31
|
+
async enable() {
|
|
32
|
+
throw this.unavailable('enable is not available on web.');
|
|
33
|
+
}
|
|
34
|
+
async disable() {
|
|
35
|
+
throw this.unavailable('disable is not available on web.');
|
|
36
|
+
}
|
|
37
|
+
async startEnabledNotifications() {
|
|
38
|
+
// not available on web
|
|
39
|
+
}
|
|
40
|
+
async stopEnabledNotifications() {
|
|
41
|
+
// not available on web
|
|
42
|
+
}
|
|
43
|
+
async isLocationEnabled() {
|
|
44
|
+
throw this.unavailable('isLocationEnabled is not available on web.');
|
|
45
|
+
}
|
|
46
|
+
async openLocationSettings() {
|
|
47
|
+
throw this.unavailable('openLocationSettings is not available on web.');
|
|
48
|
+
}
|
|
49
|
+
async openBluetoothSettings() {
|
|
50
|
+
throw this.unavailable('openBluetoothSettings is not available on web.');
|
|
51
|
+
}
|
|
52
|
+
async openAppSettings() {
|
|
53
|
+
throw this.unavailable('openAppSettings is not available on web.');
|
|
54
|
+
}
|
|
55
|
+
async setDisplayStrings() {
|
|
56
|
+
// not available on web
|
|
57
|
+
}
|
|
58
|
+
async requestDevice(options) {
|
|
59
|
+
const filters = this.getFilters(options);
|
|
60
|
+
const device = await navigator.bluetooth.requestDevice({
|
|
61
|
+
filters: filters.length ? filters : undefined,
|
|
62
|
+
optionalServices: options === null || options === void 0 ? void 0 : options.optionalServices,
|
|
63
|
+
acceptAllDevices: filters.length === 0,
|
|
64
|
+
});
|
|
65
|
+
this.deviceMap.set(device.id, device);
|
|
66
|
+
const bleDevice = this.getBleDevice(device);
|
|
67
|
+
return bleDevice;
|
|
68
|
+
}
|
|
69
|
+
async requestLEScan(options) {
|
|
70
|
+
this.requestBleDeviceOptions = options;
|
|
71
|
+
const filters = this.getFilters(options);
|
|
72
|
+
await this.stopLEScan();
|
|
73
|
+
this.discoveredDevices = new Map();
|
|
74
|
+
navigator.bluetooth.removeEventListener('advertisementreceived', this.onAdvertisementReceivedCallback);
|
|
75
|
+
navigator.bluetooth.addEventListener('advertisementreceived', this.onAdvertisementReceivedCallback);
|
|
76
|
+
this.scan = await navigator.bluetooth.requestLEScan({
|
|
77
|
+
filters: filters.length ? filters : undefined,
|
|
78
|
+
acceptAllAdvertisements: filters.length === 0,
|
|
79
|
+
keepRepeatedDevices: options === null || options === void 0 ? void 0 : options.allowDuplicates,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
onAdvertisementReceived(event) {
|
|
83
|
+
var _a, _b, _c;
|
|
84
|
+
const deviceId = event.device.id;
|
|
85
|
+
this.deviceMap.set(deviceId, event.device);
|
|
86
|
+
const isNew = !this.discoveredDevices.has(deviceId);
|
|
87
|
+
// Apply service data filtering client-side (Web Bluetooth API doesn't support it in scan filters)
|
|
88
|
+
if (((_a = this.requestBleDeviceOptions) === null || _a === void 0 ? void 0 : _a.serviceData) && !this.matchesServiceDataFilter(event)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (isNew || ((_b = this.requestBleDeviceOptions) === null || _b === void 0 ? void 0 : _b.allowDuplicates)) {
|
|
92
|
+
this.discoveredDevices.set(deviceId, true);
|
|
93
|
+
const device = this.getBleDevice(event.device);
|
|
94
|
+
const result = {
|
|
95
|
+
device,
|
|
96
|
+
localName: device.name,
|
|
97
|
+
rssi: event.rssi,
|
|
98
|
+
txPower: event.txPower,
|
|
99
|
+
manufacturerData: mapToObject(event.manufacturerData),
|
|
100
|
+
serviceData: mapToObject(event.serviceData),
|
|
101
|
+
uuids: (_c = event.uuids) === null || _c === void 0 ? void 0 : _c.map(webUUIDToString),
|
|
102
|
+
};
|
|
103
|
+
this.notifyListeners('onScanResult', result);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async stopLEScan() {
|
|
107
|
+
var _a;
|
|
108
|
+
if ((_a = this.scan) === null || _a === void 0 ? void 0 : _a.active) {
|
|
109
|
+
this.scan.stop();
|
|
110
|
+
}
|
|
111
|
+
this.scan = null;
|
|
112
|
+
}
|
|
113
|
+
async getDevices(options) {
|
|
114
|
+
const devices = await navigator.bluetooth.getDevices();
|
|
115
|
+
const bleDevices = devices
|
|
116
|
+
.filter((device) => options.deviceIds.includes(device.id))
|
|
117
|
+
.map((device) => {
|
|
118
|
+
this.deviceMap.set(device.id, device);
|
|
119
|
+
const bleDevice = this.getBleDevice(device);
|
|
120
|
+
return bleDevice;
|
|
121
|
+
});
|
|
122
|
+
return { devices: bleDevices };
|
|
123
|
+
}
|
|
124
|
+
async getConnectedDevices(_options) {
|
|
125
|
+
const devices = await navigator.bluetooth.getDevices();
|
|
126
|
+
const bleDevices = devices
|
|
127
|
+
.filter((device) => {
|
|
128
|
+
var _a;
|
|
129
|
+
return (_a = device.gatt) === null || _a === void 0 ? void 0 : _a.connected;
|
|
130
|
+
})
|
|
131
|
+
.map((device) => {
|
|
132
|
+
this.deviceMap.set(device.id, device);
|
|
133
|
+
const bleDevice = this.getBleDevice(device);
|
|
134
|
+
return bleDevice;
|
|
135
|
+
});
|
|
136
|
+
return { devices: bleDevices };
|
|
137
|
+
}
|
|
138
|
+
async getBondedDevices() {
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
async connect(options) {
|
|
142
|
+
var _a, _b;
|
|
143
|
+
const device = this.getDeviceFromMap(options.deviceId);
|
|
144
|
+
device.removeEventListener('gattserverdisconnected', this.onDisconnectedCallback);
|
|
145
|
+
device.addEventListener('gattserverdisconnected', this.onDisconnectedCallback);
|
|
146
|
+
const timeoutError = Symbol();
|
|
147
|
+
if (device.gatt === undefined) {
|
|
148
|
+
throw new Error('No gatt server available.');
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : this.DEFAULT_CONNECTION_TIMEOUT;
|
|
152
|
+
await runWithTimeout(device.gatt.connect(), timeout, timeoutError);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
// cancel pending connect call, does not work yet in chromium because of a bug:
|
|
156
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=684073
|
|
157
|
+
await ((_b = device.gatt) === null || _b === void 0 ? void 0 : _b.disconnect());
|
|
158
|
+
if (error === timeoutError) {
|
|
159
|
+
throw new Error('Connection timeout');
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
onDisconnected(event) {
|
|
167
|
+
const deviceId = event.target.id;
|
|
168
|
+
const key = `disconnected|${deviceId}`;
|
|
169
|
+
this.notifyListeners(key, null);
|
|
170
|
+
}
|
|
171
|
+
async createBond(_options) {
|
|
172
|
+
throw this.unavailable('createBond is not available on web.');
|
|
173
|
+
}
|
|
174
|
+
async isBonded(_options) {
|
|
175
|
+
throw this.unavailable('isBonded is not available on web.');
|
|
176
|
+
}
|
|
177
|
+
async disconnect(options) {
|
|
178
|
+
var _a;
|
|
179
|
+
(_a = this.getDeviceFromMap(options.deviceId).gatt) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
180
|
+
}
|
|
181
|
+
async getServices(options) {
|
|
182
|
+
var _a, _b;
|
|
183
|
+
const services = (_b = (await ((_a = this.getDeviceFromMap(options.deviceId).gatt) === null || _a === void 0 ? void 0 : _a.getPrimaryServices()))) !== null && _b !== void 0 ? _b : [];
|
|
184
|
+
const bleServices = [];
|
|
185
|
+
for (const service of services) {
|
|
186
|
+
const characteristics = await service.getCharacteristics();
|
|
187
|
+
const bleCharacteristics = [];
|
|
188
|
+
for (const characteristic of characteristics) {
|
|
189
|
+
bleCharacteristics.push({
|
|
190
|
+
uuid: characteristic.uuid,
|
|
191
|
+
properties: this.getProperties(characteristic),
|
|
192
|
+
descriptors: await this.getDescriptors(characteristic),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
bleServices.push({ uuid: service.uuid, characteristics: bleCharacteristics });
|
|
196
|
+
}
|
|
197
|
+
return { services: bleServices };
|
|
198
|
+
}
|
|
199
|
+
async getDescriptors(characteristic) {
|
|
200
|
+
try {
|
|
201
|
+
const descriptors = await characteristic.getDescriptors();
|
|
202
|
+
return descriptors.map((descriptor) => ({
|
|
203
|
+
uuid: descriptor.uuid,
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
catch (_a) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
getProperties(characteristic) {
|
|
211
|
+
return {
|
|
212
|
+
broadcast: characteristic.properties.broadcast,
|
|
213
|
+
read: characteristic.properties.read,
|
|
214
|
+
writeWithoutResponse: characteristic.properties.writeWithoutResponse,
|
|
215
|
+
write: characteristic.properties.write,
|
|
216
|
+
notify: characteristic.properties.notify,
|
|
217
|
+
indicate: characteristic.properties.indicate,
|
|
218
|
+
authenticatedSignedWrites: characteristic.properties.authenticatedSignedWrites,
|
|
219
|
+
reliableWrite: characteristic.properties.reliableWrite,
|
|
220
|
+
writableAuxiliaries: characteristic.properties.writableAuxiliaries,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async getCharacteristic(options) {
|
|
224
|
+
var _a;
|
|
225
|
+
const service = await ((_a = this.getDeviceFromMap(options.deviceId).gatt) === null || _a === void 0 ? void 0 : _a.getPrimaryService(options === null || options === void 0 ? void 0 : options.service));
|
|
226
|
+
return service === null || service === void 0 ? void 0 : service.getCharacteristic(options === null || options === void 0 ? void 0 : options.characteristic);
|
|
227
|
+
}
|
|
228
|
+
async getDescriptor(options) {
|
|
229
|
+
const characteristic = await this.getCharacteristic(options);
|
|
230
|
+
return characteristic === null || characteristic === void 0 ? void 0 : characteristic.getDescriptor(options === null || options === void 0 ? void 0 : options.descriptor);
|
|
231
|
+
}
|
|
232
|
+
async discoverServices(_options) {
|
|
233
|
+
throw this.unavailable('discoverServices is not available on web.');
|
|
234
|
+
}
|
|
235
|
+
async getMtu(_options) {
|
|
236
|
+
throw this.unavailable('getMtu is not available on web.');
|
|
237
|
+
}
|
|
238
|
+
async requestConnectionPriority(_options) {
|
|
239
|
+
throw this.unavailable('requestConnectionPriority is not available on web.');
|
|
240
|
+
}
|
|
241
|
+
async readRssi(_options) {
|
|
242
|
+
throw this.unavailable('readRssi is not available on web.');
|
|
243
|
+
}
|
|
244
|
+
async read(options) {
|
|
245
|
+
const characteristic = await this.getCharacteristic(options);
|
|
246
|
+
const value = await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.readValue());
|
|
247
|
+
return { value };
|
|
248
|
+
}
|
|
249
|
+
async write(options) {
|
|
250
|
+
const characteristic = await this.getCharacteristic(options);
|
|
251
|
+
let dataView;
|
|
252
|
+
if (typeof options.value === 'string') {
|
|
253
|
+
dataView = hexStringToDataView(options.value);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
dataView = options.value;
|
|
257
|
+
}
|
|
258
|
+
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.writeValueWithResponse(toArrayBufferDataView(dataView)));
|
|
259
|
+
}
|
|
260
|
+
async writeWithoutResponse(options) {
|
|
261
|
+
const characteristic = await this.getCharacteristic(options);
|
|
262
|
+
let dataView;
|
|
263
|
+
if (typeof options.value === 'string') {
|
|
264
|
+
dataView = hexStringToDataView(options.value);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
dataView = options.value;
|
|
268
|
+
}
|
|
269
|
+
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.writeValueWithoutResponse(toArrayBufferDataView(dataView)));
|
|
270
|
+
}
|
|
271
|
+
async readDescriptor(options) {
|
|
272
|
+
const descriptor = await this.getDescriptor(options);
|
|
273
|
+
const value = await (descriptor === null || descriptor === void 0 ? void 0 : descriptor.readValue());
|
|
274
|
+
return { value };
|
|
275
|
+
}
|
|
276
|
+
async writeDescriptor(options) {
|
|
277
|
+
const descriptor = await this.getDescriptor(options);
|
|
278
|
+
let dataView;
|
|
279
|
+
if (typeof options.value === 'string') {
|
|
280
|
+
dataView = hexStringToDataView(options.value);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
dataView = options.value;
|
|
284
|
+
}
|
|
285
|
+
await (descriptor === null || descriptor === void 0 ? void 0 : descriptor.writeValue(toArrayBufferDataView(dataView)));
|
|
286
|
+
}
|
|
287
|
+
async startNotifications(options) {
|
|
288
|
+
const characteristic = await this.getCharacteristic(options);
|
|
289
|
+
characteristic === null || characteristic === void 0 ? void 0 : characteristic.removeEventListener('characteristicvaluechanged', this.onCharacteristicValueChangedCallback);
|
|
290
|
+
characteristic === null || characteristic === void 0 ? void 0 : characteristic.addEventListener('characteristicvaluechanged', this.onCharacteristicValueChangedCallback);
|
|
291
|
+
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.startNotifications());
|
|
292
|
+
}
|
|
293
|
+
onCharacteristicValueChanged(event) {
|
|
294
|
+
var _a, _b;
|
|
295
|
+
const characteristic = event.target;
|
|
296
|
+
const key = `notification|${(_a = characteristic.service) === null || _a === void 0 ? void 0 : _a.device.id}|${(_b = characteristic.service) === null || _b === void 0 ? void 0 : _b.uuid}|${characteristic.uuid}`;
|
|
297
|
+
this.notifyListeners(key, {
|
|
298
|
+
value: characteristic.value,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async stopNotifications(options) {
|
|
302
|
+
const characteristic = await this.getCharacteristic(options);
|
|
303
|
+
await (characteristic === null || characteristic === void 0 ? void 0 : characteristic.stopNotifications());
|
|
304
|
+
}
|
|
305
|
+
getFilters(options) {
|
|
306
|
+
var _a, _b;
|
|
307
|
+
const filters = [];
|
|
308
|
+
for (const service of (_a = options === null || options === void 0 ? void 0 : options.services) !== null && _a !== void 0 ? _a : []) {
|
|
309
|
+
filters.push({
|
|
310
|
+
services: [service],
|
|
311
|
+
name: options === null || options === void 0 ? void 0 : options.name,
|
|
312
|
+
namePrefix: options === null || options === void 0 ? void 0 : options.namePrefix,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (((options === null || options === void 0 ? void 0 : options.name) || (options === null || options === void 0 ? void 0 : options.namePrefix)) && filters.length === 0) {
|
|
316
|
+
filters.push({
|
|
317
|
+
name: options.name,
|
|
318
|
+
namePrefix: options.namePrefix,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
for (const manufacturerData of (_b = options === null || options === void 0 ? void 0 : options.manufacturerData) !== null && _b !== void 0 ? _b : []) {
|
|
322
|
+
// Cast to any to avoid type incompatibility - conversion is handled in bleClient.ts
|
|
323
|
+
filters.push({
|
|
324
|
+
manufacturerData: [manufacturerData],
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
// Note: Web Bluetooth API does not support service data in scan filters.
|
|
328
|
+
// Service data filtering will be done client-side in onAdvertisementReceived.
|
|
329
|
+
// We still accept serviceData in options for API consistency across platforms.
|
|
330
|
+
return filters;
|
|
331
|
+
}
|
|
332
|
+
matchesServiceDataFilter(event) {
|
|
333
|
+
var _a;
|
|
334
|
+
const filters = (_a = this.requestBleDeviceOptions) === null || _a === void 0 ? void 0 : _a.serviceData;
|
|
335
|
+
if (!filters || filters.length === 0) {
|
|
336
|
+
return true; // No filters, accept all
|
|
337
|
+
}
|
|
338
|
+
if (!event.serviceData) {
|
|
339
|
+
return false; // No service data in advertisement
|
|
340
|
+
}
|
|
341
|
+
// Check if any filter matches (OR logic)
|
|
342
|
+
for (const filter of filters) {
|
|
343
|
+
const serviceData = event.serviceData.get(filter.serviceUuid);
|
|
344
|
+
if (!serviceData) {
|
|
345
|
+
continue; // This filter doesn't match, try next
|
|
346
|
+
}
|
|
347
|
+
// If we have service data for this UUID
|
|
348
|
+
if (!filter.dataPrefix) {
|
|
349
|
+
return true; // Filter matched by service UUID alone
|
|
350
|
+
}
|
|
351
|
+
// Check data prefix (DataView on web)
|
|
352
|
+
const data = new Uint8Array(serviceData.buffer);
|
|
353
|
+
const prefixView = filter.dataPrefix;
|
|
354
|
+
if (data.length < prefixView.byteLength) {
|
|
355
|
+
continue; // Data too short
|
|
356
|
+
}
|
|
357
|
+
// Apply mask if provided
|
|
358
|
+
if (filter.mask) {
|
|
359
|
+
const maskView = filter.mask;
|
|
360
|
+
let matches = true;
|
|
361
|
+
for (let i = 0; i < prefixView.byteLength; i++) {
|
|
362
|
+
if ((data[i] & maskView.getUint8(i)) !== (prefixView.getUint8(i) & maskView.getUint8(i))) {
|
|
363
|
+
matches = false;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (matches) {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
// Check if data starts with prefix
|
|
373
|
+
let matches = true;
|
|
374
|
+
for (let i = 0; i < prefixView.byteLength; i++) {
|
|
375
|
+
if (data[i] !== prefixView.getUint8(i)) {
|
|
376
|
+
matches = false;
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (matches) {
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return false; // No filter matched
|
|
386
|
+
}
|
|
387
|
+
getDeviceFromMap(deviceId) {
|
|
388
|
+
const device = this.deviceMap.get(deviceId);
|
|
389
|
+
if (device === undefined) {
|
|
390
|
+
throw new Error('Device not found. Call "requestDevice", "requestLEScan" or "getDevices" first.');
|
|
391
|
+
}
|
|
392
|
+
return device;
|
|
393
|
+
}
|
|
394
|
+
getBleDevice(device) {
|
|
395
|
+
var _a;
|
|
396
|
+
const bleDevice = {
|
|
397
|
+
deviceId: device.id,
|
|
398
|
+
// use undefined instead of null if name is not available
|
|
399
|
+
name: (_a = device.name) !== null && _a !== void 0 ? _a : undefined,
|
|
400
|
+
};
|
|
401
|
+
return bleDevice;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
404
|
//# sourceMappingURL=web.js.map
|