|
|
@@ -1,604 +1,181 @@
|
|
|
/**
|
|
|
- * 低功耗蓝牙设备服务 (BLE - Bluetooth Low Energy)
|
|
|
- * 提供蓝牙设备的初始化、搜索、连接、配对和数据写入等功能
|
|
|
- * 使用 uni-app 的 BLE API
|
|
|
+ * 蓝牙相关封装,面向 uni-app 的 API。
|
|
|
+ *
|
|
|
+ * 说明:
|
|
|
+ * - 仅支持蓝牙低功耗(BLE),适用于打印机等外设。
|
|
|
+ * - 组件内必须先调用 initBluetoothAdapter 之后才可开始搜索/连接。
|
|
|
*/
|
|
|
|
|
|
-// 蓝牙设备信息接口
|
|
|
-export interface BluetoothDeviceInfo {
|
|
|
- deviceId: string; // 设备 ID
|
|
|
- deviceName: string; // 设备名称
|
|
|
- address: string; // 设备地址
|
|
|
- RSSI?: number; // 信号强度
|
|
|
- isPaired?: boolean; // 是否已配对
|
|
|
-}
|
|
|
-
|
|
|
-// 蓝牙服务特征值接口
|
|
|
-export interface BluetoothServiceCharacteristic {
|
|
|
- serviceId: string; // 服务 UUID
|
|
|
- characteristicId: string; // 特征值 UUID
|
|
|
-}
|
|
|
-
|
|
|
-// 蓝牙服务类
|
|
|
-export class BlueDeviceServices {
|
|
|
- private bluetoothAdapter: any = null;
|
|
|
- private connectedDeviceId: string = '';
|
|
|
- private isConnected: boolean = false;
|
|
|
- private isInitializing: boolean = false;
|
|
|
-
|
|
|
- // 回调函数
|
|
|
- private onDeviceFound: ((device: BluetoothDeviceInfo) => void) | null = null;
|
|
|
- private onConnectionChanged: ((connected: boolean, device?: BluetoothDeviceInfo) => void) | null = null;
|
|
|
- private onReceiveData: ((data: ArrayBuffer) => void) | null = null;
|
|
|
- private onError: ((error: string) => void) | null = null;
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化蓝牙管理器
|
|
|
- */
|
|
|
- async initializeBluetooth(): Promise<boolean> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- if (this.isInitializing) {
|
|
|
- reject('蓝牙正在初始化中');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- this.isInitializing = true;
|
|
|
-
|
|
|
- const ensureBluetoothScope = () => {
|
|
|
- return new Promise<void>((resolveScope, rejectScope) => {
|
|
|
- uni.getSetting({
|
|
|
- success: (res: any) => {
|
|
|
- const hasScope = res.authSetting?.['scope.bluetooth'];
|
|
|
- if (hasScope) {
|
|
|
- resolveScope();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- uni.authorize({
|
|
|
- scope: 'scope.bluetooth',
|
|
|
- success: () => {
|
|
|
- resolveScope();
|
|
|
- },
|
|
|
- fail: (authErr: any) => {
|
|
|
- console.warn('蓝牙权限授权失败:', authErr);
|
|
|
- rejectScope(authErr);
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.warn('获取权限设置失败:', err);
|
|
|
- rejectScope(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- const handleInitFail = (err: any) => {
|
|
|
- console.error('蓝牙适配器初始化失败:', err);
|
|
|
- this.isInitializing = false;
|
|
|
- this.onError?.(`蓝牙适配器初始化失败:${err.message || JSON.stringify(err)}`);
|
|
|
- reject(err);
|
|
|
- };
|
|
|
-
|
|
|
- // 先检查权限
|
|
|
- ensureBluetoothScope().then(() => {
|
|
|
- uni.openBluetoothAdapter({
|
|
|
- success: () => {
|
|
|
- console.log('蓝牙适配器初始化成功');
|
|
|
- this.isInitializing = false;
|
|
|
- resolve(true);
|
|
|
- },
|
|
|
- fail: handleInitFail
|
|
|
- });
|
|
|
- }).catch((err) => {
|
|
|
- // 权限被拒绝或获取失败,提示用户打开设置
|
|
|
- this.isInitializing = false;
|
|
|
- this.onError?.('蓝牙权限未授权,请到系统设置中开启');
|
|
|
- reject(err);
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 关闭蓝牙适配器
|
|
|
- */
|
|
|
- async closeBluetoothAdapter(): Promise<void> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- try {
|
|
|
- uni.closeBluetoothAdapter({
|
|
|
- success: () => {
|
|
|
- console.log('蓝牙适配器已关闭');
|
|
|
- this.resetState();
|
|
|
- resolve();
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('关闭蓝牙适配器失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- reject(error);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 重置蓝牙状态
|
|
|
- */
|
|
|
- private resetState(): void {
|
|
|
- this.connectedDeviceId = '';
|
|
|
- this.isConnected = false;
|
|
|
- this.onConnectionChanged?.(false);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 设置设备发现回调
|
|
|
- */
|
|
|
- setOnDeviceFound(callback: (device: BluetoothDeviceInfo) => void): void {
|
|
|
- this.onDeviceFound = callback;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 设置连接状态变化回调
|
|
|
- */
|
|
|
- setOnConnectionChanged(callback: (connected: boolean, device?: BluetoothDeviceInfo) => void): void {
|
|
|
- this.onConnectionChanged = callback;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 设置数据接收回调
|
|
|
- */
|
|
|
- setOnReceiveData(callback: (data: ArrayBuffer) => void): void {
|
|
|
- this.onReceiveData = callback;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 设置错误回调
|
|
|
- */
|
|
|
- setOnError(callback: (error: string) => void): void {
|
|
|
- this.onError = callback;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取所有已配对的蓝牙设备列表
|
|
|
- */
|
|
|
- async getBluetoothDevices(): Promise<BluetoothDeviceInfo[]> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- uni.getBluetoothDevices({
|
|
|
- success: (res: any) => {
|
|
|
- const devices: BluetoothDeviceInfo[] = Object.values(res.devices).map((device: any) => ({
|
|
|
- deviceId: device.deviceId,
|
|
|
- deviceName: device.name || '未知设备',
|
|
|
- address: device.address,
|
|
|
- RSSI: device.RSSI,
|
|
|
- isPaired: device.isPaired || false
|
|
|
- }));
|
|
|
- resolve(devices);
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('获取蓝牙设备列表失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 开始搜索附近的蓝牙设备
|
|
|
- * @param services 要搜索的服务 UUID 列表(可选)
|
|
|
- */
|
|
|
- async startDiscoveryBluetoothDevices(services?: string[]): Promise<boolean> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- if (!this.bluetoothAdapter) {
|
|
|
- reject('请先初始化蓝牙模块');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const options: Record<string, any> = {
|
|
|
- success: () => {
|
|
|
- console.log('开始搜索蓝牙设备');
|
|
|
- resolve(true);
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('搜索蓝牙设备失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- // 如果有指定服务 UUID,添加到选项中
|
|
|
- if (services && services.length > 0) {
|
|
|
- options.services = services;
|
|
|
- }
|
|
|
-
|
|
|
- // 监听设备发现
|
|
|
- this.bluetoothAdapter.onDeviceFound((res: any) => {
|
|
|
- const device: BluetoothDeviceInfo = {
|
|
|
- deviceId: res.device.deviceId,
|
|
|
- deviceName: res.device.name || '未知设备',
|
|
|
- address: res.device.address,
|
|
|
- RSSI: res.device.RSSI,
|
|
|
- isPaired: res.device.isPaired || false
|
|
|
- };
|
|
|
-
|
|
|
- console.log('发现新设备:', device);
|
|
|
- this.onDeviceFound?.(device);
|
|
|
- });
|
|
|
-
|
|
|
- this.bluetoothAdapter.startDiscoveryBluetoothDevices(options);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 停止搜索蓝牙设备
|
|
|
- */
|
|
|
- async stopDiscoveryBluetoothDevices(): Promise<void> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- if (!this.bluetoothAdapter) {
|
|
|
- resolve();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- this.bluetoothAdapter.stopDiscoveryBluetoothDevices({
|
|
|
- success: () => {
|
|
|
- console.log('停止搜索蓝牙设备');
|
|
|
- resolve();
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('停止搜索蓝牙设备失败:', err);
|
|
|
- reject(err);
|
|
|
+export async function checkBluetoothPermission(): Promise<void> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.getSetting({
|
|
|
+ success: (res) => {
|
|
|
+ const hasLocation = res.authSetting?.['scope.userLocation']
|
|
|
+ if (hasLocation) {
|
|
|
+ return resolve()
|
|
|
}
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 连接蓝牙设备(建立 BLE 连接)
|
|
|
- * @param deviceId 设备 ID
|
|
|
- * @param timeout 超时时间(毫秒),默认 10000
|
|
|
- */
|
|
|
- async connectBluetoothDevice(deviceId: string, timeout: number = 10000): Promise<BluetoothDeviceInfo> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- if (!this.bluetoothAdapter) {
|
|
|
- reject('请先初始化蓝牙模块');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const connectTimeout = setTimeout(() => {
|
|
|
- reject(new Error('连接超时'));
|
|
|
- }, timeout);
|
|
|
-
|
|
|
- // 监听连接成功
|
|
|
- this.bluetoothAdapter.onConnectSuccess(() => {
|
|
|
- clearTimeout(connectTimeout);
|
|
|
- console.log('连接蓝牙设备成功:', deviceId);
|
|
|
- this.connectedDeviceId = deviceId;
|
|
|
- this.isConnected = true;
|
|
|
-
|
|
|
- // 获取设备信息
|
|
|
- this.getDeviceInfo(deviceId).then((info) => {
|
|
|
- this.onConnectionChanged?.(true, info);
|
|
|
- resolve(info);
|
|
|
- }).catch(reject);
|
|
|
- });
|
|
|
|
|
|
- // 监听连接失败
|
|
|
- this.bluetoothAdapter.onConnectFail(() => {
|
|
|
- clearTimeout(connectTimeout);
|
|
|
- console.error('连接蓝牙设备失败');
|
|
|
- this.isConnected = false;
|
|
|
- reject(new Error('连接失败'));
|
|
|
- });
|
|
|
+ uni.authorize({
|
|
|
+ scope: 'scope.userLocation',
|
|
|
+ success: () => resolve(),
|
|
|
+ fail: (err) => {
|
|
|
+ uni.showModal({
|
|
|
+ title: '权限提示',
|
|
|
+ content: '需要定位权限才能扫描蓝牙设备,请前往设置开启定位权限。',
|
|
|
+ showCancel: false,
|
|
|
+ confirmText: '去设置',
|
|
|
+ success: () => {
|
|
|
+ uni.openSetting({})
|
|
|
+ },
|
|
|
+ })
|
|
|
+ reject(err)
|
|
|
+ },
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- // 发起 BLE 连接
|
|
|
- uni.createBLEConnection({
|
|
|
- deviceId,
|
|
|
- success: () => {
|
|
|
- console.log('发起蓝牙连接请求');
|
|
|
+export async function initBluetoothAdapter(): Promise<void> {
|
|
|
+ const closeAdapter = () =>
|
|
|
+ new Promise<void>((resolve) => {
|
|
|
+ uni.closeBluetoothAdapter({
|
|
|
+ success: () => resolve(),
|
|
|
+ fail: () => resolve(),
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ const openAdapter = () =>
|
|
|
+ new Promise<void>((resolve, reject) => {
|
|
|
+ uni.openBluetoothAdapter({
|
|
|
+ success: () => resolve(),
|
|
|
+ fail: (err) => {
|
|
|
+ console.log(err)
|
|
|
+ reject(new Error(err.errMsg || `openBluetoothAdapter(${mode}) 失败`))
|
|
|
},
|
|
|
- fail: (err: any) => {
|
|
|
- clearTimeout(connectTimeout);
|
|
|
- console.error('发起蓝牙连接失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
+ })
|
|
|
+ })
|
|
|
|
|
|
- /**
|
|
|
- * 断开蓝牙连接
|
|
|
- */
|
|
|
- async disconnectBluetoothDevice(): Promise<void> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- if (!this.connectedDeviceId) {
|
|
|
- resolve();
|
|
|
- return;
|
|
|
- }
|
|
|
+ // 先尝试关闭现有 adapter,避免重复初始化导致的异常
|
|
|
+ await closeAdapter()
|
|
|
|
|
|
- uni.closeBLEConnection({
|
|
|
- deviceId: this.connectedDeviceId,
|
|
|
- success: () => {
|
|
|
- console.log('断开蓝牙连接成功');
|
|
|
- this.resetState();
|
|
|
- resolve();
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('断开蓝牙连接失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
+ // iOS 需要分别以主机/从机模式初始化一次,才能兼容全部场景。
|
|
|
+ const platform = uni.getSystemInfoSync?.()?.platform || ''
|
|
|
+ console.log(platform, 'initBluetoothAdapter platform')
|
|
|
|
|
|
- /**
|
|
|
- * 获取设备信息
|
|
|
- */
|
|
|
- private async getDeviceInfo(deviceId: string): Promise<BluetoothDeviceInfo> {
|
|
|
- return {
|
|
|
- deviceId,
|
|
|
- deviceName: '未知设备',
|
|
|
- address: deviceId,
|
|
|
- isPaired: false
|
|
|
- };
|
|
|
- }
|
|
|
+ // Android 等其他平台只需 central 模式
|
|
|
+ await openAdapter()
|
|
|
+}
|
|
|
|
|
|
- /**
|
|
|
- * 获取设备支持的所有服务
|
|
|
- */
|
|
|
- async getBluetoothDeviceServices(deviceId: string): Promise<any[]> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- uni.getBLEDeviceServices({
|
|
|
- deviceId,
|
|
|
- success: (res: any) => {
|
|
|
- console.log('获取设备服务成功:', res.services);
|
|
|
- resolve(res.services);
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('获取设备服务失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
+export async function getBluetoothAdapterState(): Promise<any> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.getBluetoothAdapterState({
|
|
|
+ success: resolve,
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- /**
|
|
|
- * 获取指定服务的特征值
|
|
|
- */
|
|
|
- async getBluetoothDeviceCharacteristics(
|
|
|
- deviceId: string,
|
|
|
- serviceId: string
|
|
|
- ): Promise<any[]> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- uni.getBLEDeviceCharacteristics({
|
|
|
- deviceId,
|
|
|
- serviceId,
|
|
|
- success: (res: any) => {
|
|
|
- console.log('获取特征值成功:', res.characteristics);
|
|
|
- resolve(res.characteristics);
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('获取特征值失败:', err);
|
|
|
- reject(err);
|
|
|
+export async function startBluetoothDevicesDiscovery(
|
|
|
+ onDeviceFound: (device: any) => void,
|
|
|
+): Promise<() => void> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ let listener: any | null = null
|
|
|
+
|
|
|
+ uni.startBluetoothDevicesDiscovery({
|
|
|
+ allowDuplicatesKey: false,
|
|
|
+ success: () => {
|
|
|
+ listener = (res: any) => {
|
|
|
+ if (!res || !res.devices) return
|
|
|
+ res.devices.forEach((device: any) => {
|
|
|
+ if (device) onDeviceFound(device)
|
|
|
+ })
|
|
|
}
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
|
|
|
- /**
|
|
|
- * 读取特征值
|
|
|
- */
|
|
|
- async readCharacteristicValue(
|
|
|
- deviceId: string,
|
|
|
- serviceId: string,
|
|
|
- characteristicId: string
|
|
|
- ): Promise<ArrayBuffer> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- uni.readBLECharacteristicValue({
|
|
|
- deviceId,
|
|
|
- serviceId,
|
|
|
- characteristicId,
|
|
|
- success: (res: any) => {
|
|
|
- console.log('读取特征值成功');
|
|
|
- resolve(res.value);
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('读取特征值失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
+ uni.onBluetoothDeviceFound(listener)
|
|
|
|
|
|
- /**
|
|
|
- * 订阅特征值变化
|
|
|
- */
|
|
|
- async notifyCharacteristicValue(
|
|
|
- deviceId: string,
|
|
|
- serviceId: string,
|
|
|
- characteristicId: string,
|
|
|
- enable: boolean = true
|
|
|
- ): Promise<void> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- uni.notifyBLECharacteristicValueChange({
|
|
|
- deviceId,
|
|
|
- serviceId,
|
|
|
- characteristicId,
|
|
|
- state: enable,
|
|
|
- success: () => {
|
|
|
- console.log(`${enable ? '启用' : '禁用'}特征值通知成功`);
|
|
|
-
|
|
|
- if (enable) {
|
|
|
- // 添加监听
|
|
|
- this.bluetoothAdapter?.onBLECharacteristicValueChange((res: any) => {
|
|
|
- if (res.deviceId === deviceId &&
|
|
|
- res.serviceId === serviceId &&
|
|
|
- res.characteristicId === characteristicId) {
|
|
|
- this.onReceiveData?.(res.value);
|
|
|
- }
|
|
|
- });
|
|
|
+ resolve(() => {
|
|
|
+ if (listener) {
|
|
|
+ uni.offBluetoothDeviceFound()
|
|
|
+ listener = null
|
|
|
}
|
|
|
-
|
|
|
- resolve();
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error(`${enable ? '启用' : '禁用'}特征值通知失败:`, err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- /**
|
|
|
- * 写入数据到特征值
|
|
|
- * @param deviceId 设备 ID
|
|
|
- * @param serviceId 服务 UUID
|
|
|
- * @param characteristicId 特征值 UUID
|
|
|
- * @param value 要写入的数据(ArrayBuffer 或 Uint8Array)
|
|
|
- * @param needResponse 是否需要响应,默认 true
|
|
|
- */
|
|
|
- async writeCharacteristicValue(
|
|
|
- deviceId: string,
|
|
|
- serviceId: string,
|
|
|
- characteristicId: string,
|
|
|
- value: ArrayBuffer | Uint8Array,
|
|
|
- needResponse: boolean = true
|
|
|
- ): Promise<void> {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- // 转换为 ArrayBuffer
|
|
|
- let buffer: ArrayBuffer;
|
|
|
- if (value instanceof Uint8Array) {
|
|
|
- buffer = value.buffer as ArrayBuffer;
|
|
|
- } else if (value instanceof ArrayBuffer) {
|
|
|
- buffer = value;
|
|
|
- } else {
|
|
|
- // 如果是字符串,转换为 UTF-8 编码
|
|
|
- const encoder = new TextEncoder();
|
|
|
- buffer = encoder.encode(value as string).buffer as ArrayBuffer;
|
|
|
- }
|
|
|
+export async function stopBluetoothDevicesDiscovery(): Promise<void> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.stopBluetoothDevicesDiscovery({
|
|
|
+ success: () => resolve(),
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- uni.writeBLECharacteristicValue({
|
|
|
- deviceId,
|
|
|
- serviceId,
|
|
|
- characteristicId,
|
|
|
- value: buffer,
|
|
|
- success: () => {
|
|
|
- console.log('写入特征值成功');
|
|
|
- resolve();
|
|
|
- },
|
|
|
- fail: (err: any) => {
|
|
|
- console.error('写入特征值失败:', err);
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
+export async function connectBLEDevice(deviceId: string): Promise<void> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.createBLEConnection({
|
|
|
+ deviceId,
|
|
|
+ success: resolve,
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- /**
|
|
|
- * 打印数据到蓝牙打印机
|
|
|
- * @param data 要打印的数据
|
|
|
- * @param printerServiceId 打印机服务 UUID
|
|
|
- * @param printerCharacteristicId 打印机特征值 UUID
|
|
|
- */
|
|
|
- async printData(
|
|
|
- data: string | Uint8Array,
|
|
|
- printerServiceId: string = '000018f0-0000-1000-8000-00805f9b34fb',
|
|
|
- printerCharacteristicId: string = '00002af1-0000-1000-8000-00805f9b34fb'
|
|
|
- ): Promise<void> {
|
|
|
- if (!this.connectedDeviceId) {
|
|
|
- throw new Error('未连接蓝牙设备');
|
|
|
- }
|
|
|
+export async function closeBLEConnection(deviceId: string): Promise<void> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.closeBLEConnection({
|
|
|
+ deviceId,
|
|
|
+ success: resolve,
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- await this.writeCharacteristicValue(
|
|
|
- this.connectedDeviceId,
|
|
|
- printerServiceId,
|
|
|
- printerCharacteristicId,
|
|
|
- data
|
|
|
- );
|
|
|
- }
|
|
|
+export async function getBluetoothDevices(): Promise<any[]> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.getBluetoothDevices({
|
|
|
+ success: (res: any) => resolve(res.devices || []),
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- /**
|
|
|
- * 检查设备是否已连接
|
|
|
- */
|
|
|
- isConnectedToDevice(): boolean {
|
|
|
- return this.isConnected;
|
|
|
- }
|
|
|
+export async function getBLEDeviceServicesAndCharacteristics(
|
|
|
+ deviceId: string,
|
|
|
+): Promise<{ services: any[]; characteristicsMap: Record<string, any[]> }> {
|
|
|
+ const services = await new Promise<any[]>((resolve, reject) => {
|
|
|
+ uni.getBLEDeviceServices({
|
|
|
+ deviceId,
|
|
|
+ success: (res: any) => resolve(res.services || []),
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
|
|
|
- /**
|
|
|
- * 获取当前连接的设备信息
|
|
|
- */
|
|
|
- getCurrentDevice(): BluetoothDeviceInfo | null {
|
|
|
- if (!this.connectedDeviceId) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- deviceId: this.connectedDeviceId,
|
|
|
- deviceName: '未知设备',
|
|
|
- address: this.connectedDeviceId
|
|
|
- };
|
|
|
- }
|
|
|
+ const characteristicsMap: Record<string, any[]> = {}
|
|
|
|
|
|
- /**
|
|
|
- * 查找并连接指定的蓝牙打印机
|
|
|
- * @param nameFilter 设备名称过滤器(部分匹配)
|
|
|
- * @param timeout 超时时间
|
|
|
- */
|
|
|
- async findAndConnectPrinter(
|
|
|
- nameFilter: string = '',
|
|
|
- timeout: number = 15000
|
|
|
- ): Promise<BluetoothDeviceInfo> {
|
|
|
- return new Promise(async (resolve, reject) => {
|
|
|
+ await Promise.all(
|
|
|
+ services.map(async (service) => {
|
|
|
try {
|
|
|
- // 初始化蓝牙
|
|
|
- await this.initializeBluetooth();
|
|
|
-
|
|
|
- // 开始搜索
|
|
|
- await this.startDiscoveryBluetoothDevices();
|
|
|
-
|
|
|
- let foundDevice: BluetoothDeviceInfo | null = null;
|
|
|
-
|
|
|
- // 设置设备发现回调
|
|
|
- this.setOnDeviceFound((device) => {
|
|
|
- // 检查是否符合过滤条件
|
|
|
- const matchesFilter = !nameFilter ||
|
|
|
- device.deviceName.includes(nameFilter) ||
|
|
|
- device.deviceName.toLowerCase().includes(nameFilter.toLowerCase());
|
|
|
-
|
|
|
- if (matchesFilter) {
|
|
|
- foundDevice = device;
|
|
|
- console.log('找到目标设备:', device);
|
|
|
-
|
|
|
- // 停止搜索
|
|
|
- this.stopDiscoveryBluetoothDevices();
|
|
|
-
|
|
|
- // 连接设备
|
|
|
- this.connectBluetoothDevice(device.deviceId, timeout)
|
|
|
- .then(resolve)
|
|
|
- .catch(reject);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 超时处理
|
|
|
- const searchTimeout = setTimeout(() => {
|
|
|
- this.stopDiscoveryBluetoothDevices();
|
|
|
- if (!foundDevice) {
|
|
|
- reject(new Error('未找到匹配的蓝牙设备'));
|
|
|
- }
|
|
|
- }, timeout);
|
|
|
-
|
|
|
- // 监听连接成功
|
|
|
- this.setOnConnectionChanged((connected, device) => {
|
|
|
- if (connected && device) {
|
|
|
- clearTimeout(searchTimeout);
|
|
|
- resolve(device);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- reject(error);
|
|
|
+ const characteristics = await new Promise<any[]>((resolve, reject) => {
|
|
|
+ uni.getBLEDeviceCharacteristics({
|
|
|
+ deviceId,
|
|
|
+ serviceId: service.uuid,
|
|
|
+ success: (res: any) => resolve(res.characteristics || []),
|
|
|
+ fail: reject,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ characteristicsMap[service.uuid] = characteristics
|
|
|
+ } catch (err) {
|
|
|
+ // 忽略某些服务获取特征值失败
|
|
|
+ console.warn('getBLEDeviceCharacteristics fail', service.uuid, err)
|
|
|
}
|
|
|
- });
|
|
|
- }
|
|
|
-}
|
|
|
+ }),
|
|
|
+ )
|
|
|
|
|
|
-// 导出单例实例
|
|
|
-export const blueDeviceService = new BlueDeviceServices();
|
|
|
+ return { services, characteristicsMap }
|
|
|
+}
|