import receivTool from "@/utils/BleReceive.js" var recei = null; const serviceDic = [ //合作供应商的蓝牙主服务 { "serviceId": "0000FFE0-0000-1000-8000-00805F9B34FB", "writeId": "0000FFE1-0000-1000-8000-00805F9B34FB", "notifyId": "0000FFE2-0000-1000-8000-00805F9B34FB" }, { "serviceId": "0000AE30-0000-1000-8000-00805F9B34FB", "writeId": "0000AE03-0000-1000-8000-00805F9B34FB", "notifyId": "0000AE02-0000-1000-8000-00805F9B34FB" } ]; class BleHelper { constructor() { this.StorageKey = "linkedDevices"; recei = receivTool.getBleReceive(); this.init(); } init() { var store = uni.getStorageInfoSync(); var f = store.keys.includes(this.StorageKey); var linkedDevices = []; if (f) { linkedDevices = uni.getStorageSync(this.StorageKey); } if (linkedDevices && linkedDevices.length && linkedDevices.length > 0) { // console.log("111111", linkedDevices); linkedDevices = linkedDevices.filter((v) => { if (v) { v.Linked = false; v.notifyState = false; } return v ? true : false; }); } const systemInfo = uni.getSystemInfoSync(); setTimeout(() => { this.OpenBlue().then(() => { this.linkAllDevices(); }); }, 10); this.data = { isOpenBlue: false, //蓝牙模块是否开启 available: false, //蓝牙状态是否可用 discovering: false, //蓝牙是否正在搜索 searchList: [], //已搜索到的设备列表, isSubscribe: false, //是否开启了订阅 LinkedList: linkedDevices, //已连接的设备列表 platform: systemInfo.uniPlatform } this.cfg = { onDeviceFound: [], //发现新设备的事件 receivDataCallback: [], //接收到数据的事件 bleDisposeCallback: [], //蓝牙断开连接的事件 recoveryCallback: [], //蓝牙连接恢复的事件 stateRecoveryCallback: [], //蓝牙适配器恢复可用事件 stateBreakCallback: [] //蓝牙适配器不可用事件 } // this.addReceiveCallback((a, b, c) => { // recei.ReceiveData(a, b, c); // }, "BleReceiveData"); this.dic = { errRemarks: [{ key: '10000', remark: '未初始化蓝牙适配器' }, { key: '10001', remark: '当前蓝牙适配器不可用' }, { key: '10002', remark: '没有找到指定设备' }, { key: '10003', remark: '蓝牙设备连接失败' }, { key: '10004', remark: '蓝牙设备没有找到指定服务' }, { key: '10005', remark: '蓝牙设备没有找到指定特征值' }, { key: '10006', remark: '蓝牙连接已断开' }, { key: '10007', remark: '蓝牙设备当前特征值不支持此操作' }, { key: '10008', remark: '其余所有系统上报的异常' }, { key: '10009', remark: '蓝牙设备不支持BLE低功耗蓝牙' }, { key: '10010', remark: '已连接' }, { key: '10011', remark: '蓝牙设备配对设备需要配对码' }, { key: '10012', remark: '蓝牙设备连接超时' }, { key: '10013', remark: '连接 deviceId 为空或者是格式不正确' } ] } } //从缓存中删除某个设备,bleId蓝牙id,deviceId数据库中的设备id DropDevice(bleId, deviceId) { let flag = false; for (var i = 0; i < this.data.LinkedList.length; i++) { let item = this.data.LinkedList[i]; if (deviceId && item.device) { if (item.device.id == deviceId) { console.log("找到要删除的设备", item); this.data.LinkedList.splice(i, 1); this.disconnectDevice(item.deviceId); flag = true; break; } } else { if (bleId && item.deviceId == bleId) { console.log("找到要删除的设备1,", item) this.data.LinkedList.splice(i, 1); this.disconnectDevice(item.deviceId); flag = true; break; } } } if (flag) { this.updateCache(); } } //更新缓存 updateCache() { uni.setStorageSync(this.StorageKey, this.data.LinkedList); } //连接所有已连接过的设备 linkAllDevices() { // console.log("模块启动时,自动连接已连接过的设备", this.data.LinkedList); if (this.data.platform == 'web') { return; } if (this.data.LinkedList && this.data.LinkedList.length > 0) { let flag = false; for (var i = 0; i < this.data.LinkedList.length; i++) { let device = this.data.LinkedList[i]; // console.log("自动连接绑定过的设备:" + device.deviceId); if (device.macAddress && device.device && device.device.id) { this.LinkBlue(device.deviceId, device.writeServiceId, device.wirteCharactId, device .notifyCharactId); flag = true; } } if (!flag) { this.updateCache(); } } else { // console.log("蓝牙未连接过任何设备"); this.updateCache(); } } //获取当前页面栈 getCurrentPagePath() { const pages = getCurrentPages(); if (pages.length === 0) { return ""; } const currentPage = pages[pages.length - 1]; // console.log("currentPage=", currentPage.route); return currentPage.route; } //添加事件回调 addCallback(callback, currKey, type) { if (!type) { return; } let key = currKey ? currKey : this.getCurrentPagePath(); if (!key) { key = new Date().getTime(); } if (key) { // console.log("key=" + key); let f = this.cfg[type].findIndex((v) => { return v.key == key; }); if (f > -1) { this.cfg[type][f].callback = callback; return; } this.cfg[type].push({ key: key, callback: callback }); } } //移除事件回调 removeCallback(currKey, type) { if (!type) { return; } let key = currKey ? currKey : this.getCurrentPagePath(); if (key) { // console.log("key=" + key); let f = this.cfg[type].findIndex((v) => { return v.key == key; }); if (f > -1) { this.cfg[type].splice(f, 1); } } else { if (this.cfg[type].length > 0) { this.cfg[type].splice(this.cfg[type].length - 1, 1); } } } //设置蓝牙恢复连接的回调 addRecoveryCallback(callback, currKey) { this.addCallback(callback, currKey, 'recoveryCallback'); } //移除蓝牙恢复连接的回调 removeRecoveryCallback(currKey) { this.removeCallback(currKey, 'recoveryCallback'); } //设置蓝牙断开连接的回调 addDisposeCallback(callback, currKey) { this.addCallback(callback, currKey, 'bleDisposeCallback'); } //移除蓝牙断开连接的回调 removeDisposeCallback(currKey) { this.removeCallback(currKey, 'bleDisposeCallback'); } //设置发现新设备的回调 addDeviceFound(callback, currKey) { this.addCallback(callback, currKey, 'onDeviceFound'); } //移除发现新设备的回调 removeDeviceFound(currKey) { this.removeCallback(currKey, 'onDeviceFound'); } //添加接收到数据的回调 addReceiveCallback(callback, currKey) { this.addCallback(callback, currKey, 'receivDataCallback'); } //移除接收到数据的回调 removeReceiveCallback(currKey) { this.removeCallback(currKey, 'receivDataCallback'); } //添加蓝牙不可用的回调 addStateBreakCallback(callback, currKey) { this.addCallback(callback, currKey, 'stateBreakCallback'); } //移除蓝牙不可用的回调 removeStateBreakCallback(currKey) { this.removeCallback(currKey, 'stateBreakCallback'); } //设置蓝牙适配器恢复可用的回调 addStateRecoveryCallback(callback, currKey) { this.addCallback(callback, currKey, 'stateRecoveryCallback'); } //移除蓝牙适配器恢复的回调 removeStateRecoveryCallback(currKey) { this.removeCallback(currKey, 'stateRecoveryCallback'); } //清除所有事件回调 removeAllCallback(currKey) { this.removeDeviceFound(currKey) this.removeDisposeCallback(currKey); this.removeReceiveCallback(currKey); this.removeRecoveryCallback(currKey); this.removeStateRecoveryCallback(currKey); this.removeStateBreakCallback(currKey); } getError(ex) { let code = ex.code; ex.msg = "未知的异常" let f = this.dic.errRemarks.find((v) => { return v.key == code; }); if (f) { ex.msg = f.remark; } return ex; } //引导到设置中打开蓝牙 showBlueSetting(showTip) { let platform = process.env.UNI_PLATFORM; var openBlueSetting = function() { // 判断平台类型 if (platform === 'mp-weixin') { uni.openSetting(); } else if (platform === 'app-plus' || platform === 'app') { //---------------------------------------------------------------- const osName = plus.os.name; if (osName === 'iOS') { // iOS 平台打开蓝牙设置 plus.runtime.openURL('App-Prefs:root=Bluetooth', function() { ////console.log('成功打开蓝牙设置'); }, function(err) { console.error('打开蓝牙设置失败:' + err.message); uni.showModal({ title: '提示', content: '无法自动打开蓝牙设置,请手动前往设置 > 蓝牙 进行操作。', showCancel: false }); }); } else if (osName === 'Android') { // Android 平台打开蓝牙设置 try { const Intent = plus.android.importClass('android.content.Intent'); const Settings = plus.android.importClass('android.provider.Settings'); const main = plus.android.runtimeMainActivity(); const intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); main.startActivity(intent); } catch (e) { console.error('打开蓝牙设置失败:' + e.message); // 尝试使用通用设置页面作为备选方案 plus.runtime.openURL('settings://', function() { ////console.log('打开系统设置成功,请手动找到蓝牙选项'); }, function() { uni.showModal({ title: '提示', content: '无法自动打开蓝牙设置,请手动前往设置页面开启蓝牙。', showCancel: false }); }); } } else { uni.showModal({ title: '提示', content: '当前系统不支持自动打开蓝牙设置,请手动操作。', showCancel: false }); } //-------------------------------------------------------------------- } else if (platform === 'mp-alipay') { uni.openSetting(); } } if (showTip !== undefined) { openBlueSetting(); return; } if (platform === 'mp-weixin' || platform === 'app-plus' || platform === 'mp-alipay' || platform === 'app') { uni.showModal({ title: '蓝牙未开启', content: '请在系统设置中打开蓝牙以使用此功能', success: (res) => { if (res.confirm) { openBlueSetting(); } } }); } else { ////console.log("当前平台不支持打开系统设置" + platform); } } //获取蓝牙适配器状态 CheckBlue() { if (this.data.platform == 'web') { return Promise.resolve({ available: true, discovering: false }); } return new Promise((resolve, reject) => { if (this.data.available) { // console.log("蓝牙模块是可用状态"); resolve({ available: this.data.available, discovering: this.data.discovering }); return; } if (!this.data.isOpenBlue) { console.error("蓝牙模块未打开"); resolve({ available: false, discovering: false }); return; } uni.getBluetoothAdapterState({ success: (info) => { // console.log("蓝牙状态获取成功,", info) this.data.available = info.available; this.data.discovering = info.discovering; resolve(info); }, fail: (ex1) => { console.error("蓝牙状态获取失败", ex1); let res1 = { available: false, discovering: false } resolve(res1); } }); }); } //打开蓝牙适配器 OpenBlue() { if (this.data.platform == 'web') { this.data.isOpenBlue = true; this.data.available = true; this.data.isSubscribe = true; return Promise.resolve(true); } var init = () => { return new Promise((resolve, reject) => { if (this.data.isOpenBlue) { // console.log("蓝牙状态已可用,提前返回"); resolve(false); return; } uni.openBluetoothAdapter({ success: (args) => { // console.log("蓝牙初始化成功:" + JSON.stringify(args)); this.data.isOpenBlue = true; this.data.available = true; resolve(true); if (this.data.isSubscribe) { //整个App生命周期,只订阅一次 return; } console.log("开始订阅各类变化消息"); this.data.isSubscribe = true; // 保存每个设备的连接状态 let bleDeviceStates = {}; var bytesToHexString = (bytes) => { return bytes.map(byte => byte.toString(16).padStart(2, '0')); } uni.onBluetoothAdapterStateChange((state) => { // console.log('蓝牙状态发生变化:' + JSON.stringify(state)); this.data.discovering = state.discovering; if (this.data.available !== state.available) { this.data.available = state.available; if (this.data.available && this.data .isOpenBlue) { //蓝牙状态再次可用,重连所有设备 if (this.cfg.stateRecoveryCallback.length > 0) { this.cfg.stateRecoveryCallback.forEach( c => { try { c.callback(); } catch (error) { console.error( "蓝牙适配器已恢复,但回调函数发生错误", error); } }) } } } if (!state.available) { //蓝牙状态不可用了,将所有设备标记为断开连接 this.data.LinkedList.filter((v) => { v.Linked = false; v.notifyState = false; return true; }); let keys = Object.keys(bleDeviceStates) keys.filter(v => { bleDeviceStates[v] = false; }); this.cfg.stateBreakCallback.forEach(f => { try { f.callback(); } catch (error) { console.error("蓝牙状态不可用了,执行回调异常", error) } }) this.updateCache(); } }); uni.onBLEConnectionStateChange((res) => { console.log("蓝牙连接状态变化了", res); // 检查状态是否真的发生了变化 if (bleDeviceStates[res.deviceId] === res.connected) { console.error('专业给Uniapp填坑,连接状态重复回调'); return; } // 更新状态记录 bleDeviceStates[res.deviceId] = res.connected; setTimeout(() => { if (!res.connected) { console.error("蓝牙已断开", res); let f = this.data.LinkedList.find(( v) => { if (v.deviceId == res .deviceId) { v.Linked = false; v.notifyState = false; return true; } return false; }); this.updateCache(); if (f && f.device && f.device.id) { console.log("尝试5次恢复连接,", f .deviceId); this.LinkBlue(res.deviceId, f .writeServiceId, f .wirteCharactId, f .notifyCharactId, 5); } if (this.cfg.bleDisposeCallback.length > 0) { this.cfg.bleDisposeCallback.forEach( (c) => { try { c.callback(res); } catch (error) { console.error( "执行蓝牙断开连接的回调出现异常,", error) } }); } } else { console.log("蓝牙连接已恢复,", res); if (this.cfg.recoveryCallback.length > 0) { this.cfg.recoveryCallback.forEach(( c) => { try { c.callback(res); } catch (error) { console.error( "执行蓝牙恢复连接的回调出现异常,", error) } }); } } }, 0); }); uni.onBluetoothDeviceFound((res) => { // console.log("发现新设备:" + JSON.stringify(devices)); let arr = []; for (var i = 0; i < res.devices.length; i++) { let item = res.devices[i]; let f = serviceDic.find(v => { return item.advertisServiceUUIDs .includes(v.serviceId); }); if (f) { console.log("发现新设备:", item); arr.push(item); } } if (arr.length === 0) { // console.error("发现了设备,但不是想要的设备", res); return; } res.devices = arr; this.data.searchList = this.data.searchList.concat( res); if (this.cfg.onDeviceFound) { if (this.cfg.onDeviceFound.length > 0) { this.cfg.onDeviceFound.forEach((found) => { try { found.callback(res); } catch (error) { console.error("处理发现设备的回调出现异常,", error); } }); } } }); uni.onBLECharacteristicValueChange((receive) => { //订阅消息 console.log("收到订阅消息", receive); let f = this.data.LinkedList.find((v) => { return v.deviceId == receive.deviceId; }) let dataView = new DataView(receive.value); // 转换为字节数组 let bytes = []; for (let i = 0; i < dataView.byteLength; i++) { bytes.push(dataView.getUint8(i)); } let hexs = bytesToHexString(bytes); let str = ''; try { let uint8Array = new Uint8Array(receive.value); for (let i = 0; i < uint8Array.length; i++) { // 将每个字节转换为对应的字符 str += String.fromCharCode(uint8Array[i]); } let header = "mac address:"; let isUpdate = false; if (str.indexOf(header) == 0) { //650以文本传输mac console.log("str=", str); this.data.LinkedList.find((v) => { if (v.deviceId == receive .deviceId) { let macStr = str.replace( header, ""); if (macStr.includes(':')) { v.macAddress = macStr; } else { v.macAddress = macStr .replace( /(.{2})/g, '$1:') .slice(0, -1) } str = header + v.macAddress; console.log("收到mac地址:", v .macAddress) } }); isUpdate = true; } if (str.indexOf('imei:') == 0) { //670以此方式传输imei let imei = str.split(':')[1]; this.data.LinkedList.find((v) => { if (v.deviceId == receive .deviceId) { v.imei = imei; isUpdate = true; } }); } if (bytes[0] == 0xFC) { //6155以0xFC开头代表mac地址 console.log("收到mac地址:", bytes) if (bytes.length >= 7) { console.log("hexs=", hexs); let mac = hexs.slice(1, 7).join(":") .toUpperCase(); this.data.LinkedList.find((v) => { if (v.deviceId == receive .deviceId) { v.macAddress = mac; isUpdate = true; } }); } } try { let receivJson = JSON.parse(str); let key = "sta_address"; //HBY100以此方式上传mac地址 if (key in receivJson) { this.data.LinkedList.find((v) => { if (v.deviceId == receive .deviceId) { let macStr = receivJson[ key]; if (macStr.includes(':')) { v.macAddress = macStr; } else { v.macAddress = macStr .replace(/(.{2})/g, '$1:').slice(0, -1) } isUpdate = true; } }); } } catch (convertException) { console.error("文本无法转json", convertException) } if (isUpdate) { this.updateCache(); } // console.log("str1=", str); } catch (ex) { console.error("将数据转文本失败", ex); } try { // console.log("11111"); let recData = { deviceId: receive.deviceId, serviceId: receive.serviceId, characteristicId: receive.characteristicId, bytes: bytes, str: str, hexs: hexs }; // console.log("监听到特征值:" + JSON.stringify(recData)); if (this.cfg.receivDataCallback) { if (this.cfg.receivDataCallback.length > 0) { let path = this.getCurrentPagePath(); // console.log("有人订阅消息") this.cfg.receivDataCallback.forEach(( rec) => { // console.log("有人订阅消息111", ) if (rec.callback) { try { rec.callback(recData, f, path, this.cfg .receivDataCallback ); } catch (err) { console.error( "订阅消息出现异常", err); } } }) } else { console.log("无人订阅消息"); } } else { console.log("无人订阅receivDataCallback,不处理数据"); } } catch (ex) { console.error("处理订阅消息失败,ex=", ex); } }); }, fail: (ex2) => { console.error("蓝牙模块启动失败", ex2); reject(this.getError(ex2)); } }); }); } return new Promise((resolve, reject) => { if (this.data.isOpenBlue) { resolve(); return; } this.CheckBlue().then((res) => { // console.log("res=", res) return init(); }).then(resolve).catch((ex) => { console.error("异常:", ex); reject(ex); }); }); } //关闭蓝牙适配器 CloseBlue() { if (this.data.platform == 'web') { this.data.available = false; this.data.discovering = false; this.data.isOpenBlue = false; this.data.searchList = []; return Promise.resolve(); } return new Promise((resolve, reject) => { this.StopSearch(); this.disconnectDevice(); uni.closeBluetoothAdapter({ success: () => { // console.log("蓝牙模块已关闭"); this.data.available = false; this.data.discovering = false; this.data.isOpenBlue = false; this.data.searchList = []; resolve(); }, fail: (ex) => { console.error('无法关闭蓝牙模块:', ex); ex = this.getError(ex); reject(ex); }, complete: () => { } }); }); } //开始搜索新设备 StartSearch() { if (this.data.platform == 'web') { return Promise.resolve({}); } this.data.searchList = []; var these = this; //开始搜索 var Search = () => { //只搜索合作供应商的服务id let serviceIds = serviceDic.map(v => { return v.serviceId }); //搜索一个服务id的设备,循环调用 let RunSearch = (serviceId) => { return new Promise((resolve, reject) => { uni.startBluetoothDevicesDiscovery({ services: serviceId ? [serviceId] : [], allowDuplicatesKey: true, success: (res) => { //console.log('开始搜索蓝牙设备成功'); resolve(res); }, fail: (err) => { console.error(`搜索蓝牙设备失败:`, err); reject(this.getError(err)); } }); }); } return new Promise((resolve, reject) => { let promises = []; // for (let i = 0; i < serviceIds.length; i++) { // promises.push(RunSearch(serviceIds[i])); // } promises.push(RunSearch()); Promise.all(promises).then(resolve).catch(reject); }); } return this.OpenBlue().then((res) => { // console.log("蓝牙适配器状态", res) return Search(); }); } //停止搜索 StopSearch() { if (this.data.platform == 'web' || !this.data.discovering) { console.error("web平台或当前没有搜索,直接返回"); return Promise.resolve(); } let p1 = new Promise((resolve, reject) => { uni.stopBluetoothDevicesDiscovery({ success: (res) => { // console.log("停止搜索蓝牙设备成功"); resolve(); }, fail: (ex) => { console.error("无法停止蓝牙搜索"); reject(this.getError(ex)); } }); }); let p2 = new Promise((succ, err) => { setTimeout(() => { err({ code: -1 }); }, 100); }); return new Promise((resolve, reject) => { Promise.race([p1, p2]).then(resolve).catch(ex => { if (ex.code == -1) { console.error('专业给Uniapp填坑,停止搜索永无回调处理'); resolve(); return; } reject(ex); }); }); } //获取已连接的设备 getLinkBlue(callback) { return new Promise((resolve, reject) => { uni.getConnectedBluetoothDevices({ success: (res) => { resolve(res); }, fail: (ex) => { ////console.log("获取已连接设备异常", ex); reject({ devices: [] }); } }); }); } //订阅消息 subScribe(deviceId, state) { // console.log("开始订阅消息", state); return new Promise((resolve, reject) => { setTimeout(() => { let c = this.data.LinkedList.find((v) => { return v.deviceId == deviceId; }); if (!deviceId) { console.error('deviceId无效') reject({ code: 404, msg: 'deviceId无效' }); return; } if (!c) { console.error("已找不到该设备"); reject({ code: 404, msg: '已找不到该设备' }); return; } if (state) { if (c.notifyState) { resolve(); return; } } let startSubScribe = (id, serviceId, characteristicId) => { // console.log("serviceId=", serviceId); // console.log("characteristicId=", characteristicId); let p1 = new Promise((succ, err) => { uni.notifyBLECharacteristicValueChange({ deviceId: id, serviceId: serviceId, characteristicId: characteristicId, state: state, success: (res) => { if (state) { // console.log("订阅消息成功", res); } else { // console.log("取消订阅成功", res); } this.data.LinkedList.find((v) => { if (v.deviceId == deviceId) { v.notifyState = state; } }); // console.log("success SubScribe"); succ( res ); //见了鬼了,有时候执行了succ但promise永远pending了 }, fail: (ex) => { err(this.getError(ex)); } }); }); let p2 = new Promise((succ, err) => { setTimeout(() => { err({ code: -1 }); }, 50); }); return new Promise((succ, err) => { Promise.race([p1, p2]).then(succ).catch(ex => { // console.error("异常了:",ex); if (ex.code == -1) { console.error('专业给Uniapp填坑,订阅消息永无回调了'); succ(); return; } err(ex); }) }); } let serv = serviceDic.find(v => { return v.serviceId == c.writeServiceId; }); let promies = new Array(); if (c.notifyServiceid && c.notifyCharactId) { promies.push(startSubScribe(c.deviceId, c.notifyServiceid, c.notifyCharactId)); } else if (serv) { promies.push(startSubScribe(c.deviceId, serv.serviceId, serv.notifyId)); } else if (c.Characteristics && c.Characteristics.length) { for (var i = 0; i < c.Characteristics.length; i++) { let item = c.Characteristics[i]; let serviceId = item.serviceId; let characteristicId = item.uuid; if (item.properties.notify) { promies.push(startSubScribe(deviceId, serviceId, characteristicId)); } } } else { // console.log("c=", c); // console.log("serv=", serv); reject({ code: 403, msg: "出现错误,找不到任何通知特征" }); return; } if (promies.length > 0) { Promise.allSettled(promies).then((results) => { results.forEach((result, index) => { if (result.status === "fulfilled") { // console.log(`操作${index + 1}成功:`, result.value); } else { // console.log(`操作${index + 1}失败:`, result.reason // .message); } }); // console.log("订阅消息成功"); resolve(); }).catch((ex) => { console.error("异常,ex=", ex); reject(ex); }).finally(() => { // console.log("finally") }); } else { console.error("没有特征需要订阅"); resolve(); } }, 800); }); } //获取服务 getService(id, targetServiceId, writeCharId, notifyCharId) { if (!targetServiceId) { targetServiceId = "xx"; // "FFE0"; } return new Promise((resolve, reject) => { var repeatCnt = 0; var startgetService = () => { uni.getBLEDeviceServices({ deviceId: id, success: (res) => { if (res.services && res.services.length > 0) { // console.log("获取到服务:" + JSON.stringify(res)); this.data.LinkedList.find((v) => { if (v.deviceId == id) { v.services = res.services; } }); //this.updateCache(); var promises = []; let s = null; let se = res.services.find((v) => { s = serviceDic.find(k => { return k.serviceId == v.uuid; }) if (s) { return true; } return false; // return v.uuid.indexOf(targetServiceId) > -1; }); if (se) { // console.log("合作供应商的", s) this.data.LinkedList.find((v) => { if (v.deviceId == id) { v.writeServiceId = s.serviceId; v.wirteCharactId = s.writeId; v.notifyServiceid = s.serviceId; v.notifyCharactId = s.notifyId; } }); this.updateCache(); //合作供应商的设备,直接订阅消息,不走发现特征了 promises.push(this.subScribe(id, true)); // promises.push(this.getFeatrus(id, se.uuid, writeCharId, // notifyCharId)); } else { console.error("预设的蓝牙服务和特征中找不到"); for (var i = 0; i < res.services.length; i++) { let service = res.services[i]; promises.push(this.getFeatrus(id, service.uuid, writeCharId, notifyCharId)); } } if (promises.length == 0) { console.error("未找到主服务"); reject({ msg: "未找到主服务", code: -1 }); } else { Promise.all(promises) .then(results => { if (!s) { //非指定供应商的设备,走订阅消息 return this.subScribe(id, true); } else { //指定供应商的设备已经订阅过了 return Promise.resolve(); } }) .then((res) => { // console.log('设备连接成功,初始化完成', this.data // .LinkedList); resolve(); }) .catch(error => { console.error("errrr=", error); reject(error); }); } } else { repeatCnt++; if (repeatCnt > 5) { reject({ msg: "获取设备服务失败", code: -1 }); return; } setTimeout(function() { startgetService(id); }, 100 + repeatCnt * 300); } }, fail: (ex) => { reject(this.getError(ex)); } }) } setTimeout(function() { startgetService(id); }, 100); }); } //获取特征 getFeatrus(id, serviceId, writeCharId, notifyCharId) { var promise = new Promise((resolve, reject) => { uni.getBLEDeviceCharacteristics({ deviceId: id, serviceId: serviceId, success: (res) => { // console.log("获取到特征:" + JSON.stringify(res)); // res.characteristics.forEach((v) => { // v.serviceId = serviceId; // }); //写特征 let writeChar = res.characteristics.find(char => { let fe = serviceDic.find(v => { return v.serviceId == serviceId && v.writeId == char .uuid; }) // return char.uuid.indexOf(writeCharId) > -1 if (fe) { return true; } return false; }); // if (!writeChar) { // writeChar = res.characteristics.find(char => { // return char.properties.write; // }); // } //通知特征 let notiChar = res.characteristics.find(char => { let fe = serviceDic.find(v => { return v.serviceId == serviceId && v.notifyId == char.uuid; }) // return char.uuid.indexOf(writeCharId) > -1 if (fe) { return true; } return false; // return char.uuid.indexOf(notifyCharId) > -1; }); // if (!notiChar) { // notiChar = res.characteristics.find(char => { // return char.properties.notify; // }); // } this.data.LinkedList.find(function(v) { if (v.deviceId == id) { if (!v.Characteristics) { v.Characteristics = []; } for (var i = 0; i < res.characteristics.length; i++) { let item = res.characteristics[i]; let flag = false; for (var j = 0; j < v.Characteristics.length; j++) { let charact = v.Characteristics[j]; if (charact.uuid == item.uuid) { flag = true; break; } } if (!flag) { v.Characteristics.push(item); } } if (writeChar) { v.writeServiceId = serviceId; v.wirteCharactId = writeChar.uuid; } if (notiChar) { v.notifyServiceid = serviceId; v.notifyCharactId = notiChar.uuid; } return true; } return false; }); this.updateCache(); resolve(res); }, fail: (ex) => { ////console.log("获取特征出现异常:" + JSON.stringify(ex)); reject(ex); } }); }); return promise; } //连接某个设备 LinkBlue(deviceId, targetServiceId, writeCharId, notifyCharId, maxRetries) { if (this.data.platform == 'web') { return Promise.resolve(true); } if (maxRetries === undefined) { maxRetries = 0; // 最大重试次数 } if (!writeCharId) { writeCharId = "xxxx"; // "FFE1"; } if (!notifyCharId) { notifyCharId = "xxxxx"; // "FFE2"; } let fIndex = -1; let f = this.data.LinkedList.find((v, index) => { if (v.deviceId == deviceId) { fIndex = index; return true; } return false; }); ////console.log("findex=" + fIndex); var these = this; //连接设备 var linkDevice = () => { // 添加重试次数限制 let retryCount = 0; const connect = () => { return new Promise((resolve, reject) => { if (fIndex > -1 && f?.Linked) { // console.log("当前已连接,跳过其他步骤"); resolve(false); return; } if (!this.data.available) { reject(this.getError({ code: 10001 })); return; } // console.log("正在连接" + deviceId); uni.createBLEConnection({ deviceId: deviceId, timeout: 30000, success: (info) => { // console.log("新连接成功", this.data.LinkedList); this.getLinkBlue().then((arr) => { let cr = arr.devices.find(c => { if (c.deviceId == deviceId) { c.Linked = true; return true; } return false; }); if (fIndex > -1) { this.data.LinkedList[fIndex].Linked = true; } else { this.data.LinkedList.push(cr); } this.updateCache(); // console.log("LinkedList=", this.data // .LinkedList); // 处理 MTU 设置 if (plus.os.name === 'Android') { this.setMtu(deviceId).catch(ex=>{ console.error("mtu设置失败=",ex); }); } resolve(true); }).catch((ex) => { reject(this.getError(ex)); }); }, fail: (ex) => { ex = this.getError(ex); console.error("蓝牙" + deviceId + "连接失败" + JSON.stringify( ex)); // 连接超时后自动重试 if (ex.code === 10012 && retryCount < maxRetries) { retryCount++; console.log(`重试连接 (${retryCount}/${maxRetries})`); // 使用 setTimeout 避免递归调用栈溢出 setTimeout(() => { connect().then(resolve).catch(reject); }, 1000); // 延迟1秒后重试 } else { reject(ex); } } }); }); }; return connect(); }; return new Promise((resolve, reject) => { this.OpenBlue().then((res) => { ////console.log("2222222"); return linkDevice(deviceId); }).then((res) => { if (res) { //新连接 // console.log("11111111"); if (fIndex == -1) { // console.log("开始获取服务", targetServiceId) return this.getService(deviceId, targetServiceId, writeCharId, notifyCharId); //获取服务 } else { if (f.wirteCharactId && f.notifyCharactId) { if (!f.notifyState) { // console.log("开始订阅特征"); this.subScribe(deviceId, true); } else { console.log("不订阅消息"); } return Promise.resolve(true); } else { console.log("开始获取服务", targetServiceId) return this.getService(deviceId, targetServiceId, writeCharId, notifyCharId); } } } else { //已连接过,直接订阅消息 // console.log("11111111"); if (fIndex > -1 && f && !f.notifyState) { this.subScribe(deviceId, true); } return Promise.resolve(true); //已连接过的设备无需获取服务 } }).then(() => { // console.log("then....."); setTimeout(() => { resolve(); }, 500); }).catch((ex) => { console.error("出现异常", ex); reject(ex); }); }); } setMtu(deviceId) { let prom = new Promise((_succ, _err) => { uni.setBLEMTU({ deviceId: deviceId, mtu: 512, success: (mtuRes) => { console.log("mtu约定成功"); _succ(mtuRes); }, fail: (ex) => { ex = this.getError(ex); _err( ex ); }, complete() { console.log("设置mtu完毕"); } }); }); return prom; } //断开连接 disconnectDevice(deviceId) { if (this.data.platform == 'web') { return Promise.resolve(); } var disconnect = (id) => { return new Promise((resolve, reject) => { uni.closeBLEConnection({ deviceId: id, success: (res) => { console.log("用户主动断开了蓝牙:" + id); this.subScribe(id, false); resolve(); }, fail: (ex) => { console.error("无法断开蓝牙连接", ex); reject(this.getError(ex)); } }); }); } if (deviceId) { return disconnect(deviceId); } if (this.data.LinkedList.length > 0) { let arr = []; for (var i = 0; i < this.data.LinkedList.length; i++) { let item = this.data.LinkedList[i]; if (item.Linked) { arr.push(disconnect(item.deviceId)); } } if (arr.length) { return new Promise((_succ, _err) => { Promise.allSettled(arr).then((results) => { results.forEach((result, index) => { if (result.status === 'fulfilled') { ////console.log(`第${index + 1}个Promise成功:`, result.value); } else { ////console.log(`第${index + 1}个Promise失败:`, result.reason.message); } }); _succ() }).catch((ex) => { _err(); }); }); } } return Promise.resolve(); } //向蓝牙设备发送一个字符串的ASCII码 sendString(deviceid, str, writeServiceId, wirteCharactId, ms) { if (str && typeof(str) == 'object') { str = JSON.stringify(str); } if (str) { let buffer = new ArrayBuffer(str.length); let dataView = new DataView(buffer); // 2. 将字符串转换为 ASCII 码并写入 DataView for (let i = 0; i < str.length; i++) { dataView.setUint8(i, str.charCodeAt(i)); } return this.sendData(deviceid, buffer, writeServiceId, wirteCharactId, ms); } else { return Promise.resolve({ code: 200, msg: "没有数据要发送" }) } } //向蓝牙设备发送一个16进制的数组数据 sendHexs(deviceid, array, writeServiceId, wirteCharactId, ms) { if (array && array.length) { let bufferSize = array.length; let buffer = new ArrayBuffer(bufferSize); let dataView = new DataView(buffer); for (let i = 0; i < array.length; i++) { dataView.setUint8(i, array); } return this.sendData(deviceid, buffer, writeServiceId, wirteCharactId, ms); } else { return Promise.resolve({ code: 200, msg: "没有数据要发送" }) } } //向蓝牙设备发送数据,如果没连接将自动连接后再发 sendData(deviceid, buffer, writeServiceId, wirteCharactId, ms) { if (this.data.platform == 'web') { return Promise.resolve("h5平台默认成功"); } // console.log("deviceid=" + deviceid + ",writeServiceId=" + writeServiceId + ",wirteCharactId=" + // wirteCharactId + ",timeout=" + ms) if (ms === undefined) { ms = 50; } let c = this.data.LinkedList.find((v) => { return v.deviceId == deviceid; }); let device = { deviceId: deviceid, writeServiceId: null, wirteCharactId: null }; if (writeServiceId) { device.writeServiceId = writeServiceId; } if (wirteCharactId) { device.wirteCharactId = wirteCharactId; } if (!device.writeServiceId || !device.wirteCharactId) { //console.log("从已连接中找设备") if (c) { device = c; } } let sendBuffer = () => { return new Promise(async (resolve, reject) => { var promise = new Promise((succ, err) => { if (!c) { err(this.getError({ code: 10002 })); //没有找到指定设备 return; } // console.log("device=", device); uni.writeBLECharacteristicValue({ deviceId: device.deviceId, serviceId: device.writeServiceId, characteristicId: device.wirteCharactId, value: buffer, success: () => { // console.log("发送数据成功"); succ(); }, fail: (ex) => { ex = this.getError(ex); console.error("发送数据失败", ex); err(ex); } }); }); if (plus.os.name == 'iOS') { function timeOut(ms) { return new Promise((_, err) => { setTimeout(() => { err({ code: -1, errMsg: '超时了' }) }, ms); }); } Promise.race([timeOut(ms), promise]).then(resolve).catch((ex) => { // console.error("ex=", ex); if (ex.code == -1) { console.error('专业给Uniapp填坑,发送消息永无回调'); resolve(ex); } else { reject(ex); } }).finally(() => { //console.log("完成了") }); } else { promise.then(resolve).catch(reject); } }); } if (c.Linked) { // console.log("蓝牙已连接,直接发送"); return sendBuffer(); } else { // console.log("先连接蓝牙再发送"); return new Promise((resolve, reject) => { let f = this.data.LinkedList.find((v) => { return v.deviceId == deviceid; }); if (!f) { reject({ code: '-9', msg: '蓝牙未连接过此设备,请重新使用蓝牙添加该设备' }); retrn; } this.LinkBlue(f.deviceId, f.writeServiceId, f.wirteCharactId, f.notifyCharactId).then(( res) => { console.log("连接成功"); return sendBuffer(); }).then(() => { //console.log("发送成功") resolve(); }).catch((ex) => { console.error("出现异常", ex); reject(this.getError(ex)); }); }); } } //将点阵数据转换成RGB565 convertToRGB565(pixels, type) { if (!type) { type = 'rgb'; } const result = new Uint16Array(pixels.length / 4); let index = 0; for (let i = 0; i < pixels.length; i += 4) { let r = pixels[i]; let g = pixels[i + 1]; let b = pixels[i + 2]; let a = pixels[i + 3]; if (type == 'bgr') { result[index++] = ((b & 0xF8) << 8) | ((g & 0xFC) << 3) | (r >> 3); } else { result[index++] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); } } return result; } } let instance = null; export default { getBleTool: function(found, receive) { if (!instance) { instance = new BleHelper(); } else { ////console.log("调用现有实例"); } return instance; } }