@fww_123/uni-ble-scanner 1.0.0

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/core/base-scanner.d.ts +99 -0
  4. package/dist/core/base-scanner.d.ts.map +1 -0
  5. package/dist/factory.d.ts +44 -0
  6. package/dist/factory.d.ts.map +1 -0
  7. package/dist/index.d.ts +15 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.esm.js +1791 -0
  10. package/dist/index.js +1818 -0
  11. package/dist/platforms/android-native.d.ts +30 -0
  12. package/dist/platforms/android-native.d.ts.map +1 -0
  13. package/dist/platforms/harmony-native.d.ts +24 -0
  14. package/dist/platforms/harmony-native.d.ts.map +1 -0
  15. package/dist/platforms/ios-native.d.ts +27 -0
  16. package/dist/platforms/ios-native.d.ts.map +1 -0
  17. package/dist/platforms/uniapp-scanner.d.ts +30 -0
  18. package/dist/platforms/uniapp-scanner.d.ts.map +1 -0
  19. package/dist/types.d.ts +170 -0
  20. package/dist/types.d.ts.map +1 -0
  21. package/dist/utils/data-parser.d.ts +32 -0
  22. package/dist/utils/data-parser.d.ts.map +1 -0
  23. package/dist/utils/errors.d.ts +27 -0
  24. package/dist/utils/errors.d.ts.map +1 -0
  25. package/dist/utils/permissions.d.ts +9 -0
  26. package/dist/utils/permissions.d.ts.map +1 -0
  27. package/dist/utils/platform.d.ts +18 -0
  28. package/dist/utils/platform.d.ts.map +1 -0
  29. package/package.json +58 -0
  30. package/src/core/base-scanner.ts +309 -0
  31. package/src/factory.ts +116 -0
  32. package/src/index.ts +54 -0
  33. package/src/platforms/android-native.ts +300 -0
  34. package/src/platforms/harmony-native.ts +267 -0
  35. package/src/platforms/ios-native.ts +264 -0
  36. package/src/platforms/uniapp-scanner.ts +171 -0
  37. package/src/types/global.d.ts +25 -0
  38. package/src/types/uni-types.d.ts +83 -0
  39. package/src/types.ts +178 -0
  40. package/src/utils/data-parser.ts +217 -0
  41. package/src/utils/errors.ts +105 -0
  42. package/src/utils/permissions.ts +244 -0
  43. package/src/utils/platform.ts +70 -0
@@ -0,0 +1,1791 @@
1
+ /**
2
+ * 扫描状态
3
+ */
4
+ var ScanState;
5
+ (function (ScanState) {
6
+ ScanState["IDLE"] = "idle";
7
+ ScanState["SCANNING"] = "scanning";
8
+ ScanState["STOPPING"] = "stopping";
9
+ ScanState["ERROR"] = "error";
10
+ })(ScanState || (ScanState = {}));
11
+ /**
12
+ * 平台类型
13
+ */
14
+ var Platform;
15
+ (function (Platform) {
16
+ Platform["ANDROID"] = "android";
17
+ Platform["IOS"] = "ios";
18
+ Platform["HARMONY"] = "harmony";
19
+ Platform["UNKNOWN"] = "unknown";
20
+ })(Platform || (Platform = {}));
21
+
22
+ /******************************************************************************
23
+ Copyright (c) Microsoft Corporation.
24
+
25
+ Permission to use, copy, modify, and/or distribute this software for any
26
+ purpose with or without fee is hereby granted.
27
+
28
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
29
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
30
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
31
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
32
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
33
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
34
+ PERFORMANCE OF THIS SOFTWARE.
35
+ ***************************************************************************** */
36
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
37
+
38
+
39
+ function __awaiter(thisArg, _arguments, P, generator) {
40
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
41
+ return new (P || (P = Promise))(function (resolve, reject) {
42
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
43
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
44
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
45
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
46
+ });
47
+ }
48
+
49
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ };
53
+
54
+ /**
55
+ * 错误码定义
56
+ */
57
+ const ErrorCodes = {
58
+ // 通用错误
59
+ UNKNOWN_ERROR: 1000,
60
+ NOT_INITIALIZED: 1001,
61
+ ALREADY_SCANNING: 1002,
62
+ NOT_SCANNING: 1003,
63
+ PERMISSION_DENIED: 1004,
64
+ BLUETOOTH_DISABLED: 1005,
65
+ UNSUPPORTED_PLATFORM: 1006,
66
+ // 蓝牙特定错误
67
+ ADAPTER_NOT_AVAILABLE: 2001,
68
+ SCAN_FAILED: 2002,
69
+ DEVICE_NOT_FOUND: 2003,
70
+ CONNECTION_FAILED: 2004,
71
+ // 平台特定错误
72
+ ANDROID_ERROR: 3000,
73
+ IOS_ERROR: 4000,
74
+ HARMONY_ERROR: 5000,
75
+ };
76
+ /**
77
+ * 创建错误对象
78
+ */
79
+ function createError(code, message, platformCode, originalError) {
80
+ return {
81
+ code,
82
+ message,
83
+ platformCode,
84
+ originalError
85
+ };
86
+ }
87
+ /**
88
+ * 通用错误
89
+ */
90
+ const Errors = {
91
+ notInitialized: () => createError(ErrorCodes.NOT_INITIALIZED, '蓝牙适配器未初始化,请先调用 init()'),
92
+ alreadyScanning: () => createError(ErrorCodes.ALREADY_SCANNING, '扫描已在进行中'),
93
+ notScanning: () => createError(ErrorCodes.NOT_SCANNING, '扫描未在进行中'),
94
+ permissionDenied: () => createError(ErrorCodes.PERMISSION_DENIED, '蓝牙权限被拒绝'),
95
+ bluetoothDisabled: () => createError(ErrorCodes.BLUETOOTH_DISABLED, '蓝牙未开启'),
96
+ unsupportedPlatform: () => createError(ErrorCodes.UNSUPPORTED_PLATFORM, '当前平台不支持蓝牙功能'),
97
+ adapterNotAvailable: () => createError(ErrorCodes.ADAPTER_NOT_AVAILABLE, '蓝牙适配器不可用'),
98
+ scanFailed: (reason) => createError(ErrorCodes.SCAN_FAILED, `扫描失败: ${reason || '未知原因'}`),
99
+ };
100
+ /**
101
+ * 处理平台特定错误
102
+ */
103
+ function handlePlatformError(error, platform) {
104
+ const platformCode = (error === null || error === void 0 ? void 0 : error.code) || (error === null || error === void 0 ? void 0 : error.errCode);
105
+ const message = (error === null || error === void 0 ? void 0 : error.message) || (error === null || error === void 0 ? void 0 : error.errMsg) || '未知错误';
106
+ let code = ErrorCodes.UNKNOWN_ERROR;
107
+ switch (platform) {
108
+ case 'android':
109
+ code = ErrorCodes.ANDROID_ERROR;
110
+ break;
111
+ case 'ios':
112
+ code = ErrorCodes.IOS_ERROR;
113
+ break;
114
+ case 'harmony':
115
+ code = ErrorCodes.HARMONY_ERROR;
116
+ break;
117
+ }
118
+ return createError(code, `${platform}平台错误: ${message}`, platformCode, error);
119
+ }
120
+
121
+ /**
122
+ * 解析广播数据
123
+ */
124
+ function parseAdvertisementData(data) {
125
+ const result = {};
126
+ if (!data) {
127
+ return result;
128
+ }
129
+ let buffer;
130
+ if (typeof data === 'string') {
131
+ // Base64 解码
132
+ buffer = base64ToArrayBuffer(data);
133
+ }
134
+ else {
135
+ buffer = data;
136
+ }
137
+ const view = new DataView(buffer);
138
+ let offset = 0;
139
+ const manufacturerData = {};
140
+ const serviceUuids = [];
141
+ while (offset < view.byteLength) {
142
+ const length = view.getUint8(offset);
143
+ if (length === 0)
144
+ break;
145
+ const type = view.getUint8(offset + 1);
146
+ const dataOffset = offset + 2;
147
+ const dataLength = length - 1;
148
+ switch (type) {
149
+ case 0x01: // Flags
150
+ const flags = view.getUint8(dataOffset);
151
+ result.isConnectable = (flags & 0x02) !== 0;
152
+ break;
153
+ case 0x02: // 16-bit Service UUIDs (partial)
154
+ case 0x03: // 16-bit Service UUIDs (complete)
155
+ for (let i = 0; i < dataLength; i += 2) {
156
+ const uuid = view.getUint16(dataOffset + i, true).toString(16).padStart(4, '0');
157
+ serviceUuids.push(`0000${uuid}-0000-1000-8000-00805f9b34fb`);
158
+ }
159
+ break;
160
+ case 0x04: // 32-bit Service UUIDs (partial)
161
+ case 0x05: // 32-bit Service UUIDs (complete)
162
+ for (let i = 0; i < dataLength; i += 4) {
163
+ const uuid = view.getUint32(dataOffset + i, true).toString(16).padStart(8, '0');
164
+ serviceUuids.push(`${uuid}-0000-1000-8000-00805f9b34fb`);
165
+ }
166
+ break;
167
+ case 0x06: // 128-bit Service UUIDs (partial)
168
+ case 0x07: // 128-bit Service UUIDs (complete)
169
+ for (let i = 0; i < dataLength; i += 16) {
170
+ const uuid = parseUUID(buffer.slice(dataOffset + i, dataOffset + i + 16));
171
+ serviceUuids.push(uuid);
172
+ }
173
+ break;
174
+ case 0x08: // Shortened Local Name
175
+ case 0x09: // Complete Local Name
176
+ result.localName = decodeUTF8(buffer.slice(dataOffset, dataOffset + dataLength));
177
+ break;
178
+ case 0x0A: // TX Power Level
179
+ result.txPowerLevel = view.getInt8(dataOffset);
180
+ break;
181
+ case 0xFF: // Manufacturer Specific Data
182
+ const companyId = view.getUint16(dataOffset, true);
183
+ const companyIdHex = companyId.toString(16).padStart(4, '0');
184
+ manufacturerData[companyIdHex] = buffer.slice(dataOffset + 2, dataOffset + dataLength);
185
+ break;
186
+ }
187
+ offset += length + 1;
188
+ }
189
+ if (Object.keys(manufacturerData).length > 0) {
190
+ result.manufacturerData = manufacturerData;
191
+ }
192
+ if (serviceUuids.length > 0) {
193
+ result.serviceUuids = serviceUuids;
194
+ }
195
+ return result;
196
+ }
197
+ /**
198
+ * Base64 转 ArrayBuffer
199
+ */
200
+ function base64ToArrayBuffer(base64) {
201
+ const binary = atob(base64);
202
+ const buffer = new ArrayBuffer(binary.length);
203
+ const view = new Uint8Array(buffer);
204
+ for (let i = 0; i < binary.length; i++) {
205
+ view[i] = binary.charCodeAt(i);
206
+ }
207
+ return buffer;
208
+ }
209
+ /**
210
+ * ArrayBuffer 转 Base64
211
+ */
212
+ function arrayBufferToBase64(buffer) {
213
+ const bytes = new Uint8Array(buffer);
214
+ let binary = '';
215
+ for (let i = 0; i < bytes.byteLength; i++) {
216
+ binary += String.fromCharCode(bytes[i]);
217
+ }
218
+ return btoa(binary);
219
+ }
220
+ /**
221
+ * 解析 UUID
222
+ */
223
+ function parseUUID(buffer) {
224
+ const view = new DataView(buffer);
225
+ const parts = [
226
+ view.getUint32(0, false).toString(16).padStart(8, '0'),
227
+ view.getUint16(4, false).toString(16).padStart(4, '0'),
228
+ view.getUint16(6, false).toString(16).padStart(4, '0'),
229
+ view.getUint16(8, false).toString(16).padStart(4, '0'),
230
+ view.getUint32(10, false).toString(16).padStart(8, '0') +
231
+ view.getUint16(14, false).toString(16).padStart(4, '0'),
232
+ ];
233
+ return `${parts[0]}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;
234
+ }
235
+ /**
236
+ * UTF-8 解码
237
+ */
238
+ function decodeUTF8(buffer) {
239
+ const decoder = new TextDecoder('utf-8');
240
+ return decoder.decode(buffer);
241
+ }
242
+ /**
243
+ * 标准化设备数据
244
+ */
245
+ function normalizeDeviceData(device) {
246
+ const normalized = {
247
+ deviceId: device.deviceId || '',
248
+ name: device.name || device.localName,
249
+ RSSI: device.RSSI || device.rssi || -100,
250
+ };
251
+ // 处理广播数据
252
+ if (device.advertisData) {
253
+ normalized.advertisData = device.advertisData;
254
+ if (typeof device.advertisData === 'string') {
255
+ normalized.advertisDataBase64 = device.advertisData;
256
+ }
257
+ else {
258
+ normalized.advertisDataBase64 = arrayBufferToBase64(device.advertisData);
259
+ }
260
+ // 解析广播数据
261
+ const parsed = parseAdvertisementData(device.advertisData);
262
+ Object.assign(normalized, parsed);
263
+ }
264
+ // 处理制造商数据
265
+ if (device.manufacturerData) {
266
+ normalized.manufacturerData = device.manufacturerData;
267
+ }
268
+ // 处理服务UUID
269
+ if (device.serviceUuids) {
270
+ normalized.serviceUuids = device.serviceUuids;
271
+ }
272
+ // 平台特定数据
273
+ normalized.platformData = {
274
+ raw: device,
275
+ };
276
+ return normalized;
277
+ }
278
+ /**
279
+ * 格式化 UUID (统一格式)
280
+ */
281
+ function formatUUID(uuid) {
282
+ // 移除所有非十六进制字符
283
+ const clean = uuid.toLowerCase().replace(/[^0-9a-f]/g, '');
284
+ if (clean.length === 4) {
285
+ // 16-bit UUID
286
+ return `0000${clean}-0000-1000-8000-00805f9b34fb`;
287
+ }
288
+ else if (clean.length === 8) {
289
+ // 32-bit UUID
290
+ return `${clean}-0000-1000-8000-00805f9b34fb`;
291
+ }
292
+ else if (clean.length === 32) {
293
+ // 128-bit UUID
294
+ return `${clean.slice(0, 8)}-${clean.slice(8, 12)}-${clean.slice(12, 16)}-${clean.slice(16, 20)}-${clean.slice(20)}`;
295
+ }
296
+ return uuid;
297
+ }
298
+ /**
299
+ * 检查 UUID 是否匹配
300
+ */
301
+ function matchUUID(uuid1, uuid2) {
302
+ return formatUUID(uuid1) === formatUUID(uuid2);
303
+ }
304
+
305
+ /**
306
+ * 扫描器基类
307
+ */
308
+ class BaseScanner {
309
+ constructor() {
310
+ this.state = ScanState.IDLE;
311
+ this.discoveredDevices = new Map();
312
+ this.callbacks = {};
313
+ this.options = {};
314
+ this.platformConfig = {};
315
+ this.initialized = false;
316
+ this.timeoutId = null;
317
+ this.adapterStateListeners = [];
318
+ }
319
+ /**
320
+ * 开始扫描
321
+ */
322
+ startScan() {
323
+ return __awaiter(this, arguments, void 0, function* (options = {}, callbacks = {}) {
324
+ var _a, _b;
325
+ if (this.state === ScanState.SCANNING) {
326
+ throw Errors.alreadyScanning();
327
+ }
328
+ if (!this.initialized) {
329
+ throw Errors.notInitialized();
330
+ }
331
+ // 检查权限
332
+ const hasPermission = yield this.checkPermissions();
333
+ if (!hasPermission) {
334
+ const granted = yield this.requestPermissions();
335
+ if (!granted) {
336
+ throw Errors.permissionDenied();
337
+ }
338
+ }
339
+ this.options = Object.assign(Object.assign({}, this.getDefaultOptions()), options);
340
+ this.callbacks = callbacks;
341
+ try {
342
+ this.setState(ScanState.SCANNING);
343
+ // 清空之前的设备列表(如果不需要重复上报)
344
+ if (!this.options.allowDuplicates) {
345
+ this.clearDevices();
346
+ }
347
+ // 设置超时
348
+ if (this.options.timeout && this.options.timeout > 0) {
349
+ this.timeoutId = window.setTimeout(() => {
350
+ this.handleTimeout();
351
+ }, this.options.timeout);
352
+ }
353
+ yield this.startScanInternal();
354
+ (_b = (_a = this.callbacks).onScanStart) === null || _b === void 0 ? void 0 : _b.call(_a);
355
+ }
356
+ catch (error) {
357
+ this.setState(ScanState.ERROR);
358
+ this.clearTimeout();
359
+ throw error;
360
+ }
361
+ });
362
+ }
363
+ /**
364
+ * 停止扫描
365
+ */
366
+ stopScan() {
367
+ return __awaiter(this, void 0, void 0, function* () {
368
+ var _a, _b;
369
+ if (this.state !== ScanState.SCANNING) {
370
+ return;
371
+ }
372
+ this.setState(ScanState.STOPPING);
373
+ this.clearTimeout();
374
+ try {
375
+ yield this.stopScanInternal();
376
+ this.setState(ScanState.IDLE);
377
+ (_b = (_a = this.callbacks).onScanStop) === null || _b === void 0 ? void 0 : _b.call(_a);
378
+ }
379
+ catch (error) {
380
+ this.setState(ScanState.ERROR);
381
+ throw error;
382
+ }
383
+ });
384
+ }
385
+ /**
386
+ * 获取已发现的设备列表
387
+ */
388
+ getDiscoveredDevices() {
389
+ return Array.from(this.discoveredDevices.values());
390
+ }
391
+ /**
392
+ * 清空设备列表
393
+ */
394
+ clearDevices() {
395
+ this.discoveredDevices.clear();
396
+ }
397
+ /**
398
+ * 获取当前扫描状态
399
+ */
400
+ getState() {
401
+ return this.state;
402
+ }
403
+ /**
404
+ * 销毁扫描器
405
+ */
406
+ destroy() {
407
+ this.stopScan();
408
+ this.clearDevices();
409
+ this.clearTimeout();
410
+ this.callbacks = {};
411
+ this.adapterStateListeners = [];
412
+ this.initialized = false;
413
+ }
414
+ /**
415
+ * 处理发现的设备
416
+ */
417
+ handleDeviceFound(rawDevice) {
418
+ var _a, _b;
419
+ try {
420
+ const device = normalizeDeviceData(rawDevice);
421
+ // 应用过滤器
422
+ if (!this.filterDevice(device)) {
423
+ return;
424
+ }
425
+ // 检查是否已存在
426
+ const existingDevice = this.discoveredDevices.get(device.deviceId);
427
+ if (existingDevice && !this.options.allowDuplicates) {
428
+ // 更新 RSSI
429
+ existingDevice.RSSI = device.RSSI;
430
+ return;
431
+ }
432
+ // 保存设备
433
+ this.discoveredDevices.set(device.deviceId, device);
434
+ // 触发回调
435
+ (_b = (_a = this.callbacks).onDeviceFound) === null || _b === void 0 ? void 0 : _b.call(_a, device);
436
+ }
437
+ catch (error) {
438
+ console.error('处理设备数据失败:', error);
439
+ }
440
+ }
441
+ /**
442
+ * 过滤设备
443
+ */
444
+ filterDevice(device) {
445
+ // 按名称过滤
446
+ if (this.options.nameFilter) {
447
+ const deviceName = device.name || '';
448
+ const filters = Array.isArray(this.options.nameFilter)
449
+ ? this.options.nameFilter
450
+ : [this.options.nameFilter];
451
+ const nameMatch = filters.some(filter => {
452
+ if (typeof filter === 'string') {
453
+ return deviceName.includes(filter);
454
+ }
455
+ return filter.test(deviceName);
456
+ });
457
+ if (!nameMatch)
458
+ return false;
459
+ }
460
+ // 按信号强度过滤
461
+ if (this.options.rssiThreshold !== undefined) {
462
+ if (device.RSSI < this.options.rssiThreshold) {
463
+ return false;
464
+ }
465
+ }
466
+ // 按服务UUID过滤
467
+ if (this.options.services && this.options.services.length > 0) {
468
+ if (!device.serviceUuids)
469
+ return false;
470
+ const hasMatchingService = this.options.services.some(service => device.serviceUuids.some(uuid => uuid.toLowerCase().includes(service.toLowerCase())));
471
+ if (!hasMatchingService)
472
+ return false;
473
+ }
474
+ // 按制造商数据过滤
475
+ if (this.options.manufacturerFilter !== undefined) {
476
+ if (!device.manufacturerData)
477
+ return false;
478
+ const filters = Array.isArray(this.options.manufacturerFilter)
479
+ ? this.options.manufacturerFilter
480
+ : [this.options.manufacturerFilter];
481
+ const manufacturerIds = Object.keys(device.manufacturerData).map(id => parseInt(id, 16));
482
+ const hasMatchingManufacturer = filters.some(filter => manufacturerIds.includes(filter));
483
+ if (!hasMatchingManufacturer)
484
+ return false;
485
+ }
486
+ return true;
487
+ }
488
+ /**
489
+ * 处理超时
490
+ */
491
+ handleTimeout() {
492
+ var _a, _b;
493
+ this.stopScan();
494
+ (_b = (_a = this.callbacks).onTimeout) === null || _b === void 0 ? void 0 : _b.call(_a);
495
+ }
496
+ /**
497
+ * 处理错误
498
+ */
499
+ handleError(error) {
500
+ var _a, _b;
501
+ const bleError = handlePlatformError(error, this.getPlatform());
502
+ this.setState(ScanState.ERROR);
503
+ (_b = (_a = this.callbacks).onError) === null || _b === void 0 ? void 0 : _b.call(_a, bleError);
504
+ }
505
+ /**
506
+ * 设置状态
507
+ */
508
+ setState(state) {
509
+ this.state = state;
510
+ }
511
+ /**
512
+ * 清除超时定时器
513
+ */
514
+ clearTimeout() {
515
+ if (this.timeoutId !== null) {
516
+ window.clearTimeout(this.timeoutId);
517
+ this.timeoutId = null;
518
+ }
519
+ }
520
+ /**
521
+ * 获取默认选项
522
+ */
523
+ getDefaultOptions() {
524
+ return {
525
+ timeout: 10000,
526
+ allowDuplicates: false,
527
+ };
528
+ }
529
+ /**
530
+ * 触发适配器状态变化
531
+ */
532
+ emitAdapterStateChange(state) {
533
+ this.adapterStateListeners.forEach(listener => listener(state));
534
+ }
535
+ }
536
+
537
+ /**
538
+ * 获取当前运行平台
539
+ */
540
+ function getPlatform() {
541
+ var _a;
542
+ // #ifdef APP-PLUS
543
+ const systemInfo = uni.getSystemInfoSync();
544
+ const platform = ((_a = systemInfo.platform) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
545
+ if (platform === 'android') {
546
+ return Platform.ANDROID;
547
+ }
548
+ else if (platform === 'ios') {
549
+ return Platform.IOS;
550
+ }
551
+ else if (platform === 'harmony' || platform === 'harmonyos') {
552
+ return Platform.HARMONY;
553
+ }
554
+ // #endif
555
+ // #ifdef HARMONY
556
+ return Platform.HARMONY;
557
+ }
558
+ /**
559
+ * 检查是否在指定平台运行
560
+ */
561
+ function isPlatform(platform) {
562
+ return getPlatform() === platform;
563
+ }
564
+ /**
565
+ * 检查是否是移动端APP环境
566
+ */
567
+ function isApp() {
568
+ // #ifdef APP-PLUS
569
+ return true;
570
+ }
571
+ /**
572
+ * 获取平台名称
573
+ */
574
+ function getPlatformName() {
575
+ const platform = getPlatform();
576
+ switch (platform) {
577
+ case Platform.ANDROID:
578
+ return 'Android';
579
+ case Platform.IOS:
580
+ return 'iOS';
581
+ case Platform.HARMONY:
582
+ return 'HarmonyOS';
583
+ default:
584
+ return 'Unknown';
585
+ }
586
+ }
587
+
588
+ /**
589
+ * 安卓权限列表
590
+ */
591
+ const ANDROID_PERMISSIONS = {
592
+ // Android 12+ 需要的新权限
593
+ BLUETOOTH_SCAN: 'android.permission.BLUETOOTH_SCAN',
594
+ BLUETOOTH_CONNECT: 'android.permission.BLUETOOTH_CONNECT',
595
+ // Android 11 及以下需要的权限
596
+ BLUETOOTH: 'android.permission.BLUETOOTH',
597
+ BLUETOOTH_ADMIN: 'android.permission.BLUETOOTH_ADMIN',
598
+ // 位置权限(扫描需要)
599
+ ACCESS_FINE_LOCATION: 'android.permission.ACCESS_FINE_LOCATION'};
600
+ /**
601
+ * iOS 不需要显式请求蓝牙权限,由系统自动处理
602
+ * 但需要在 Info.plist 中配置 NSBluetoothAlwaysUsageDescription
603
+ */
604
+ /**
605
+ * 鸿蒙权限列表
606
+ */
607
+ const HARMONY_PERMISSIONS = {
608
+ ACCESS_BLUETOOTH: 'ohos.permission.ACCESS_BLUETOOTH',
609
+ LOCATION: 'ohos.permission.LOCATION',
610
+ APPROXIMATELY_LOCATION: 'ohos.permission.APPROXIMATELY_LOCATION',
611
+ };
612
+ /**
613
+ * 检查权限
614
+ */
615
+ function checkPermissions() {
616
+ return __awaiter(this, void 0, void 0, function* () {
617
+ const platform = getPlatform();
618
+ switch (platform) {
619
+ case Platform.ANDROID:
620
+ return checkAndroidPermissions();
621
+ case Platform.IOS:
622
+ return checkIOSPermissions();
623
+ case Platform.HARMONY:
624
+ return checkHarmonyPermissions();
625
+ default:
626
+ return false;
627
+ }
628
+ });
629
+ }
630
+ /**
631
+ * 请求权限
632
+ */
633
+ function requestPermissions() {
634
+ return __awaiter(this, void 0, void 0, function* () {
635
+ const platform = getPlatform();
636
+ switch (platform) {
637
+ case Platform.ANDROID:
638
+ return requestAndroidPermissions();
639
+ case Platform.IOS:
640
+ return requestIOSPermissions();
641
+ case Platform.HARMONY:
642
+ return requestHarmonyPermissions();
643
+ default:
644
+ return false;
645
+ }
646
+ });
647
+ }
648
+ /**
649
+ * 检查安卓权限
650
+ */
651
+ function checkAndroidPermissions() {
652
+ return __awaiter(this, void 0, void 0, function* () {
653
+ return new Promise((resolve) => {
654
+ var _a;
655
+ // #ifdef APP-PLUS
656
+ const mainActivity = plus.android.runtimeMainActivity();
657
+ const PackageManager = plus.android.importClass('android.content.pm.PackageManager');
658
+ // 获取系统版本
659
+ const systemInfo = uni.getSystemInfoSync();
660
+ const osVersion = parseInt(((_a = systemInfo.system) === null || _a === void 0 ? void 0 : _a.split('.')[0]) || '0');
661
+ let permissions;
662
+ if (osVersion >= 12) {
663
+ // Android 12+ 使用新权限
664
+ permissions = [
665
+ ANDROID_PERMISSIONS.BLUETOOTH_SCAN,
666
+ ANDROID_PERMISSIONS.BLUETOOTH_CONNECT,
667
+ ANDROID_PERMISSIONS.ACCESS_FINE_LOCATION,
668
+ ];
669
+ }
670
+ else {
671
+ // Android 11 及以下使用旧权限
672
+ permissions = [
673
+ ANDROID_PERMISSIONS.BLUETOOTH,
674
+ ANDROID_PERMISSIONS.BLUETOOTH_ADMIN,
675
+ ANDROID_PERMISSIONS.ACCESS_FINE_LOCATION,
676
+ ];
677
+ }
678
+ for (const permission of permissions) {
679
+ const result = mainActivity.checkSelfPermission(permission);
680
+ if (result !== PackageManager.PERMISSION_GRANTED) {
681
+ resolve(false);
682
+ return;
683
+ }
684
+ }
685
+ resolve(true);
686
+ // #endif
687
+ // #ifndef APP-PLUS
688
+ resolve(true);
689
+ // #endif
690
+ });
691
+ });
692
+ }
693
+ /**
694
+ * 请求安卓权限
695
+ */
696
+ function requestAndroidPermissions() {
697
+ return __awaiter(this, void 0, void 0, function* () {
698
+ return new Promise((resolve) => {
699
+ var _a;
700
+ // #ifdef APP-PLUS
701
+ plus.android.runtimeMainActivity();
702
+ const systemInfo = uni.getSystemInfoSync();
703
+ const osVersion = parseInt(((_a = systemInfo.system) === null || _a === void 0 ? void 0 : _a.split('.')[0]) || '0');
704
+ let permissions;
705
+ if (osVersion >= 12) {
706
+ permissions = [
707
+ ANDROID_PERMISSIONS.BLUETOOTH_SCAN,
708
+ ANDROID_PERMISSIONS.BLUETOOTH_CONNECT,
709
+ ANDROID_PERMISSIONS.ACCESS_FINE_LOCATION,
710
+ ];
711
+ }
712
+ else {
713
+ permissions = [
714
+ ANDROID_PERMISSIONS.BLUETOOTH,
715
+ ANDROID_PERMISSIONS.BLUETOOTH_ADMIN,
716
+ ANDROID_PERMISSIONS.ACCESS_FINE_LOCATION,
717
+ ];
718
+ }
719
+ // 使用 uniapp 的权限请求API
720
+ uni.requestAndroidPermission(permissions[0], (result) => {
721
+ if (result.granted) {
722
+ resolve(true);
723
+ }
724
+ else {
725
+ resolve(false);
726
+ }
727
+ }, (error) => {
728
+ console.error('请求权限失败:', error);
729
+ resolve(false);
730
+ });
731
+ // #endif
732
+ // #ifndef APP-PLUS
733
+ resolve(true);
734
+ // #endif
735
+ });
736
+ });
737
+ }
738
+ /**
739
+ * 检查 iOS 权限
740
+ */
741
+ function checkIOSPermissions() {
742
+ return __awaiter(this, void 0, void 0, function* () {
743
+ // iOS 权限由系统自动管理
744
+ // 需要在 manifest.json 或原生工程中配置 Info.plist
745
+ return true;
746
+ });
747
+ }
748
+ /**
749
+ * 请求 iOS 权限
750
+ */
751
+ function requestIOSPermissions() {
752
+ return __awaiter(this, void 0, void 0, function* () {
753
+ // iOS 权限由系统自动管理
754
+ return true;
755
+ });
756
+ }
757
+ /**
758
+ * 检查鸿蒙权限
759
+ */
760
+ function checkHarmonyPermissions() {
761
+ return __awaiter(this, void 0, void 0, function* () {
762
+ // #ifdef HARMONY
763
+ // 鸿蒙权限检查逻辑
764
+ try {
765
+ const atManager = require('@ohos.abilityAccessCtrl').createAtManager();
766
+ const tokenId = require('@ohos.process').process.accessTokenId;
767
+ const permissions = [
768
+ HARMONY_PERMISSIONS.ACCESS_BLUETOOTH,
769
+ HARMONY_PERMISSIONS.LOCATION,
770
+ ];
771
+ for (const permission of permissions) {
772
+ const result = yield atManager.verifyAccessToken(tokenId, permission);
773
+ if (result !== 0) { // PERMISSION_GRANTED = 0
774
+ return false;
775
+ }
776
+ }
777
+ return true;
778
+ }
779
+ catch (error) {
780
+ console.error('检查鸿蒙权限失败:', error);
781
+ return false;
782
+ }
783
+ // #endif
784
+ // #ifndef HARMONY
785
+ return true;
786
+ // #endif
787
+ });
788
+ }
789
+ /**
790
+ * 请求鸿蒙权限
791
+ */
792
+ function requestHarmonyPermissions() {
793
+ return __awaiter(this, void 0, void 0, function* () {
794
+ // #ifdef HARMONY
795
+ try {
796
+ const atManager = require('@ohos.abilityAccessCtrl').createAtManager();
797
+ const context = getContext(this);
798
+ const permissions = [
799
+ HARMONY_PERMISSIONS.ACCESS_BLUETOOTH,
800
+ HARMONY_PERMISSIONS.LOCATION,
801
+ ];
802
+ const result = yield atManager.requestPermissionsFromUser(context, permissions);
803
+ return result.authResults.every((authResult) => authResult === 0);
804
+ }
805
+ catch (error) {
806
+ console.error('请求鸿蒙权限失败:', error);
807
+ return false;
808
+ }
809
+ // #endif
810
+ // #ifndef HARMONY
811
+ return true;
812
+ // #endif
813
+ });
814
+ }
815
+
816
+ /**
817
+ * UniApp 蓝牙扫描器
818
+ * 支持安卓、iOS 和鸿蒙平台
819
+ */
820
+ class UniAppScanner extends BaseScanner {
821
+ constructor() {
822
+ super(...arguments);
823
+ this.adapterStateChangeHandler = null;
824
+ }
825
+ getPlatform() {
826
+ var _a;
827
+ const systemInfo = uni.getSystemInfoSync();
828
+ const platform = ((_a = systemInfo.platform) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
829
+ if (platform === 'android') {
830
+ return Platform.ANDROID;
831
+ }
832
+ else if (platform === 'ios') {
833
+ return Platform.IOS;
834
+ }
835
+ else if (platform === 'harmony' || platform === 'harmonyos') {
836
+ return Platform.HARMONY;
837
+ }
838
+ return Platform.UNKNOWN;
839
+ }
840
+ init() {
841
+ return __awaiter(this, void 0, void 0, function* () {
842
+ return new Promise((resolve, reject) => {
843
+ // 打开蓝牙适配器
844
+ uni.openBluetoothAdapter({
845
+ success: () => {
846
+ this.initialized = true;
847
+ resolve();
848
+ },
849
+ fail: (err) => {
850
+ reject(createError(ErrorCodes.ADAPTER_NOT_AVAILABLE, `初始化蓝牙适配器失败: ${err.errMsg}`, err.errCode, err));
851
+ },
852
+ });
853
+ });
854
+ });
855
+ }
856
+ startScanInternal() {
857
+ return __awaiter(this, void 0, void 0, function* () {
858
+ return new Promise((resolve, reject) => {
859
+ const scanOptions = {
860
+ allowDuplicatesKey: this.options.allowDuplicates || false,
861
+ success: () => {
862
+ // 监听设备发现
863
+ this.startDeviceDiscovery();
864
+ resolve();
865
+ },
866
+ fail: (err) => {
867
+ reject(Errors.scanFailed(err.errMsg));
868
+ },
869
+ };
870
+ // 添加服务UUID过滤
871
+ if (this.options.services && this.options.services.length > 0) {
872
+ scanOptions.services = this.options.services;
873
+ }
874
+ uni.startBluetoothDevicesDiscovery(scanOptions);
875
+ });
876
+ });
877
+ }
878
+ stopScanInternal() {
879
+ return __awaiter(this, void 0, void 0, function* () {
880
+ return new Promise((resolve, reject) => {
881
+ uni.stopBluetoothDevicesDiscovery({
882
+ success: () => {
883
+ this.stopDeviceDiscovery();
884
+ resolve();
885
+ },
886
+ fail: (err) => {
887
+ reject(Errors.scanFailed(err.errMsg));
888
+ },
889
+ });
890
+ });
891
+ });
892
+ }
893
+ checkPermissions() {
894
+ return __awaiter(this, void 0, void 0, function* () {
895
+ return checkPermissions();
896
+ });
897
+ }
898
+ requestPermissions() {
899
+ return __awaiter(this, void 0, void 0, function* () {
900
+ return requestPermissions();
901
+ });
902
+ }
903
+ onAdapterStateChange(callback) {
904
+ this.adapterStateListeners.push(callback);
905
+ // 如果还没有监听,开始监听
906
+ if (!this.adapterStateChangeHandler) {
907
+ this.adapterStateChangeHandler = (res) => {
908
+ this.emitAdapterStateChange({
909
+ available: res.available,
910
+ discovering: res.discovering,
911
+ });
912
+ };
913
+ uni.onBluetoothAdapterStateChange(this.adapterStateChangeHandler);
914
+ }
915
+ }
916
+ destroy() {
917
+ super.destroy();
918
+ // 移除适配器状态监听
919
+ if (this.adapterStateChangeHandler) {
920
+ uni.offBluetoothAdapterStateChange(this.adapterStateChangeHandler);
921
+ this.adapterStateChangeHandler = null;
922
+ }
923
+ // 关闭蓝牙适配器
924
+ uni.closeBluetoothAdapter({
925
+ success: () => {
926
+ console.log('蓝牙适配器已关闭');
927
+ },
928
+ fail: (err) => {
929
+ console.error('关闭蓝牙适配器失败:', err);
930
+ },
931
+ });
932
+ }
933
+ /**
934
+ * 开始监听设备发现
935
+ */
936
+ startDeviceDiscovery() {
937
+ uni.onBluetoothDeviceFound((res) => {
938
+ const devices = res.devices || [];
939
+ devices.forEach((device) => {
940
+ this.handleDeviceFound(device);
941
+ });
942
+ });
943
+ }
944
+ /**
945
+ * 停止监听设备发现
946
+ */
947
+ stopDeviceDiscovery() {
948
+ uni.offBluetoothDeviceFound();
949
+ }
950
+ /**
951
+ * 获取蓝牙适配器状态
952
+ */
953
+ getAdapterState() {
954
+ return __awaiter(this, void 0, void 0, function* () {
955
+ return new Promise((resolve, reject) => {
956
+ uni.getBluetoothAdapterState({
957
+ success: (res) => {
958
+ resolve({
959
+ available: res.available,
960
+ discovering: res.discovering,
961
+ });
962
+ },
963
+ fail: (err) => {
964
+ reject(Errors.adapterNotAvailable());
965
+ },
966
+ });
967
+ });
968
+ });
969
+ }
970
+ }
971
+
972
+ /**
973
+ * Android 原生蓝牙扫描器
974
+ * 使用 uni-app 的 Native.js 调用 Android 原生 API
975
+ */
976
+ class AndroidNativeScanner extends BaseScanner {
977
+ constructor() {
978
+ super(...arguments);
979
+ this.bluetoothAdapter = null;
980
+ this.bluetoothLeScanner = null;
981
+ this.scanCallback = null;
982
+ this.adapterStateReceiver = null;
983
+ this.legacyScanCallback = null;
984
+ }
985
+ getPlatform() {
986
+ return Platform.ANDROID;
987
+ }
988
+ init() {
989
+ return __awaiter(this, void 0, void 0, function* () {
990
+ return new Promise((resolve, reject) => {
991
+ try {
992
+ // #ifdef APP-PLUS
993
+ const mainActivity = plus.android.runtimeMainActivity();
994
+ const BluetoothManager = plus.android.importClass('android.bluetooth.BluetoothManager');
995
+ const bluetoothManager = mainActivity.getSystemService('bluetooth');
996
+ this.bluetoothAdapter = bluetoothManager.getAdapter();
997
+ if (!this.bluetoothAdapter) {
998
+ reject(Errors.adapterNotAvailable());
999
+ return;
1000
+ }
1001
+ if (!this.bluetoothAdapter.isEnabled()) {
1002
+ reject(Errors.bluetoothDisabled());
1003
+ return;
1004
+ }
1005
+ this.initialized = true;
1006
+ resolve();
1007
+ // #endif
1008
+ // #ifndef APP-PLUS
1009
+ reject(createError(ErrorCodes.UNSUPPORTED_PLATFORM, '非 APP 环境不支持原生 Android 扫描'));
1010
+ // #endif
1011
+ }
1012
+ catch (error) {
1013
+ reject(handlePlatformError(error, 'android'));
1014
+ }
1015
+ });
1016
+ });
1017
+ }
1018
+ startScanInternal() {
1019
+ return __awaiter(this, void 0, void 0, function* () {
1020
+ return new Promise((resolve, reject) => {
1021
+ var _a;
1022
+ try {
1023
+ // #ifdef APP-PLUS
1024
+ const systemInfo = uni.getSystemInfoSync();
1025
+ const osVersion = parseInt(((_a = systemInfo.system) === null || _a === void 0 ? void 0 : _a.split('.')[0]) || '0');
1026
+ if (osVersion >= 21 && this.bluetoothAdapter.getBluetoothLeScanner) {
1027
+ // Android 5.0+ 使用新的扫描 API
1028
+ this.startNewScanAPI(resolve, reject);
1029
+ }
1030
+ else {
1031
+ // 旧版扫描 API
1032
+ this.startLegacyScan(resolve, reject);
1033
+ }
1034
+ // #endif
1035
+ // #ifndef APP-PLUS
1036
+ reject(Errors.unsupportedPlatform());
1037
+ // #endif
1038
+ }
1039
+ catch (error) {
1040
+ reject(handlePlatformError(error, 'android'));
1041
+ }
1042
+ });
1043
+ });
1044
+ }
1045
+ stopScanInternal() {
1046
+ return __awaiter(this, void 0, void 0, function* () {
1047
+ return new Promise((resolve, reject) => {
1048
+ try {
1049
+ // #ifdef APP-PLUS
1050
+ if (this.bluetoothLeScanner && this.scanCallback) {
1051
+ this.bluetoothLeScanner.stopScan(this.scanCallback);
1052
+ }
1053
+ else if (this.bluetoothAdapter) {
1054
+ this.bluetoothAdapter.stopLeScan(this.legacyScanCallback);
1055
+ }
1056
+ resolve();
1057
+ // #endif
1058
+ // #ifndef APP-PLUS
1059
+ reject(Errors.unsupportedPlatform());
1060
+ // #endif
1061
+ }
1062
+ catch (error) {
1063
+ reject(handlePlatformError(error, 'android'));
1064
+ }
1065
+ });
1066
+ });
1067
+ }
1068
+ checkPermissions() {
1069
+ return __awaiter(this, void 0, void 0, function* () {
1070
+ var _a;
1071
+ // #ifdef APP-PLUS
1072
+ const mainActivity = plus.android.runtimeMainActivity();
1073
+ const PackageManager = plus.android.importClass('android.content.pm.PackageManager');
1074
+ const systemInfo = uni.getSystemInfoSync();
1075
+ const osVersion = parseInt(((_a = systemInfo.system) === null || _a === void 0 ? void 0 : _a.split('.')[0]) || '0');
1076
+ let permissions;
1077
+ if (osVersion >= 12) {
1078
+ permissions = [
1079
+ 'android.permission.BLUETOOTH_SCAN',
1080
+ 'android.permission.BLUETOOTH_CONNECT',
1081
+ 'android.permission.ACCESS_FINE_LOCATION',
1082
+ ];
1083
+ }
1084
+ else {
1085
+ permissions = [
1086
+ 'android.permission.BLUETOOTH',
1087
+ 'android.permission.BLUETOOTH_ADMIN',
1088
+ 'android.permission.ACCESS_FINE_LOCATION',
1089
+ ];
1090
+ }
1091
+ for (const permission of permissions) {
1092
+ const result = mainActivity.checkSelfPermission(permission);
1093
+ if (result !== PackageManager.PERMISSION_GRANTED) {
1094
+ return false;
1095
+ }
1096
+ }
1097
+ return true;
1098
+ // #endif
1099
+ });
1100
+ }
1101
+ requestPermissions() {
1102
+ return __awaiter(this, void 0, void 0, function* () {
1103
+ // #ifdef APP-PLUS
1104
+ return new Promise((resolve) => {
1105
+ var _a;
1106
+ const systemInfo = uni.getSystemInfoSync();
1107
+ const osVersion = parseInt(((_a = systemInfo.system) === null || _a === void 0 ? void 0 : _a.split('.')[0]) || '0');
1108
+ let permissions;
1109
+ if (osVersion >= 12) {
1110
+ permissions = [
1111
+ 'android.permission.BLUETOOTH_SCAN',
1112
+ 'android.permission.BLUETOOTH_CONNECT',
1113
+ 'android.permission.ACCESS_FINE_LOCATION',
1114
+ ];
1115
+ }
1116
+ else {
1117
+ permissions = [
1118
+ 'android.permission.BLUETOOTH',
1119
+ 'android.permission.BLUETOOTH_ADMIN',
1120
+ 'android.permission.ACCESS_FINE_LOCATION',
1121
+ ];
1122
+ }
1123
+ uni.requestAndroidPermission(permissions[0], (result) => {
1124
+ resolve(result.granted);
1125
+ }, () => {
1126
+ resolve(false);
1127
+ });
1128
+ });
1129
+ // #endif
1130
+ });
1131
+ }
1132
+ onAdapterStateChange(callback) {
1133
+ this.adapterStateListeners.push(callback);
1134
+ // #ifdef APP-PLUS
1135
+ if (!this.adapterStateReceiver) {
1136
+ plus.android.runtimeMainActivity();
1137
+ const IntentFilter = plus.android.importClass('android.content.IntentFilter');
1138
+ const BluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter');
1139
+ const filter = new IntentFilter();
1140
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
1141
+ // 这里简化处理,实际应该创建 BroadcastReceiver
1142
+ // 由于 Native.js 限制,建议使用 uni-app 的标准 API 监听状态
1143
+ }
1144
+ // #endif
1145
+ }
1146
+ destroy() {
1147
+ super.destroy();
1148
+ this.bluetoothAdapter = null;
1149
+ this.bluetoothLeScanner = null;
1150
+ this.scanCallback = null;
1151
+ }
1152
+ /**
1153
+ * 使用新版扫描 API (Android 5.0+)
1154
+ */
1155
+ startNewScanAPI(resolve, reject) {
1156
+ var _a;
1157
+ // #ifdef APP-PLUS
1158
+ try {
1159
+ const ScanSettings = plus.android.importClass('android.bluetooth.le.ScanSettings');
1160
+ const ScanFilter = plus.android.importClass('android.bluetooth.le.ScanFilter');
1161
+ const ScanCallback = plus.android.importClass('android.bluetooth.le.ScanCallback');
1162
+ this.bluetoothLeScanner = this.bluetoothAdapter.getBluetoothLeScanner();
1163
+ // 构建扫描设置
1164
+ const builder = new ScanSettings.Builder();
1165
+ // 设置扫描模式
1166
+ const scanMode = ((_a = this.platformConfig.android) === null || _a === void 0 ? void 0 : _a.scanMode) || ScanSettings.SCAN_MODE_LOW_LATENCY;
1167
+ builder.setScanMode(scanMode);
1168
+ const settings = builder.build();
1169
+ // 构建扫描过滤器
1170
+ const filters = [];
1171
+ if (this.options.services && this.options.services.length > 0) {
1172
+ // 可以添加服务 UUID 过滤
1173
+ }
1174
+ // 创建扫描回调
1175
+ const self = this;
1176
+ this.scanCallback = new ScanCallback({
1177
+ onScanResult: (callbackType, result) => {
1178
+ const device = result.getDevice();
1179
+ const rssi = result.getRssi();
1180
+ const scanRecord = result.getScanRecord();
1181
+ self.handleDeviceFound({
1182
+ deviceId: device.getAddress(),
1183
+ name: device.getName(),
1184
+ RSSI: rssi,
1185
+ advertisData: scanRecord ? scanRecord.getBytes() : null,
1186
+ });
1187
+ },
1188
+ onBatchScanResults: (results) => {
1189
+ results.forEach((result) => {
1190
+ this.scanCallback.onScanResult(0, result);
1191
+ });
1192
+ },
1193
+ onScanFailed: (errorCode) => {
1194
+ reject(Errors.scanFailed(`错误码: ${errorCode}`));
1195
+ },
1196
+ });
1197
+ this.bluetoothLeScanner.startScan(filters, settings, this.scanCallback);
1198
+ resolve();
1199
+ }
1200
+ catch (error) {
1201
+ reject(handlePlatformError(error, 'android'));
1202
+ }
1203
+ // #endif
1204
+ }
1205
+ /**
1206
+ * 使用旧版扫描 API
1207
+ */
1208
+ startLegacyScan(resolve, reject) {
1209
+ // #ifdef APP-PLUS
1210
+ try {
1211
+ const self = this;
1212
+ this.legacyScanCallback = new plus.android.implements('android.bluetooth.BluetoothAdapter.LeScanCallback', {
1213
+ onLeScan: (device, rssi, scanRecord) => {
1214
+ self.handleDeviceFound({
1215
+ deviceId: device.getAddress(),
1216
+ name: device.getName(),
1217
+ RSSI: rssi,
1218
+ advertisData: scanRecord,
1219
+ });
1220
+ },
1221
+ });
1222
+ let serviceUuids = null;
1223
+ if (this.options.services && this.options.services.length > 0) {
1224
+ serviceUuids = this.options.services;
1225
+ }
1226
+ const started = this.bluetoothAdapter.startLeScan(serviceUuids, this.legacyScanCallback);
1227
+ if (started) {
1228
+ resolve();
1229
+ }
1230
+ else {
1231
+ reject(Errors.scanFailed('启动扫描失败'));
1232
+ }
1233
+ }
1234
+ catch (error) {
1235
+ reject(handlePlatformError(error, 'android'));
1236
+ }
1237
+ // #endif
1238
+ }
1239
+ }
1240
+
1241
+ /**
1242
+ * iOS 原生蓝牙扫描器
1243
+ * 使用 uni-app 的 Native.js 调用 iOS CoreBluetooth API
1244
+ */
1245
+ class IOSNativeScanner extends BaseScanner {
1246
+ constructor() {
1247
+ super(...arguments);
1248
+ this.centralManager = null;
1249
+ this.delegate = null;
1250
+ }
1251
+ getPlatform() {
1252
+ return Platform.IOS;
1253
+ }
1254
+ init() {
1255
+ return __awaiter(this, void 0, void 0, function* () {
1256
+ return new Promise((resolve, reject) => {
1257
+ var _a;
1258
+ try {
1259
+ // #ifdef APP-PLUS
1260
+ // 导入 iOS CoreBluetooth 框架
1261
+ const CBCentralManager = plus.ios.importClass('CBCentralManager');
1262
+ const CBUUID = plus.ios.importClass('CBUUID');
1263
+ const dispatch_queue_attr_make_with_qos_class = plus.ios.importClass('dispatch_queue_attr_make_with_qos_class');
1264
+ const dispatch_queue_create = plus.ios.importClass('dispatch_queue_create');
1265
+ // 创建队列
1266
+ const queue = dispatch_queue_create('com.ble.scanner', null);
1267
+ // 创建中央管理器
1268
+ this.centralManager = new CBCentralManager({
1269
+ queue: queue,
1270
+ options: {
1271
+ 'CBCentralManagerOptionShowPowerAlertKey': true,
1272
+ 'CBCentralManagerOptionRestoreIdentifierKey': (_a = this.platformConfig.ios) === null || _a === void 0 ? void 0 : _a.restoreIdentifier,
1273
+ },
1274
+ });
1275
+ if (!this.centralManager) {
1276
+ reject(Errors.adapterNotAvailable());
1277
+ return;
1278
+ }
1279
+ // 设置委托
1280
+ const self = this;
1281
+ this.delegate = plus.ios.implements('CBCentralManagerDelegate', {
1282
+ centralManagerDidUpdateState: (central) => {
1283
+ const state = central.state();
1284
+ // CBManagerStatePoweredOn = 5
1285
+ if (state === 5) {
1286
+ self.initialized = true;
1287
+ resolve();
1288
+ }
1289
+ else if (state === 4) { // CBManagerStatePoweredOff
1290
+ reject(Errors.bluetoothDisabled());
1291
+ }
1292
+ else {
1293
+ reject(createError(ErrorCodes.ADAPTER_NOT_AVAILABLE, `蓝牙适配器状态异常: ${state}`));
1294
+ }
1295
+ },
1296
+ centralManagerDidDiscoverPeripheralAdvertisementDataRSSI: (central, peripheral, advertisementData, RSSI) => {
1297
+ self.handlePeripheral(peripheral, advertisementData, RSSI);
1298
+ },
1299
+ });
1300
+ this.centralManager.setDelegate(this.delegate);
1301
+ // 检查当前状态
1302
+ const currentState = this.centralManager.state();
1303
+ if (currentState === 5) {
1304
+ this.initialized = true;
1305
+ resolve();
1306
+ }
1307
+ // 否则等待委托回调
1308
+ // #endif
1309
+ // #ifndef APP-PLUS
1310
+ reject(createError(ErrorCodes.UNSUPPORTED_PLATFORM, '非 APP 环境不支持原生 iOS 扫描'));
1311
+ // #endif
1312
+ }
1313
+ catch (error) {
1314
+ reject(handlePlatformError(error, 'ios'));
1315
+ }
1316
+ });
1317
+ });
1318
+ }
1319
+ startScanInternal() {
1320
+ return __awaiter(this, void 0, void 0, function* () {
1321
+ return new Promise((resolve, reject) => {
1322
+ try {
1323
+ // #ifdef APP-PLUS
1324
+ const CBUUID = plus.ios.importClass('CBUUID');
1325
+ const NSArray = plus.ios.importClass('NSArray');
1326
+ const NSMutableDictionary = plus.ios.importClass('NSMutableDictionary');
1327
+ // 构建服务 UUID 列表
1328
+ let serviceUUIDs = null;
1329
+ if (this.options.services && this.options.services.length > 0) {
1330
+ const uuids = [];
1331
+ this.options.services.forEach((uuid) => {
1332
+ const cbUuid = CBUUID.UUIDWithString(uuid);
1333
+ uuids.push(cbUuid);
1334
+ });
1335
+ serviceUUIDs = NSArray.arrayWithArray(uuids);
1336
+ }
1337
+ // 构建扫描选项
1338
+ const options = NSMutableDictionary.dictionary();
1339
+ options.setObjectForKey(this.options.allowDuplicates ? 1 : 0, 'CBCentralManagerScanOptionAllowDuplicatesKey');
1340
+ // 开始扫描
1341
+ this.centralManager.scanForPeripheralsWithServicesOptions(serviceUUIDs, options);
1342
+ resolve();
1343
+ // #endif
1344
+ // #ifndef APP-PLUS
1345
+ reject(Errors.unsupportedPlatform());
1346
+ // #endif
1347
+ }
1348
+ catch (error) {
1349
+ reject(handlePlatformError(error, 'ios'));
1350
+ }
1351
+ });
1352
+ });
1353
+ }
1354
+ stopScanInternal() {
1355
+ return __awaiter(this, void 0, void 0, function* () {
1356
+ return new Promise((resolve, reject) => {
1357
+ try {
1358
+ // #ifdef APP-PLUS
1359
+ if (this.centralManager) {
1360
+ this.centralManager.stopScan();
1361
+ }
1362
+ resolve();
1363
+ // #endif
1364
+ // #ifndef APP-PLUS
1365
+ reject(Errors.unsupportedPlatform());
1366
+ // #endif
1367
+ }
1368
+ catch (error) {
1369
+ reject(handlePlatformError(error, 'ios'));
1370
+ }
1371
+ });
1372
+ });
1373
+ }
1374
+ checkPermissions() {
1375
+ return __awaiter(this, void 0, void 0, function* () {
1376
+ // iOS 权限由系统自动管理
1377
+ // 需要在原生工程的 Info.plist 中配置 NSBluetoothAlwaysUsageDescription
1378
+ return true;
1379
+ });
1380
+ }
1381
+ requestPermissions() {
1382
+ return __awaiter(this, void 0, void 0, function* () {
1383
+ // iOS 权限由系统自动管理
1384
+ return true;
1385
+ });
1386
+ }
1387
+ onAdapterStateChange(callback) {
1388
+ this.adapterStateListeners.push(callback);
1389
+ // 委托中已处理状态变化
1390
+ }
1391
+ destroy() {
1392
+ super.destroy();
1393
+ // #ifdef APP-PLUS
1394
+ if (this.centralManager) {
1395
+ this.centralManager.stopScan();
1396
+ plus.ios.deleteObject(this.centralManager);
1397
+ }
1398
+ if (this.delegate) {
1399
+ plus.ios.deleteObject(this.delegate);
1400
+ }
1401
+ // #endif
1402
+ this.centralManager = null;
1403
+ this.delegate = null;
1404
+ }
1405
+ /**
1406
+ * 处理发现的外设
1407
+ */
1408
+ handlePeripheral(peripheral, advertisementData, RSSI) {
1409
+ try {
1410
+ // #ifdef APP-PLUS
1411
+ const deviceId = peripheral.identifier().UUIDString();
1412
+ const name = peripheral.name();
1413
+ // 解析广播数据
1414
+ const localName = advertisementData.objectForKey('kCBAdvDataLocalName');
1415
+ const serviceUUIDs = advertisementData.objectForKey('kCBAdvDataServiceUUIDs');
1416
+ const manufacturerData = advertisementData.objectForKey('kCBAdvDataManufacturerData');
1417
+ const isConnectable = advertisementData.objectForKey('kCBAdvDataIsConnectable');
1418
+ const txPowerLevel = advertisementData.objectForKey('kCBAdvDataTxPowerLevel');
1419
+ // 转换服务 UUID
1420
+ const serviceUuids = [];
1421
+ if (serviceUUIDs) {
1422
+ const count = serviceUUIDs.count();
1423
+ for (let i = 0; i < count; i++) {
1424
+ const uuid = serviceUUIDs.objectAtIndex(i).UUIDString();
1425
+ serviceUuids.push(uuid);
1426
+ }
1427
+ }
1428
+ // 转换制造商数据
1429
+ let manufacturerDataBuffer;
1430
+ if (manufacturerData) {
1431
+ const length = manufacturerData.length();
1432
+ const bytes = new Uint8Array(length);
1433
+ manufacturerData.getBytes(bytes);
1434
+ manufacturerDataBuffer = bytes.buffer;
1435
+ }
1436
+ this.handleDeviceFound({
1437
+ deviceId,
1438
+ name: name || localName,
1439
+ RSSI,
1440
+ advertisData: manufacturerDataBuffer,
1441
+ serviceUuids,
1442
+ localName,
1443
+ isConnectable,
1444
+ txPowerLevel,
1445
+ });
1446
+ // #endif
1447
+ }
1448
+ catch (error) {
1449
+ console.error('处理外设数据失败:', error);
1450
+ }
1451
+ }
1452
+ /**
1453
+ * 获取 iOS 蓝牙适配器状态
1454
+ */
1455
+ getAdapterState() {
1456
+ return __awaiter(this, void 0, void 0, function* () {
1457
+ return new Promise((resolve, reject) => {
1458
+ // #ifdef APP-PLUS
1459
+ try {
1460
+ const state = this.centralManager.state();
1461
+ // CBManagerStatePoweredOn = 5
1462
+ resolve({
1463
+ available: state === 5,
1464
+ discovering: this.state === 'scanning',
1465
+ });
1466
+ }
1467
+ catch (error) {
1468
+ reject(handlePlatformError(error, 'ios'));
1469
+ }
1470
+ // #endif
1471
+ // #ifndef APP-PLUS
1472
+ reject(Errors.unsupportedPlatform());
1473
+ // #endif
1474
+ });
1475
+ });
1476
+ }
1477
+ }
1478
+
1479
+ /**
1480
+ * 鸿蒙原生蓝牙扫描器
1481
+ * 使用鸿蒙原生 API 进行蓝牙扫描
1482
+ */
1483
+ class HarmonyNativeScanner extends BaseScanner {
1484
+ constructor() {
1485
+ super(...arguments);
1486
+ this.bluetoothManager = null;
1487
+ this.bleScanner = null;
1488
+ this.scanCallback = null;
1489
+ }
1490
+ getPlatform() {
1491
+ return Platform.HARMONY;
1492
+ }
1493
+ init() {
1494
+ return __awaiter(this, void 0, void 0, function* () {
1495
+ return new Promise((resolve, reject) => {
1496
+ try {
1497
+ // #ifdef HARMONY
1498
+ // 导入鸿蒙蓝牙模块
1499
+ const bluetooth = require('@ohos.bluetooth.ble');
1500
+ // 获取蓝牙管理器
1501
+ this.bluetoothManager = bluetooth.BLE;
1502
+ if (!this.bluetoothManager) {
1503
+ reject(Errors.adapterNotAvailable());
1504
+ return;
1505
+ }
1506
+ // 检查蓝牙是否开启
1507
+ const state = this.bluetoothManager.getState();
1508
+ if (state !== 2) { // STATE_ON = 2
1509
+ reject(Errors.bluetoothDisabled());
1510
+ return;
1511
+ }
1512
+ this.initialized = true;
1513
+ resolve();
1514
+ // #endif
1515
+ // #ifndef HARMONY
1516
+ reject(createError(ErrorCodes.UNSUPPORTED_PLATFORM, '非鸿蒙环境不支持原生鸿蒙扫描'));
1517
+ // #endif
1518
+ }
1519
+ catch (error) {
1520
+ reject(handlePlatformError(error, 'harmony'));
1521
+ }
1522
+ });
1523
+ });
1524
+ }
1525
+ startScanInternal() {
1526
+ return __awaiter(this, void 0, void 0, function* () {
1527
+ return new Promise((resolve, reject) => {
1528
+ try {
1529
+ // #ifdef HARMONY
1530
+ const bluetooth = require('@ohos.bluetooth.ble');
1531
+ // 构建扫描过滤器
1532
+ const filters = [];
1533
+ if (this.options.services && this.options.services.length > 0) {
1534
+ this.options.services.forEach((uuid) => {
1535
+ const filter = {
1536
+ serviceUuid: uuid,
1537
+ };
1538
+ filters.push(filter);
1539
+ });
1540
+ }
1541
+ if (this.options.nameFilter) {
1542
+ const names = Array.isArray(this.options.nameFilter)
1543
+ ? this.options.nameFilter
1544
+ : [this.options.nameFilter];
1545
+ names.forEach((name) => {
1546
+ if (typeof name === 'string') {
1547
+ filters.push({ deviceName: name });
1548
+ }
1549
+ });
1550
+ }
1551
+ // 构建扫描配置
1552
+ const scanOptions = {
1553
+ interval: 0,
1554
+ dutyMode: bluetooth.ScanDuty.SCAN_MODE_LOW_LATENCY,
1555
+ matchMode: bluetooth.MatchMode.MATCH_MODE_AGGRESSIVE,
1556
+ };
1557
+ // 创建扫描回调
1558
+ const self = this;
1559
+ this.scanCallback = {
1560
+ onScanResult: (result) => {
1561
+ const device = result.device;
1562
+ const data = result.data;
1563
+ const rssi = result.rssi;
1564
+ self.handleDeviceFound({
1565
+ deviceId: device.deviceId,
1566
+ name: device.deviceName,
1567
+ RSSI: rssi,
1568
+ advertisData: data,
1569
+ serviceUuids: result.serviceUuids,
1570
+ });
1571
+ },
1572
+ onScanFailed: (errorCode) => {
1573
+ console.error('扫描失败:', errorCode);
1574
+ },
1575
+ };
1576
+ // 开始扫描
1577
+ this.bluetoothManager.startBLEScan(filters, scanOptions, this.scanCallback);
1578
+ resolve();
1579
+ // #endif
1580
+ // #ifndef HARMONY
1581
+ reject(Errors.unsupportedPlatform());
1582
+ // #endif
1583
+ }
1584
+ catch (error) {
1585
+ reject(handlePlatformError(error, 'harmony'));
1586
+ }
1587
+ });
1588
+ });
1589
+ }
1590
+ stopScanInternal() {
1591
+ return __awaiter(this, void 0, void 0, function* () {
1592
+ return new Promise((resolve, reject) => {
1593
+ try {
1594
+ // #ifdef HARMONY
1595
+ if (this.bluetoothManager) {
1596
+ this.bluetoothManager.stopBLEScan();
1597
+ }
1598
+ resolve();
1599
+ // #endif
1600
+ // #ifndef HARMONY
1601
+ reject(Errors.unsupportedPlatform());
1602
+ // #endif
1603
+ }
1604
+ catch (error) {
1605
+ reject(handlePlatformError(error, 'harmony'));
1606
+ }
1607
+ });
1608
+ });
1609
+ }
1610
+ checkPermissions() {
1611
+ return __awaiter(this, void 0, void 0, function* () {
1612
+ // #ifdef HARMONY
1613
+ try {
1614
+ const atManager = require('@ohos.abilityAccessCtrl').createAtManager();
1615
+ const tokenId = require('@ohos.process').process.accessTokenId;
1616
+ const permissions = [
1617
+ 'ohos.permission.ACCESS_BLUETOOTH',
1618
+ 'ohos.permission.LOCATION',
1619
+ ];
1620
+ for (const permission of permissions) {
1621
+ const result = yield atManager.verifyAccessToken(tokenId, permission);
1622
+ if (result !== 0) {
1623
+ return false;
1624
+ }
1625
+ }
1626
+ return true;
1627
+ }
1628
+ catch (error) {
1629
+ console.error('检查权限失败:', error);
1630
+ return false;
1631
+ }
1632
+ // #endif
1633
+ // #ifndef HARMONY
1634
+ return false;
1635
+ // #endif
1636
+ });
1637
+ }
1638
+ requestPermissions() {
1639
+ return __awaiter(this, void 0, void 0, function* () {
1640
+ // #ifdef HARMONY
1641
+ try {
1642
+ const atManager = require('@ohos.abilityAccessCtrl').createAtManager();
1643
+ const context = getContext(this);
1644
+ const permissions = [
1645
+ 'ohos.permission.ACCESS_BLUETOOTH',
1646
+ 'ohos.permission.LOCATION',
1647
+ ];
1648
+ const result = yield atManager.requestPermissionsFromUser(context, permissions);
1649
+ return result.authResults.every((authResult) => authResult === 0);
1650
+ }
1651
+ catch (error) {
1652
+ console.error('请求权限失败:', error);
1653
+ return false;
1654
+ }
1655
+ // #endif
1656
+ // #ifndef HARMONY
1657
+ return false;
1658
+ // #endif
1659
+ });
1660
+ }
1661
+ onAdapterStateChange(callback) {
1662
+ this.adapterStateListeners.push(callback);
1663
+ // #ifdef HARMONY
1664
+ try {
1665
+ const bluetooth = require('@ohos.bluetooth.ble');
1666
+ // 监听蓝牙状态变化
1667
+ bluetooth.on('stateChange', (data) => {
1668
+ this.emitAdapterStateChange({
1669
+ available: data.state === 2, // STATE_ON = 2
1670
+ discovering: this.state === 'scanning',
1671
+ });
1672
+ });
1673
+ }
1674
+ catch (error) {
1675
+ console.error('监听状态变化失败:', error);
1676
+ }
1677
+ // #endif
1678
+ }
1679
+ destroy() {
1680
+ super.destroy();
1681
+ // #ifdef HARMONY
1682
+ try {
1683
+ const bluetooth = require('@ohos.bluetooth.ble');
1684
+ // 停止扫描
1685
+ if (this.bluetoothManager) {
1686
+ this.bluetoothManager.stopBLEScan();
1687
+ }
1688
+ // 移除状态监听
1689
+ bluetooth.off('stateChange');
1690
+ }
1691
+ catch (error) {
1692
+ console.error('销毁失败:', error);
1693
+ }
1694
+ // #endif
1695
+ this.bluetoothManager = null;
1696
+ this.scanCallback = null;
1697
+ }
1698
+ /**
1699
+ * 获取鸿蒙蓝牙适配器状态
1700
+ */
1701
+ getAdapterState() {
1702
+ return __awaiter(this, void 0, void 0, function* () {
1703
+ return new Promise((resolve, reject) => {
1704
+ // #ifdef HARMONY
1705
+ try {
1706
+ const state = this.bluetoothManager.getState();
1707
+ resolve({
1708
+ available: state === 2,
1709
+ discovering: this.state === 'scanning',
1710
+ });
1711
+ }
1712
+ catch (error) {
1713
+ reject(handlePlatformError(error, 'harmony'));
1714
+ }
1715
+ // #endif
1716
+ // #ifndef HARMONY
1717
+ reject(Errors.unsupportedPlatform());
1718
+ // #endif
1719
+ });
1720
+ });
1721
+ }
1722
+ }
1723
+
1724
+ /**
1725
+ * 创建蓝牙扫描器
1726
+ * @param options 创建选项
1727
+ * @returns 蓝牙扫描器实例
1728
+ */
1729
+ function createScanner(options = {}) {
1730
+ const { type = 'auto', platformConfig = {} } = options;
1731
+ const platform = getPlatform();
1732
+ const app = isApp();
1733
+ // 根据类型和平台选择合适的扫描器
1734
+ if (type === 'uniapp' || (type === 'auto' && !app)) {
1735
+ // 使用 UniApp 标准 API
1736
+ const scanner = new UniAppScanner();
1737
+ Object.assign(scanner, { platformConfig });
1738
+ return scanner;
1739
+ }
1740
+ if (type === 'native' || type === 'auto') {
1741
+ // 使用原生 API
1742
+ switch (platform) {
1743
+ case Platform.ANDROID:
1744
+ const androidScanner = new AndroidNativeScanner();
1745
+ Object.assign(androidScanner, { platformConfig });
1746
+ return androidScanner;
1747
+ case Platform.IOS:
1748
+ const iosScanner = new IOSNativeScanner();
1749
+ Object.assign(iosScanner, { platformConfig });
1750
+ return iosScanner;
1751
+ case Platform.HARMONY:
1752
+ const harmonyScanner = new HarmonyNativeScanner();
1753
+ Object.assign(harmonyScanner, { platformConfig });
1754
+ return harmonyScanner;
1755
+ default:
1756
+ // 未知平台使用 UniApp API
1757
+ const defaultScanner = new UniAppScanner();
1758
+ Object.assign(defaultScanner, { platformConfig });
1759
+ return defaultScanner;
1760
+ }
1761
+ }
1762
+ // 默认使用 UniApp API
1763
+ const defaultScanner = new UniAppScanner();
1764
+ Object.assign(defaultScanner, { platformConfig });
1765
+ return defaultScanner;
1766
+ }
1767
+ /**
1768
+ * 创建 UniApp 扫描器
1769
+ * 使用 UniApp 标准蓝牙 API,兼容所有平台
1770
+ */
1771
+ function createUniAppScanner(platformConfig) {
1772
+ return createScanner({ type: 'uniapp', platformConfig });
1773
+ }
1774
+ /**
1775
+ * 创建原生扫描器
1776
+ * 使用平台原生 API,功能更强大但需要特定条件
1777
+ */
1778
+ function createNativeScanner(platformConfig) {
1779
+ return createScanner({ type: 'native', platformConfig });
1780
+ }
1781
+ /**
1782
+ * 检查当前环境是否支持蓝牙
1783
+ */
1784
+ function isBluetoothSupported() {
1785
+ // #ifdef APP-PLUS
1786
+ return true;
1787
+ }
1788
+
1789
+ // 导出枚举
1790
+
1791
+ export { AndroidNativeScanner, BaseScanner, ErrorCodes, Errors, HarmonyNativeScanner, IOSNativeScanner, Platform, ScanState, UniAppScanner, arrayBufferToBase64, base64ToArrayBuffer, checkPermissions, createError, createNativeScanner, createScanner, createUniAppScanner, createScanner as default, formatUUID, getPlatform, getPlatformName, isApp, isBluetoothSupported, isPlatform, matchUUID, parseAdvertisementData, requestPermissions };