index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. import * as tools from "./tools"
  2. import {inArray, isObject, logs, sleep} from "../util";
  3. import {showToast} from "../app";
  4. import {checkBleAuth} from "../uniFunction";
  5. import {ab2hex, orderSplit, stringToArrayBuffer} from "./hex";
  6. import {isH5} from "@/utils/platform";
  7. /**
  8. * 蓝牙工具类
  9. * 封装小程序蓝牙流程方法
  10. * 处理事件通信
  11. */
  12. export class bleBase {
  13. constructor(topClass) {
  14. this.topClass = topClass
  15. this.typeKey = ''
  16. this.option = {}
  17. this.deviceId = null
  18. this.serviceId = null
  19. this.writeId = null // 写入id
  20. this.characteristicId = null// 接收id
  21. this.conSevInterval = 2000
  22. this.sList = []
  23. this.OSList = []
  24. }
  25. /**
  26. * 搜索蓝牙设备
  27. * @param data
  28. * @param op
  29. * @param showLoad
  30. * @returns {Promise<unknown>}
  31. */
  32. getList(data = {}, op = {}, showLoad = true) {
  33. let option = {
  34. second: 5000,
  35. type: 1,
  36. start: null,
  37. fun: null,
  38. fail: null,
  39. }
  40. option = Object.assign(option, op)
  41. return new Promise(async (resolve, reject) => {
  42. logs('进入 searchBle')
  43. try {
  44. await this.topClass.init()
  45. console.error('getList init')
  46. await checkBleAuth() // 检查用户蓝牙授权是否通过
  47. if (isH5) throw 'H5不支持蓝牙功能'
  48. if (showLoad) uni.showLoading({title: '搜索设备中。。。'})
  49. await tools._startSearch(Object.assign({
  50. allowDuplicatesKey: true,
  51. // services: [],
  52. // powerLevel: 'high',
  53. }, data))
  54. if (option.type == 3 && option.start) option.start()
  55. let list = await this.bluetoothDeviceFound(option.second, option.type, option.fun)
  56. if (showLoad) uni.hideLoading()
  57. resolve(Object.values(list))
  58. } catch (msg) {
  59. console.error('getList err', msg)
  60. if (showLoad) uni.hideLoading()
  61. showToast(msg, 4000)
  62. if (option.type == 3 && option.fail) {
  63. option.fail(msg)
  64. } else {
  65. reject()
  66. }
  67. }
  68. })
  69. }
  70. /**
  71. * 过滤并转换附加参数 ok 表示 是否通过过滤
  72. * @param item
  73. * @returns item
  74. */
  75. filter(item) {
  76. item.ok = false
  77. if (this.option.top) {
  78. let index = item.manufacturerData.search(this.option.top)
  79. if (this.option.indexJudge) item.ok = this.option.indexJudge(index)
  80. } else {
  81. item.ok = true
  82. }
  83. if (item.ok) {
  84. if (this.option.macSlice) item.mac = item.manufacturerData.slice(...this.option.macSlice)
  85. if (this.option.order) {
  86. let res = orderSplit(item.manufacturerData, this.option.order)
  87. for (const resKey in res) {
  88. item[resKey] = res[resKey]
  89. }
  90. }
  91. if (this.option.judge) item.ok = this.option.judge(item)
  92. }
  93. if (this.option.search && !inArray(item.mac, this.option.machine)) item.ok = false
  94. return item
  95. }
  96. /**
  97. * 过滤设备信息
  98. * @param second
  99. * @param type 1 普通模式(只过滤是否符合);
  100. * 2 指定模式(只有找到一个就会结束,但因为搜索未完全结束可能会返回多个的情况)
  101. * 3 无限模式
  102. * @param fun
  103. * @returns {Promise<unknown>}
  104. */
  105. bluetoothDeviceFound(second = 5000, type = 1, fun = null) {
  106. let list = {}
  107. return new Promise(async (resolve, reject) => {
  108. console.log('bleDeviceFound run')
  109. try {
  110. this.topClass.bleDeviceFound((res) => {
  111. res.devices.forEach((item) => {
  112. item.manufacturerData = ab2hex(item.advertisData)
  113. // console.log('res.devices.forEach', item)
  114. if (item.localName != '') {
  115. // console.log(`bleDeviceFound forEach localName:${item.localName} deviceId:${item.deviceId} manufacturerData:${item.manufacturerData} advertisData:${item.advertisData}`)
  116. // console.log('res.devices.forEach', item)
  117. }
  118. item = this.filter(item)
  119. // console.log('res.devices.forEach after', item)
  120. if (item.ok) {
  121. list[item.deviceId] = item
  122. if (type == 2) {
  123. // console.log('type2 stop')
  124. tools._stopSearchBluetooth();
  125. throw '已找到设备'
  126. } else if (type == 3 && fun) {
  127. fun({
  128. run: true,
  129. data: item,
  130. })
  131. }
  132. }
  133. })
  134. })
  135. if (second > 0) {
  136. await sleep(second)
  137. // console.log('sleep stop')
  138. throw '搜索结束'
  139. }
  140. } catch (error) {
  141. // console.log('catch stop')
  142. await tools._stopSearchBluetooth();
  143. if (type != 3) {
  144. } else if (type == 3 && fun) {
  145. fun({
  146. run: false,
  147. })
  148. }
  149. this.topClass.bleDeviceFound(null)
  150. return resolve(Object.values(list))
  151. }
  152. })
  153. }
  154. async startSearch(data) {
  155. await tools._startSearch(Object.assign({
  156. allowDuplicatesKey: true
  157. }, data))
  158. }
  159. stopSearch() {
  160. /* #ifndef H5 */
  161. // console.log('stopSearch stop')
  162. tools._stopSearchBluetooth();
  163. /* #endif */
  164. }
  165. // 二、 蓝牙连接 根据某一id连接设备,4.0
  166. async connectionBle(deviceId) {
  167. try {
  168. if (isH5) throw 'H5无法运行'
  169. if (this.deviceId && this.deviceId == deviceId) {
  170. logs('同一台设备,跳出')
  171. return Promise.resolve() // 已连接设备与当前为同一台
  172. }
  173. if (this.deviceId && this.deviceId != deviceId) {
  174. logs('有旧设备,关闭旧设备中')
  175. await this.closeConnection() // 结束前一台
  176. await sleep(500)
  177. }
  178. await checkBleAuth() // 检查用户蓝牙授权是否通过
  179. this.topClass.addConnect(deviceId, this.typeKey)
  180. this.deviceId = deviceId
  181. logs('connectBle --- ' + this.deviceId)
  182. await tools._connectBlue(this.deviceId)
  183. // await sleep(this.conSevInterval);
  184. this.serviceId = await this.getBLEServices(this.option.serviceKey)
  185. await sleep(500)
  186. const main = await this.getCharacteristics(this.option.channelKey)
  187. this.writeId = main.writeId // 写入id
  188. this.characteristicId = main.characteristicId // 写入id
  189. await tools._notifyBLECharacteristicValueChange(this.deviceId, this.serviceId, this.characteristicId)
  190. return Promise.resolve()
  191. } catch (e) {
  192. await this.closeConnection()
  193. console.error('connectionBle catch', e)
  194. return Promise.reject('连接失败')
  195. }
  196. }
  197. async getBLEServices_old(serviceKey) {
  198. if (!this.deviceId) console.error('获取服务错误,没有deviceId')
  199. let servicesList = await tools._getBLEServices(this.deviceId)
  200. if (!servicesList) throw '链接失败,蓝牙未找到服务号'
  201. logs('开始蓝牙获取服务', servicesList)
  202. this.sList = servicesList.services
  203. let uuid = null
  204. for (let i = 0; i < servicesList.services.length; i++) {
  205. if (servicesList.services[i].uuid.indexOf(serviceKey) != -1) {
  206. // this.serviceId = servicesList.services[i].uuid
  207. uuid = servicesList.services[i].uuid
  208. return uuid;
  209. }
  210. }
  211. return uuid;
  212. }
  213. async getBLEServices(serviceKey) {
  214. if (!this.deviceId) console.error('获取服务错误,没有deviceId');
  215. const timeout = new Promise((_, reject) => {
  216. setTimeout(() => {
  217. reject(new Error('超时,未能在5秒内获取到服务'));
  218. }, 5000);
  219. });
  220. const getService = async () => {
  221. let uuid = null;
  222. for (let i = 0; i < 5; i++) {
  223. try {
  224. const servicesList = await tools._getBLEServices(this.deviceId);
  225. if (servicesList) {
  226. logs('getService', servicesList);
  227. this.sList = servicesList.services;
  228. for (let j = 0; j < servicesList.services.length; j++) {
  229. if (servicesList.services[j].uuid.indexOf(serviceKey) !== -1) {
  230. uuid = servicesList.services[j].uuid;
  231. break; // 找到匹配的 uuid 后退出内层循环
  232. }
  233. }
  234. if (uuid) break; // 找到匹配的 uuid 后退出外层循环
  235. }
  236. } catch (error) {
  237. // 如果获取失败,继续下一次尝试
  238. console.error('获取服务失败,重试中...');
  239. }
  240. await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒后重试
  241. }
  242. if (uuid) {
  243. return uuid;
  244. } else {
  245. throw new Error('未能在5秒内获取到服务');
  246. }
  247. };
  248. try {
  249. const uuid = await Promise.race([getService(), timeout]);
  250. return uuid;
  251. } catch (error) {
  252. throw error;
  253. }
  254. }
  255. async getCharacteristics(channelKey = '', obj = {}) {
  256. logs('开始蓝牙通道信息')
  257. // 获取通道信息(读、写、监听)
  258. let writeKey = ''
  259. let notifyKey = ''
  260. if (isObject(channelKey)) {
  261. writeKey = channelKey.writeKey
  262. notifyKey = channelKey.notifyKey
  263. } else {
  264. writeKey = notifyKey = channelKey
  265. }
  266. let op = {
  267. deviceId: this.deviceId,
  268. serviceId: this.serviceId,
  269. ...obj
  270. }
  271. let {characteristics} = await tools._getCharacteristics(op.deviceId, op.serviceId)
  272. logs(`开始蓝牙通道信息 writeKey:${writeKey} notifyKey:${notifyKey} characteristics:${characteristics}`)
  273. let d = {
  274. writeId: null,
  275. characteristicId: null,
  276. }
  277. for (let i = 0; i < characteristics.length; i++) {
  278. let item = characteristics[i];
  279. if (item.uuid.search(writeKey) != -1) {
  280. d.writeId = item.uuid // 写入id
  281. }
  282. if (item.uuid.search(notifyKey) != -1) {
  283. d.characteristicId = item.uuid // 写入id
  284. }
  285. }
  286. logs(`开始蓝牙通道信息 writeId:${d.writeId} characteristicId:${d.characteristicId}`)
  287. return d
  288. }
  289. /**
  290. * 关闭设备连接
  291. * @param deviceId
  292. * @returns {Promise<void>}
  293. */
  294. async closeConnection(deviceId = null) {
  295. if (deviceId == null) deviceId = this.deviceId
  296. // 断开已连接的连接
  297. try {
  298. if (this.deviceId) await tools._closeBLEConnection(deviceId);
  299. } catch (e) {
  300. }
  301. this.deviceId = null
  302. this.serviceId = null
  303. this.writeId = null
  304. this.characteristicId = null
  305. this.sList = []
  306. this.OSList = []
  307. this.topClass.delConnect(deviceId)
  308. }
  309. async sentOrder(str, type = 1, obj = {}) {
  310. let buffer = type != 0 ? stringToArrayBuffer(str, type) : str
  311. logs("-- 发送数据: ", ab2hex(buffer, ' '))
  312. let op = {
  313. deviceId: this.deviceId,
  314. serviceId: this.serviceId,
  315. writeId: this.writeId,
  316. ...obj
  317. }
  318. try {
  319. /* #ifndef H5 */
  320. if (this.deviceId) await tools._writeBLECharacteristicValue(op.deviceId, op.serviceId, op.writeId, buffer)
  321. /* #endif */
  322. if (op.suc) op.suc()
  323. return true
  324. } catch (error) {
  325. if (error.errCode == 10006) {
  326. this.topClass.delConnect(this.deviceId, this.typeKey)
  327. }
  328. if (op.err) op.err()
  329. console.error(error)
  330. logs(`发送出错 基本信息 deviceId:${this.deviceId}, serviceId:${this.serviceId}, writeId:${this.writeId}`)
  331. logs(error)
  332. }
  333. }
  334. }