This commit is contained in:
2026-06-02 15:28:25 +08:00
commit b271b84f70
249 changed files with 42240 additions and 0 deletions
+383
View File
@@ -0,0 +1,383 @@
/**
* 蓝牙多设备管理模块(Android 优化版)
* 功能:
* - 统一初始化蓝牙适配器
* - 扫描设备(可指定服务 UUID)
* - 直接连接已知设备(通过 deviceId/MAC
* - 管理多个设备(打印机、电子秤等)
* - 写入数据、接收数据、连接状态监听
* - 自动重连(可选)
*
* 使用方法:
* import bluetoothManager from '@/utils/BluetoothManager.js';
* await bluetoothManager.init();
* bluetoothManager.setDeviceFoundCallback(...);
* await bluetoothManager.connectKnownDevice(deviceId, serviceUUID, writeUUID);
*/
class BluetoothManager {
constructor() {
this.adapterOpen = false; // 蓝牙适配器是否已打开
this.discovering = false; // 是否正在扫描
this.devices = new Map(); // 存储已连接设备的信息:key=deviceId, value={ serviceId, writeCharId, notifyCharId, name }
// 全局回调(只注册一次)
this.globalListeners = {
onDeviceFound: null,
onConnectionChange: null,
onDataReceived: null,
};
}
/**
* 初始化蓝牙适配器(全局只需调用一次)
* @returns {Promise<void>}
*/
async init() {
if (this.adapterOpen) return;
return new Promise((resolve, reject) => {
uni.openBluetoothAdapter({
success: () => {
this.adapterOpen = true;
this._registerGlobalListeners();
resolve();
},
fail: (err) => {
console.error('蓝牙初始化失败', err);
reject(err);
},
});
});
}
/**
* 注册全局唯一的监听器(避免覆盖)
*/
_registerGlobalListeners() {
// 设备发现监听
uni.onBluetoothDeviceFound((res) => {
const { devices } = res;
if (this.globalListeners.onDeviceFound) {
devices.forEach(device => this.globalListeners.onDeviceFound(device));
}
});
// 连接状态变化监听
uni.onBLEConnectionStateChange((res) => {
const { deviceId, connected } = res;
if (this.globalListeners.onConnectionChange) {
this.globalListeners.onConnectionChange(deviceId, connected);
}
// 如果连接断开,从 devices Map 中移除(可选,保留以便重连)
if (!connected && this.devices.has(deviceId)) {
// 注意:不断开 Map 中的记录,因为可能用于重连;但可以标记一个状态
// 这里不做自动删除,由业务层决定是否移除
}
});
// 特征值数据接收监听
uni.onBLECharacteristicValueChange((res) => {
const { deviceId, value } = res;
if (this.globalListeners.onDataReceived) {
this.globalListeners.onDataReceived(deviceId, value);
}
});
}
/**
* 设置设备发现回调
* @param {Function} callback (device) => {}
*/
setDeviceFoundCallback(callback) {
this.globalListeners.onDeviceFound = callback;
}
/**
* 设置连接状态变化回调
* @param {Function} callback (deviceId, connected) => {}
*/
setConnectionChangeCallback(callback) {
this.globalListeners.onConnectionChange = callback;
}
/**
* 设置数据接收回调
* @param {Function} callback (deviceId, arrayBuffer) => {}
*/
setDataReceivedCallback(callback) {
this.globalListeners.onDataReceived = callback;
}
/**
* 开始扫描设备
* @param {Array} services 要过滤的服务 UUID 数组(可选)
* @returns {Promise}
*/
async startScan(services = []) {
if (!this.adapterOpen) {
throw new Error('蓝牙未初始化,请先调用 init()');
}
if (this.discovering) {
await this.stopScan();
}
return new Promise((resolve, reject) => {
uni.startBluetoothDevicesDiscovery({
services,
success: () => {
this.discovering = true;
resolve();
},
fail: reject,
});
});
}
/**
* 停止扫描
*/
stopScan() {
if (!this.discovering) return Promise.resolve();
return new Promise((resolve, reject) => {
uni.stopBluetoothDevicesDiscovery({
success: () => {
this.discovering = false;
resolve();
},
fail: reject,
});
});
}
/**
* 直接连接已知设备(不依赖扫描结果)
* @param {string} deviceId 设备 MAC 地址(Android
* @param {string} targetServiceUUID 目标服务的 UUID
* @param {string} writeUUID 写入特征值 UUID(可选,自动查找第一个可写)
* @param {string} notifyUUID 通知特征值 UUID(可选,自动查找第一个可通知)
* @returns {Promise<object>} 返回设备信息 { deviceId, serviceId, writeCharId, notifyCharId }
*/
async connectKnownDevice(deviceId, targetServiceUUID, writeUUID = null, notifyUUID = null) {
if (!this.adapterOpen) {
throw new Error('蓝牙未初始化,请先调用 init()');
}
// 如果已经连接且 Map 中有记录,直接返回(可选,但为了避免重复连接,先断开)
if (this.devices.has(deviceId)) {
await this.disconnectDevice(deviceId);
}
return new Promise((resolve, reject) => {
uni.createBLEConnection({
deviceId,
success: async () => {
// 等待服务发现稳定(重要:解决连接成功但查不到服务的问题)
await this._delay(800);
try {
// 1. 获取服务列表
const { services } = await this._getBLEDeviceServices(deviceId);
const targetService = services.find(s => s.uuid.toLowerCase().includes(targetServiceUUID.toLowerCase()));
if (!targetService) {
throw new Error(`未找到服务 ${targetServiceUUID}`);
}
// 2. 获取特征值
const characteristics = await this._getBLEDeviceCharacteristics(deviceId, targetService.uuid);
let writeChar = null;
if (writeUUID) {
writeChar = characteristics.find(c => c.uuid.toLowerCase() === writeUUID.toLowerCase());
} else {
writeChar = characteristics.find(c => c.properties.write || c.properties.writeNoResponse);
}
if (!writeChar) {
throw new Error('未找到可写入的特征值');
}
let notifyChar = null;
if (notifyUUID) {
notifyChar = characteristics.find(c => c.uuid.toLowerCase() === notifyUUID.toLowerCase());
} else {
notifyChar = characteristics.find(c => c.properties.notify || c.properties.indicate);
}
// 3. 启用通知(如果有 notify 特征值)
if (notifyChar && (notifyChar.properties.notify || notifyChar.properties.indicate)) {
await this._notifyBLECharacteristicValueChange(deviceId, targetService.uuid, notifyChar.uuid, true);
}
// 4. 存储设备信息
this.devices.set(deviceId, {
serviceId: targetService.uuid,
writeCharId: writeChar.uuid,
notifyCharId: notifyChar ? notifyChar.uuid : null,
});
resolve({
deviceId,
serviceId: targetService.uuid,
writeCharId: writeChar.uuid,
notifyCharId: notifyChar ? notifyChar.uuid : null,
});
} catch (err) {
reject(err);
}
},
fail: reject,
});
});
}
/**
* 向指定设备写入数据
* @param {string} deviceId
* @param {ArrayBuffer} buffer
* @returns {Promise}
*/
async writeData(deviceId, buffer) {
const device = this.devices.get(deviceId);
if (!device) {
throw new Error('设备未连接或未初始化,请先调用 connectKnownDevice');
}
return new Promise((resolve, reject) => {
uni.writeBLECharacteristicValue({
deviceId,
serviceId: device.serviceId,
characteristicId: device.writeCharId,
value: buffer,
success: resolve,
fail: reject,
});
});
}
/**
* 断开指定设备
* @param {string} deviceId
* @returns {Promise}
*/
disconnectDevice(deviceId) {
if (!this.devices.has(deviceId)) return Promise.resolve();
return new Promise((resolve, reject) => {
uni.closeBLEConnection({
deviceId,
success: () => {
this.devices.delete(deviceId);
resolve();
},
fail: reject,
});
});
}
/**
* 断开所有已连接设备并关闭蓝牙适配器
*/
async closeAll() {
const disconnectPromises = Array.from(this.devices.keys()).map(id => this.disconnectDevice(id));
await Promise.all(disconnectPromises);
if (this.adapterOpen) {
return new Promise((resolve) => {
uni.closeBluetoothAdapter({
success: () => {
this.adapterOpen = false;
this.discovering = false;
resolve();
},
fail: () => {
this.adapterOpen = false;
resolve();
},
});
});
}
}
// ---------- 内部辅助方法 ----------
_delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
_getBLEDeviceServices(deviceId) {
return new Promise((resolve, reject) => {
uni.getBLEDeviceServices({
deviceId,
success: resolve,
fail: reject,
});
});
}
_getBLEDeviceCharacteristics(deviceId, serviceId) {
return new Promise((resolve, reject) => {
uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: resolve,
fail: reject,
});
});
}
_notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId, state = true) {
return new Promise((resolve, reject) => {
uni.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId,
state,
success: resolve,
fail: reject,
});
});
}
/**
* 仅连接设备并获取服务列表(不预设任何特征值)
* 用于设备配置阶段,让用户手动选择服务和特征值
* @param {string} deviceId
* @returns {Promise<{services: Array}>} 返回服务列表
*/
async connectForDiscovery(deviceId) {
if (!this.adapterOpen) {
throw new Error('蓝牙未初始化');
}
// 如果已连接,先断开
if (this.devices.has(deviceId)) {
await this.disconnectDevice(deviceId);
}
return new Promise((resolve, reject) => {
uni.createBLEConnection({
deviceId,
success: async () => {
await this._delay(800);
try {
const { services } = await this._getBLEDeviceServices(deviceId);
// 暂不存储到 devices Map,因为还没确定特征值
resolve({ services, deviceId });
} catch (err) {
reject(err);
}
},
fail: reject,
});
});
}
/**
* 保存用户选择的配置,并完成最终连接(启用通知等)
* @param {string} deviceId
* @param {string} serviceId
* @param {string} writeCharId
* @param {string} notifyCharId (可选)
* @returns {Promise}
*/
async saveAndConnect(deviceId, serviceId, writeCharId, notifyCharId = null) {
if (!this.adapterOpen) {
throw new Error('蓝牙未初始化');
}
// 启用通知(如果需要)
if (notifyCharId) {
await this._notifyBLECharacteristicValueChange(deviceId, serviceId, notifyCharId, true);
}
// 存储设备信息
this.devices.set(deviceId, {
serviceId: serviceId,
writeCharId: writeCharId,
notifyCharId: notifyCharId,
});
return { deviceId, serviceId, writeCharId, notifyCharId };
}
}
export default new BluetoothManager();