/* eslint-disable no-useless-assignment, @typescript-eslint/no-unused-vars */ import { showToast } from "@/uni-helper"; const systemInfo = uni.getSystemInfoSync(); const isAndroidApp = systemInfo.uniPlatform === "app" && systemInfo.osName === "android"; // #ifdef APP-PLUS const BluetoothAdapter = plus.android.importClass( "android.bluetooth.BluetoothAdapter", ) as Android.BluetoothAdapter; const UUID = plus.android.importClass("java.util.UUID") as Android.UUID; const Context = plus.android.importClass("android.content.Context"); const Intent = plus.android.importClass("android.content.Intent"); const IntentFilter = plus.android.importClass("android.content.IntentFilter"); const InputStream = plus.android.importClass("java.io.InputStream"); const OutputStream = plus.android.importClass("java.io.OutputStream"); const Activity = plus.android.importClass("android.app.Activity"); const BluetoothSocket = plus.android.importClass( "android.bluetooth.BluetoothSocket", ); const BluetoothDevice = plus.android.importClass( "android.bluetooth.BluetoothDevice", ) as Android.BluetoothDevice; const invoke = plus.android.invoke; // #endif export interface Device extends UniNamespace.BluetoothDeviceInfo { // 通知服务ID notifyServiceId?: string; // 通知特征值ID notifyCharacterId?: string; // 写入服务ID writeServiceId?: string; // 写入特征值ID writeCharacterId?: string; // 读取服务ID readServiceId?: string; // 读取特征值ID readCharacterId?: string; } export class BluetoothError extends Error { constructor(massage?: string) { super(massage); this.name = "BluetoothError"; } } export class BluetoothUtils { static async safeInit(cb: () => Promise) { try { console.log("uni.openBluetoothAdapter"); await uni.openBluetoothAdapter(); return await cb(); } catch (error) { if (error instanceof BluetoothError) { showToast(error.message, "error"); } else { showToast("初始化蓝牙失败", "error"); } throw error; } finally { console.log("uni.closeBluetoothAdapter"); await uni.closeBluetoothAdapter(); } } /** * 获取所有设备 * @param cb * @param time * @returns */ static async getDevices(cb: (d: Device[]) => void, time: number) { return BluetoothUtils.safeInit(() => BluetoothUtils._getDevices(cb, time)); } private static async _getDevices(cb: (d: Device[]) => void, time = 3000) { console.log("BluetoothUtils._getDevices()"); const foundDevices = []; let btFindReceiver = null; let activity = null; let btAdapter: Android.BluetoothAdapter = {}; if (isAndroidApp) { btAdapter = BluetoothAdapter.getDefaultAdapter(); } try { if (isAndroidApp) { activity = plus.android.runtimeMainActivity() as Android.Activity; console.log({ activity, btAdapter }); if (btAdapter.isDiscovering()) { btAdapter.cancelDiscovery(); } btFindReceiver = plus.android.implements( "io.dcloud.android.content.BroadcastReceiver", { onReceive: function ( context: Android.Context, intent: Android.Intent, ) { // plus.android.importClass(context); // plus.android.importClass(intent); const action = intent.getAction(); console.log("onReceive"); if (BluetoothDevice.ACTION_FOUND === action) { // 找到设备 const device = intent.getParcelableExtra( BluetoothDevice.EXTRA_DEVICE, ); const newDevice = { name: device.getName(), deviceId: device.getAddress(), RSSI: -1, advertisData: [], advertisServiceUUIDs: [], localName: "", serviceData: [], }; const repetition = foundDevices.some( (v) => v.deviceId === newDevice.deviceId, ); console.log({ newDevice }); console.log({ repetition }); if ( !repetition && newDevice.name && newDevice.name !== "未知设备" ) { cb([newDevice]); foundDevices.push(newDevice); } } // if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) { // // 搜索完成 // } }, }, ); const filter = plus.android.newObject( "android.content.IntentFilter", ) as Android.IntentFilter; filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); activity.registerReceiver(btFindReceiver, filter); btAdapter.startDiscovery(); } else { const bluetoothAdapterState = await uni.getBluetoothAdapterState(); console.log(bluetoothAdapterState); if (!bluetoothAdapterState.available) { throw new BluetoothError("蓝牙适配器不可用"); } if (bluetoothAdapterState.discovering) { const res = await uni.stopBluetoothDevicesDiscovery(); console.log(res); } const sbddRes = await uni.startBluetoothDevicesDiscovery(); console.log("startBluetoothDevicesDiscovery", sbddRes); // 蓝牙设备监听 plus.bluetooth.onBluetoothDeviceFound uni.onBluetoothDeviceFound((result) => { // console.log("uni.onBluetoothDeviceFound", result); const newDevices = result.devices.filter((v) => { // console.log(foundDevices, v); return ( v.name && v.name !== "未知设备" && !foundDevices.some((v2) => v2.deviceId === v.deviceId) ); }); if (newDevices.length > 0) { cb(newDevices); foundDevices.push(...newDevices); } }); } return await new Promise((resolve) => { setTimeout(async () => { if (isAndroidApp) { btAdapter.cancelDiscovery(); } console.log({ foundDevices }); resolve(foundDevices); }, time); }); } catch (error) { console.error(error); if (error instanceof BluetoothError) { showToast(error.message, "error"); } else { showToast("获取蓝牙设备失败", "error"); } return Promise.reject(error); } finally { if (isAndroidApp) { if (activity) { if (btFindReceiver != null) { activity.unregisterReceiver(btFindReceiver); } activity = null; } } else { await uni.stopBluetoothDevicesDiscovery(); } } } /** * 绑定蓝牙设备 * @param device * @returns */ static async bindDevice(device: Device, cb: () => Promise) { return BluetoothUtils.safeInit(() => BluetoothUtils._bindDevice(device, cb), ); } private static async _bindDevice(device: Device, cb: () => Promise) { console.log("_bindDevice", device); try { let res = null; if (isAndroidApp) { res = await cb(); } else { // 失败重连5次 for (let i = 1; i <= 5; i++) { try { await uni.createBLEConnection({ deviceId: device.deviceId, }); break; } catch (error) { console.log(`蓝牙失败重连${i}次`); if (i == 5) { throw error; } } } res = await new Promise((resolve) => { setTimeout(async () => { const services = await BluetoothUtils._getServices(device); console.log("services", services); await BluetoothUtils._setCharacteristics(device, services); resolve(await cb()); // await cb() }, 1000); }); } return res; } catch (error) { showToast("绑定蓝牙设备失败", "error"); throw error; } finally { if (!isAndroidApp) { await uni.closeBLEConnection({ deviceId: device.deviceId, }); } } } /** * 获取蓝牙设备所有服务 * @param device * @returns */ private static async _getServices(device: Device) { console.log("获取蓝牙设备所有服务"); // const res = await new Promise( // (resolve, reject) => { // logger.info("uni.getBLEDeviceServices", device.deviceId); // uni.getBLEDeviceServices({ // deviceId: device.deviceId, // success: (res) => { // console.log(res); // logger.info("res: ", res); // resolve(res); // }, // fail: (err) => { // console.error(err); // logger.info("err: ", err); // reject(err); // }, // }); // } // ); const res = await uni.getBLEDeviceServices({ deviceId: device.deviceId, }); console.log(res); return res.services; } /** * 设置蓝牙设备特征值 */ private static async _setCharacteristics( device: Device, services: UniNamespace.GetBLEDeviceServicesSuccessData[], ) { console.log("_setCharacteristics", services); // 获取蓝牙设备某个服务中所有特征值 // plus.bluetooth.getBLEDeviceCharacteristics let notifyServiceId = ""; let writeServiceId = ""; let readServiceId = ""; let notifyCharacterId = ""; let writeCharacterId = ""; let readCharacterId = ""; for (const service of services) { // const { notifyServiceId, writeServiceId, readServiceId } = device; const done = [notifyServiceId, writeServiceId, readServiceId].every( (v) => v !== "", ); if (done) { break; } console.log( "获取蓝牙设备某个服务中所有特征值/uni.getBLEDeviceCharacteristics", ); const res = await uni.getBLEDeviceCharacteristics({ deviceId: device.deviceId, serviceId: service.uuid, }); console.log(res); for (const characteristic of res.characteristics) { console.log(characteristic); // const { notifyCharacterId, writeCharacterId, readCharacterId } = device; const done = [ notifyCharacterId, writeCharacterId, readCharacterId, ].every((v) => v !== ""); if (done) { break; } const { uuid, properties } = characteristic; if (!notifyCharacterId) { if (properties.notify) { notifyCharacterId = uuid; notifyServiceId = service.uuid; } } if (!writeCharacterId) { if (properties.write) { writeCharacterId = uuid; writeServiceId = service.uuid; } } if (!readCharacterId) { if (properties.read) { readCharacterId = uuid; readServiceId = service.uuid; } } } } device.notifyServiceId = notifyServiceId; device.notifyCharacterId = notifyCharacterId; device.writeServiceId = writeServiceId; device.writeCharacterId = writeCharacterId; device.readServiceId = readServiceId; device.readCharacterId = readCharacterId; console.log(device); } static async getBLEMTU(device: Device) { return BluetoothUtils.bindDevice(device, () => BluetoothUtils._getBLEMTU(device), ); } private static async _getBLEMTU(device: Device) { console.log("_getBLEMTU", device); // return new Promise(async (resolve, reject) => { return uni.getBLEMTU({ deviceId: device.deviceId, // success: (res) => { // resolve(res.mtu); // }, }); // }); } static async sendData(device: Device, data: number[]) { if (isAndroidApp) { BluetoothUtils.sendDataAndroid(device, data); } else { const buf = new ArrayBuffer(data.length); const dataView = new DataView(buf); data.forEach((d, i) => { dataView.setUint8(i, d); }); return await new Promise((resolve, reject) => { uni.writeBLECharacteristicValue({ deviceId: device.deviceId ?? "", serviceId: device.writeServiceId ?? "", characteristicId: device.writeCharacterId ?? "", value: buf, success: (res) => { resolve(res); }, fail: (err) => { reject(err); }, }); }); } } static async sendDataAndroid(device: Device, data: number[]) { const btAdapter = BluetoothAdapter.getDefaultAdapter(); const deviceObj = invoke(btAdapter, "getRemoteDevice", device.deviceId); const PRINTER_UUID = UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB", ); const btSocket = invoke( deviceObj, "createRfcommSocketToServiceRecord", PRINTER_UUID, ); if (!invoke(btSocket, "isConnected")) { console.log("检测到设备未连接,尝试连接...."); invoke(btSocket, "connect"); } console.log("设备已连接"); const outputStream = invoke(btSocket, "getOutputStream"); invoke(outputStream, "write", data); invoke(outputStream, "flush"); setTimeout(() => { invoke(outputStream, "close"); invoke(btSocket, "close"); }, 10 * 1000); } }