@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,309 @@
1
+ import {
2
+ BLEDevice,
3
+ ScanOptions,
4
+ ScanState,
5
+ ScanCallbacks,
6
+ IBLEScanner,
7
+ AdapterState,
8
+ BLEError,
9
+ Platform,
10
+ PlatformConfig,
11
+ DeviceFilter,
12
+ } from '../types';
13
+ import { Errors, handlePlatformError } from '../utils/errors';
14
+ import { normalizeDeviceData } from '../utils/data-parser';
15
+
16
+ /**
17
+ * 扫描器基类
18
+ */
19
+ export abstract class BaseScanner implements IBLEScanner {
20
+ protected state: ScanState = ScanState.IDLE;
21
+ protected discoveredDevices: Map<string, BLEDevice> = new Map();
22
+ protected callbacks: ScanCallbacks = {};
23
+ protected options: ScanOptions = {};
24
+ protected platformConfig: PlatformConfig = {};
25
+ protected initialized = false;
26
+ protected timeoutId: number | null = null;
27
+ protected adapterStateListeners: ((state: AdapterState) => void)[] = [];
28
+
29
+ /**
30
+ * 获取当前平台
31
+ */
32
+ abstract getPlatform(): Platform;
33
+
34
+ /**
35
+ * 初始化蓝牙适配器
36
+ */
37
+ abstract init(): Promise<void>;
38
+
39
+ /**
40
+ * 平台特定的开始扫描实现
41
+ */
42
+ protected abstract startScanInternal(): Promise<void>;
43
+
44
+ /**
45
+ * 平台特定的停止扫描实现
46
+ */
47
+ protected abstract stopScanInternal(): Promise<void>;
48
+
49
+ /**
50
+ * 检查蓝牙权限
51
+ */
52
+ abstract checkPermissions(): Promise<boolean>;
53
+
54
+ /**
55
+ * 请求蓝牙权限
56
+ */
57
+ abstract requestPermissions(): Promise<boolean>;
58
+
59
+ /**
60
+ * 监听适配器状态变化
61
+ */
62
+ abstract onAdapterStateChange(callback: (state: AdapterState) => void): void;
63
+
64
+ /**
65
+ * 开始扫描
66
+ */
67
+ async startScan(options: ScanOptions = {}, callbacks: ScanCallbacks = {}): Promise<void> {
68
+ if (this.state === ScanState.SCANNING) {
69
+ throw Errors.alreadyScanning();
70
+ }
71
+
72
+ if (!this.initialized) {
73
+ throw Errors.notInitialized();
74
+ }
75
+
76
+ // 检查权限
77
+ const hasPermission = await this.checkPermissions();
78
+ if (!hasPermission) {
79
+ const granted = await this.requestPermissions();
80
+ if (!granted) {
81
+ throw Errors.permissionDenied();
82
+ }
83
+ }
84
+
85
+ this.options = { ...this.getDefaultOptions(), ...options };
86
+ this.callbacks = callbacks;
87
+
88
+ try {
89
+ this.setState(ScanState.SCANNING);
90
+
91
+ // 清空之前的设备列表(如果不需要重复上报)
92
+ if (!this.options.allowDuplicates) {
93
+ this.clearDevices();
94
+ }
95
+
96
+ // 设置超时
97
+ if (this.options.timeout && this.options.timeout > 0) {
98
+ this.timeoutId = window.setTimeout(() => {
99
+ this.handleTimeout();
100
+ }, this.options.timeout);
101
+ }
102
+
103
+ await this.startScanInternal();
104
+ this.callbacks.onScanStart?.();
105
+ } catch (error) {
106
+ this.setState(ScanState.ERROR);
107
+ this.clearTimeout();
108
+ throw error;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 停止扫描
114
+ */
115
+ async stopScan(): Promise<void> {
116
+ if (this.state !== ScanState.SCANNING) {
117
+ return;
118
+ }
119
+
120
+ this.setState(ScanState.STOPPING);
121
+ this.clearTimeout();
122
+
123
+ try {
124
+ await this.stopScanInternal();
125
+ this.setState(ScanState.IDLE);
126
+ this.callbacks.onScanStop?.();
127
+ } catch (error) {
128
+ this.setState(ScanState.ERROR);
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * 获取已发现的设备列表
135
+ */
136
+ getDiscoveredDevices(): BLEDevice[] {
137
+ return Array.from(this.discoveredDevices.values());
138
+ }
139
+
140
+ /**
141
+ * 清空设备列表
142
+ */
143
+ clearDevices(): void {
144
+ this.discoveredDevices.clear();
145
+ }
146
+
147
+ /**
148
+ * 获取当前扫描状态
149
+ */
150
+ getState(): ScanState {
151
+ return this.state;
152
+ }
153
+
154
+ /**
155
+ * 销毁扫描器
156
+ */
157
+ destroy(): void {
158
+ this.stopScan();
159
+ this.clearDevices();
160
+ this.clearTimeout();
161
+ this.callbacks = {};
162
+ this.adapterStateListeners = [];
163
+ this.initialized = false;
164
+ }
165
+
166
+ /**
167
+ * 处理发现的设备
168
+ */
169
+ protected handleDeviceFound(rawDevice: any): void {
170
+ try {
171
+ const device = normalizeDeviceData(rawDevice);
172
+
173
+ // 应用过滤器
174
+ if (!this.filterDevice(device)) {
175
+ return;
176
+ }
177
+
178
+ // 检查是否已存在
179
+ const existingDevice = this.discoveredDevices.get(device.deviceId);
180
+ if (existingDevice && !this.options.allowDuplicates) {
181
+ // 更新 RSSI
182
+ existingDevice.RSSI = device.RSSI;
183
+ return;
184
+ }
185
+
186
+ // 保存设备
187
+ this.discoveredDevices.set(device.deviceId, device);
188
+
189
+ // 触发回调
190
+ this.callbacks.onDeviceFound?.(device);
191
+ } catch (error) {
192
+ console.error('处理设备数据失败:', error);
193
+ }
194
+ }
195
+
196
+ /**
197
+ * 过滤设备
198
+ */
199
+ protected filterDevice(device: BLEDevice): boolean {
200
+ // 按名称过滤
201
+ if (this.options.nameFilter) {
202
+ const deviceName = device.name || '';
203
+ const filters = Array.isArray(this.options.nameFilter)
204
+ ? this.options.nameFilter
205
+ : [this.options.nameFilter];
206
+
207
+ const nameMatch = filters.some(filter => {
208
+ if (typeof filter === 'string') {
209
+ return deviceName.includes(filter);
210
+ }
211
+ return filter.test(deviceName);
212
+ });
213
+
214
+ if (!nameMatch) return false;
215
+ }
216
+
217
+ // 按信号强度过滤
218
+ if (this.options.rssiThreshold !== undefined) {
219
+ if (device.RSSI < this.options.rssiThreshold) {
220
+ return false;
221
+ }
222
+ }
223
+
224
+ // 按服务UUID过滤
225
+ if (this.options.services && this.options.services.length > 0) {
226
+ if (!device.serviceUuids) return false;
227
+
228
+ const hasMatchingService = this.options.services.some(service =>
229
+ device.serviceUuids!.some(uuid =>
230
+ uuid.toLowerCase().includes(service.toLowerCase())
231
+ )
232
+ );
233
+
234
+ if (!hasMatchingService) return false;
235
+ }
236
+
237
+ // 按制造商数据过滤
238
+ if (this.options.manufacturerFilter !== undefined) {
239
+ if (!device.manufacturerData) return false;
240
+
241
+ const filters = Array.isArray(this.options.manufacturerFilter)
242
+ ? this.options.manufacturerFilter
243
+ : [this.options.manufacturerFilter];
244
+
245
+ const manufacturerIds = Object.keys(device.manufacturerData).map(id =>
246
+ parseInt(id, 16)
247
+ );
248
+
249
+ const hasMatchingManufacturer = filters.some(filter =>
250
+ manufacturerIds.includes(filter)
251
+ );
252
+
253
+ if (!hasMatchingManufacturer) return false;
254
+ }
255
+
256
+ return true;
257
+ }
258
+
259
+ /**
260
+ * 处理超时
261
+ */
262
+ protected handleTimeout(): void {
263
+ this.stopScan();
264
+ this.callbacks.onTimeout?.();
265
+ }
266
+
267
+ /**
268
+ * 处理错误
269
+ */
270
+ protected handleError(error: any): void {
271
+ const bleError = handlePlatformError(error, this.getPlatform());
272
+ this.setState(ScanState.ERROR);
273
+ this.callbacks.onError?.(bleError);
274
+ }
275
+
276
+ /**
277
+ * 设置状态
278
+ */
279
+ protected setState(state: ScanState): void {
280
+ this.state = state;
281
+ }
282
+
283
+ /**
284
+ * 清除超时定时器
285
+ */
286
+ protected clearTimeout(): void {
287
+ if (this.timeoutId !== null) {
288
+ window.clearTimeout(this.timeoutId);
289
+ this.timeoutId = null;
290
+ }
291
+ }
292
+
293
+ /**
294
+ * 获取默认选项
295
+ */
296
+ protected getDefaultOptions(): ScanOptions {
297
+ return {
298
+ timeout: 10000,
299
+ allowDuplicates: false,
300
+ };
301
+ }
302
+
303
+ /**
304
+ * 触发适配器状态变化
305
+ */
306
+ protected emitAdapterStateChange(state: AdapterState): void {
307
+ this.adapterStateListeners.forEach(listener => listener(state));
308
+ }
309
+ }
package/src/factory.ts ADDED
@@ -0,0 +1,116 @@
1
+ import { IBLEScanner, Platform, PlatformConfig } from './types';
2
+ import { UniAppScanner } from './platforms/uniapp-scanner';
3
+ import { AndroidNativeScanner } from './platforms/android-native';
4
+ import { IOSNativeScanner } from './platforms/ios-native';
5
+ import { HarmonyNativeScanner } from './platforms/harmony-native';
6
+ import { getPlatform, isApp } from './utils/platform';
7
+
8
+ /**
9
+ * 扫描器类型
10
+ */
11
+ export type ScannerType = 'auto' | 'uniapp' | 'native';
12
+
13
+ /**
14
+ * 创建扫描器的选项
15
+ */
16
+ export interface CreateScannerOptions {
17
+ /** 扫描器类型 */
18
+ type?: ScannerType;
19
+ /** 平台特定配置 */
20
+ platformConfig?: PlatformConfig;
21
+ }
22
+
23
+ /**
24
+ * 创建蓝牙扫描器
25
+ * @param options 创建选项
26
+ * @returns 蓝牙扫描器实例
27
+ */
28
+ export function createScanner(options: CreateScannerOptions = {}): IBLEScanner {
29
+ const { type = 'auto', platformConfig = {} } = options;
30
+ const platform = getPlatform();
31
+ const app = isApp();
32
+
33
+ // 根据类型和平台选择合适的扫描器
34
+ if (type === 'uniapp' || (type === 'auto' && !app)) {
35
+ // 使用 UniApp 标准 API
36
+ const scanner = new UniAppScanner();
37
+ Object.assign(scanner, { platformConfig });
38
+ return scanner;
39
+ }
40
+
41
+ if (type === 'native' || type === 'auto') {
42
+ // 使用原生 API
43
+ switch (platform) {
44
+ case Platform.ANDROID:
45
+ const androidScanner = new AndroidNativeScanner();
46
+ Object.assign(androidScanner, { platformConfig });
47
+ return androidScanner;
48
+
49
+ case Platform.IOS:
50
+ const iosScanner = new IOSNativeScanner();
51
+ Object.assign(iosScanner, { platformConfig });
52
+ return iosScanner;
53
+
54
+ case Platform.HARMONY:
55
+ const harmonyScanner = new HarmonyNativeScanner();
56
+ Object.assign(harmonyScanner, { platformConfig });
57
+ return harmonyScanner;
58
+
59
+ default:
60
+ // 未知平台使用 UniApp API
61
+ const defaultScanner = new UniAppScanner();
62
+ Object.assign(defaultScanner, { platformConfig });
63
+ return defaultScanner;
64
+ }
65
+ }
66
+
67
+ // 默认使用 UniApp API
68
+ const defaultScanner = new UniAppScanner();
69
+ Object.assign(defaultScanner, { platformConfig });
70
+ return defaultScanner;
71
+ }
72
+
73
+ /**
74
+ * 创建 UniApp 扫描器
75
+ * 使用 UniApp 标准蓝牙 API,兼容所有平台
76
+ */
77
+ export function createUniAppScanner(platformConfig?: PlatformConfig): IBLEScanner {
78
+ return createScanner({ type: 'uniapp', platformConfig });
79
+ }
80
+
81
+ /**
82
+ * 创建原生扫描器
83
+ * 使用平台原生 API,功能更强大但需要特定条件
84
+ */
85
+ export function createNativeScanner(platformConfig?: PlatformConfig): IBLEScanner {
86
+ return createScanner({ type: 'native', platformConfig });
87
+ }
88
+
89
+ /**
90
+ * 获取当前平台
91
+ */
92
+ export { getPlatform, Platform };
93
+
94
+ /**
95
+ * 检查当前环境是否支持蓝牙
96
+ */
97
+ export function isBluetoothSupported(): boolean {
98
+ // #ifdef APP-PLUS
99
+ return true;
100
+ // #endif
101
+
102
+ // #ifdef H5
103
+ return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
104
+ // #endif
105
+
106
+ // #ifdef MP
107
+ return true;
108
+ // #endif
109
+
110
+ return false;
111
+ }
112
+
113
+ /**
114
+ * 检查当前环境是否是 APP
115
+ */
116
+ export { isApp };
package/src/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ // 导出类型
2
+ export type {
3
+ BLEDevice,
4
+ ScanOptions,
5
+ ScanCallbacks,
6
+ AdapterState,
7
+ BLEError,
8
+ IBLEScanner,
9
+ PlatformConfig,
10
+ DeviceFilter,
11
+ CreateScannerOptions,
12
+ } from './types';
13
+
14
+ // 导出枚举
15
+ export { ScanState, Platform } from './types';
16
+
17
+ // 导出工厂函数
18
+ export {
19
+ createScanner,
20
+ createUniAppScanner,
21
+ createNativeScanner,
22
+ getPlatform,
23
+ isBluetoothSupported,
24
+ isApp,
25
+ } from './factory';
26
+
27
+ // 导出错误处理
28
+ export { ErrorCodes, createError, Errors } from './utils/errors';
29
+
30
+ // 导出数据解析工具
31
+ export {
32
+ parseAdvertisementData,
33
+ base64ToArrayBuffer,
34
+ arrayBufferToBase64,
35
+ formatUUID,
36
+ matchUUID,
37
+ } from './utils/data-parser';
38
+
39
+ // 导出权限工具
40
+ export { checkPermissions, requestPermissions } from './utils/permissions';
41
+
42
+ // 导出平台工具
43
+ export { isPlatform, getPlatformName } from './utils/platform';
44
+
45
+ // 导出扫描器类(供高级使用)
46
+ export { BaseScanner } from './core/base-scanner';
47
+ export { UniAppScanner } from './platforms/uniapp-scanner';
48
+ export { AndroidNativeScanner } from './platforms/android-native';
49
+ export { IOSNativeScanner } from './platforms/ios-native';
50
+ export { HarmonyNativeScanner } from './platforms/harmony-native';
51
+
52
+ // 默认导出
53
+ import { createScanner } from './factory';
54
+ export default createScanner;