ecBLEALI.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. const logEnable = false
  2. let isAndroid = false
  3. let ecBluetoothAdapterStateChangeCallback = () => { }
  4. let ecBLEConnectionStateChangeCallback = () => { }
  5. let ecDeviceId = ''
  6. let ecGattServerUUID = ''
  7. const ecGattServerUUIDOption1 = '0000FFF0-0000-1000-8000-00805F9B34FB'
  8. const ecGattServerUUIDOption2 = 'FFF0'
  9. let ecGattCharacteristicWriteUUID = ''
  10. const ecGattCharacteristicWriteUUIDOption1 = '0000FFF2-0000-1000-8000-00805F9B34FB'
  11. const ecGattCharacteristicWriteUUIDOption2 = 'FFF2'
  12. const log = data => {
  13. if (logEnable) {
  14. console.log('[eciot]:' + JSON.stringify(data))
  15. }
  16. }
  17. const onBluetoothAdapterStateChange = cb => {
  18. ecBluetoothAdapterStateChangeCallback = cb
  19. }
  20. const getSetting = () => {
  21. return new Promise(function (resolve, reject) {
  22. my.getSetting({
  23. success(res) {
  24. log(res)
  25. if (res.authSetting && res.authSetting.bluetooth) {
  26. resolve({ ok: true, errCode: 0, errMsg: '' })
  27. } else {
  28. resolve({
  29. ok: false,
  30. errCode: 30001,
  31. errMsg: 'getSetting fail',
  32. })
  33. }
  34. },
  35. fail(res) {
  36. log(res)
  37. resolve({
  38. ok: false,
  39. errCode: res.error ? res.error : 30000,
  40. errMsg: res.errorMessage ? res.errorMessage : 'getSetting fail',
  41. })
  42. },
  43. })
  44. })
  45. }
  46. const authorize = async () => {
  47. // return new Promise(function (resolve, reject) {
  48. // my.showBLEPermissionGuide({
  49. // success(res) {
  50. // log(res)
  51. // resolve({ ok: true, errCode: 0, errMsg: '' })
  52. // },
  53. // fail(res) {
  54. // log(res)
  55. // resolve({ ok: false, errCode: 30000, errMsg: res.errorMessage })
  56. // },
  57. // })
  58. // })
  59. return await _openBluetoothAdapter()
  60. }
  61. const _openBluetoothAdapter = () => {
  62. return new Promise(function (resolve, reject) {
  63. my.openBluetoothAdapter({
  64. success(res) {
  65. log(res)
  66. if (res.isSupportBLE) {
  67. resolve({ ok: true, errCode: 0, errMsg: '' })
  68. } else {
  69. resolve({ ok: false, errCode: 30001, errMsg: 'isSupportBLE is false' })
  70. }
  71. },
  72. fail(res) {
  73. log(res)
  74. resolve({
  75. ok: false,
  76. errCode: res.error ? res.error : 30000,
  77. errMsg: res.errorMessage,
  78. })
  79. },
  80. })
  81. })
  82. }
  83. const openBluetoothAdapter = async () => {
  84. await _openBluetoothAdapter()
  85. const systemInfo = my.getSystemInfoSync()
  86. log(systemInfo)
  87. if (systemInfo.platform.toLowerCase() === 'android') {
  88. isAndroid = true
  89. }
  90. if (isAndroid && !systemInfo.bluetoothEnabled) {
  91. ecBluetoothAdapterStateChangeCallback({
  92. ok: false,
  93. errCode: 30001,
  94. errMsg: '请打开系统蓝牙开关',
  95. })
  96. return
  97. }
  98. if (isAndroid && !systemInfo.locationEnabled) {
  99. ecBluetoothAdapterStateChangeCallback({
  100. ok: false,
  101. errCode: 30002,
  102. errMsg: '请打开系统定位开关',
  103. })
  104. return
  105. }
  106. if (isAndroid && !systemInfo.locationAuthorized) {
  107. ecBluetoothAdapterStateChangeCallback({
  108. ok: false,
  109. errCode: 30003,
  110. errMsg: '请打开支付宝定位权限,允许支付宝使用您的位置信息',
  111. })
  112. return
  113. }
  114. const setting = await getSetting() //小程序蓝牙权限
  115. if (!setting.ok) {
  116. const authRes = await authorize()
  117. if (!authRes.ok) {
  118. ecBluetoothAdapterStateChangeCallback({
  119. ok: false,
  120. errCode: 30004,
  121. errMsg: '请打开小程序蓝牙开关,点击右上角三个点,然后点击设置',
  122. })
  123. return
  124. }
  125. }
  126. my.offBluetoothAdapterStateChange()
  127. my.onBluetoothAdapterStateChange(res => {
  128. log(res)
  129. // {"available":false,"discovering":false,"NBPageUrl":"https://2021002131657266.hybrid.alipay-eco.com/index.html#pages/index/index"}
  130. if (!res.available) {
  131. ecBluetoothAdapterStateChangeCallback({
  132. ok: false,
  133. errCode: 30005,
  134. errMsg: '蓝牙适配器不可用',
  135. })
  136. }
  137. })
  138. const openRes = await _openBluetoothAdapter()
  139. ecBluetoothAdapterStateChangeCallback(openRes)
  140. }
  141. const onBluetoothDeviceFound = cb => {
  142. my.offBluetoothDeviceFound()
  143. my.onBluetoothDeviceFound(res => {
  144. log(res)
  145. const device = res.devices[0]
  146. const name = device.name ? device.name : device.localName
  147. if (!name) {
  148. return
  149. }
  150. let id = device.deviceId
  151. let rssi = device.RSSI
  152. cb({ id, name, rssi })
  153. })
  154. }
  155. const startBluetoothDevicesDiscovery = () => {
  156. my.startBluetoothDevicesDiscovery({
  157. //services: [ecServerId],
  158. allowDuplicatesKey: true,
  159. // powerLevel: 'high',
  160. complete(res) {
  161. log(res)
  162. },
  163. })
  164. }
  165. const stopBluetoothDevicesDiscovery = () => {
  166. my.stopBluetoothDevicesDiscovery({
  167. complete(res) {
  168. log(res)
  169. },
  170. })
  171. }
  172. const onBLEConnectionStateChange = cb => {
  173. ecBLEConnectionStateChangeCallback = cb
  174. }
  175. const _createBLEConnection = () => {
  176. return new Promise(function (resolve, reject) {
  177. my.connectBLEDevice({
  178. deviceId: ecDeviceId,
  179. success(res) {
  180. log(res)
  181. resolve({ ok: true, errCode: 0, errMsg: '' })
  182. },
  183. fail(res) {
  184. log(res)
  185. resolve({
  186. ok: false,
  187. errCode: res.error,
  188. errMsg: res.errorMessage,
  189. })
  190. },
  191. })
  192. })
  193. }
  194. const getBLEDeviceServices = () => {
  195. return new Promise(function (resolve, reject) {
  196. my.getBLEDeviceServices({
  197. deviceId: ecDeviceId,
  198. success(res) {
  199. log(res)
  200. resolve({
  201. ok: true,
  202. errCode: 0,
  203. errMsg: '',
  204. services: res.services,
  205. })
  206. },
  207. fail(res) {
  208. log(res)
  209. resolve({ ok: false, errCode: res.error, errMsg: res.errorMessage })
  210. },
  211. })
  212. })
  213. }
  214. const getBLEDeviceCharacteristics = serviceId => {
  215. return new Promise(function (resolve, reject) {
  216. my.getBLEDeviceCharacteristics({
  217. deviceId: ecDeviceId,
  218. serviceId,
  219. success(res) {
  220. log(res)
  221. resolve({
  222. ok: true,
  223. errCode: 0,
  224. errMsg: '',
  225. characteristics: res.characteristics,
  226. })
  227. },
  228. fail(res) {
  229. log(res)
  230. resolve({ ok: false, errCode: res.error, errMsg: res.errorMessage })
  231. },
  232. })
  233. })
  234. }
  235. const notifyBLECharacteristicValueChange = (serviceId, characteristicId) => {
  236. return new Promise(function (resolve, reject) {
  237. my.notifyBLECharacteristicValueChange({
  238. state: true,
  239. deviceId: ecDeviceId,
  240. serviceId,
  241. characteristicId,
  242. success(res) {
  243. log(res)
  244. resolve({ ok: true, errCode: 0, errMsg: '' })
  245. },
  246. fail(res) {
  247. log(res)
  248. resolve({ ok: false, errCode: res.error, errMsg: res.errorMessage })
  249. },
  250. })
  251. })
  252. }
  253. const setBLEMTU = mtu => {
  254. return new Promise(function (resolve, reject) {
  255. my.setBLEMTU({
  256. deviceId: ecDeviceId,
  257. mtu,
  258. success(res) {
  259. log(res)
  260. resolve({ ok: true, errCode: 0, errMsg: '' })
  261. },
  262. fail(res) {
  263. log(res)
  264. resolve({ ok: false, errCode: res.error, errMsg: res.errorMessage })
  265. },
  266. })
  267. })
  268. }
  269. const createBLEConnection = async id => {
  270. ecDeviceId = id
  271. my.offBLEConnectionStateChanged()
  272. my.onBLEConnectionStateChanged(async res => {
  273. log(res)
  274. // {"deviceId":"EC:22:05:13:78:49","connected":true}
  275. if (res.connected) {
  276. const servicesResult = await getBLEDeviceServices()
  277. if (!servicesResult.ok) {
  278. ecBLEConnectionStateChangeCallback(servicesResult)
  279. closeBLEConnection()
  280. return
  281. }
  282. for (const service of servicesResult.services) {
  283. if ((service.serviceId.toUpperCase() === ecGattServerUUIDOption1) ||
  284. (service.serviceId.toUpperCase() === ecGattServerUUIDOption2)) {
  285. ecGattServerUUID = service.serviceId
  286. }
  287. const characteristicsResult = await getBLEDeviceCharacteristics(
  288. service.serviceId
  289. )
  290. if (!characteristicsResult.ok) {
  291. ecBLEConnectionStateChangeCallback(characteristicsResult)
  292. closeBLEConnection()
  293. return
  294. }
  295. for (const characteristic of characteristicsResult.characteristics) {
  296. if (
  297. characteristic.properties &&
  298. characteristic.properties.notify
  299. ) {
  300. const notifyResult =
  301. await notifyBLECharacteristicValueChange(
  302. service.serviceId,
  303. characteristic.characteristicId
  304. )
  305. if (!notifyResult.ok) {
  306. ecBLEConnectionStateChangeCallback({
  307. ok: false,
  308. errCode: 30000,
  309. errMsg: 'notify error',
  310. })
  311. closeBLEConnection()
  312. return
  313. }
  314. }
  315. if ((characteristic.characteristicId.toUpperCase() === ecGattCharacteristicWriteUUIDOption1) ||
  316. (characteristic.characteristicId.toUpperCase() === ecGattCharacteristicWriteUUIDOption2)) {
  317. ecGattCharacteristicWriteUUID = characteristic.characteristicId
  318. }
  319. }
  320. }
  321. if (isAndroid) {
  322. await setBLEMTU(247)
  323. }
  324. ecBLEConnectionStateChangeCallback({
  325. ok: true,
  326. errCode: 0,
  327. errMsg: '',
  328. })
  329. } else {
  330. ecBLEConnectionStateChangeCallback({
  331. ok: false,
  332. errCode: 0,
  333. errMsg: 'disconnect',
  334. })
  335. }
  336. })
  337. const res = await _createBLEConnection()
  338. if (!res.ok) {
  339. ecBLEConnectionStateChangeCallback(res)
  340. }
  341. }
  342. const closeBLEConnection = () => {
  343. my.disconnectBLEDevice({
  344. deviceId: ecDeviceId,
  345. complete(res) {
  346. log(res)
  347. },
  348. })
  349. }
  350. const onBLECharacteristicValueChange = cb => {
  351. my.offBLECharacteristicValueChange()
  352. my.onBLECharacteristicValueChange(res => {
  353. log(res)
  354. let bytes = []
  355. for (let i = 0; i < (res.value.length / 2); i++) {
  356. bytes.push(parseInt(res.value.substr(i * 2, 2), 16))
  357. }
  358. let str = utf8BytesToStr(bytes)
  359. let strHex = res.value
  360. log(str)
  361. log(strHex)
  362. cb(str, strHex)
  363. })
  364. }
  365. const _writeBLECharacteristicValue = buffer => {
  366. return new Promise(function (resolve, reject) {
  367. my.writeBLECharacteristicValue({
  368. deviceId: ecDeviceId,
  369. serviceId: ecGattServerUUID,
  370. characteristicId: ecGattCharacteristicWriteUUID,
  371. value: buffer,
  372. // writeType: 'writeNoResponse',
  373. success(res) {
  374. log(res)
  375. resolve({ ok: true, errCode: 0, errMsg: '' })
  376. },
  377. fail(res) {
  378. log(res)
  379. resolve({ ok: false, errCode: res.error, errMsg: res.errorMessage })
  380. },
  381. })
  382. })
  383. }
  384. const writeBLECharacteristicValue = async (str, isHex) => {
  385. if (str.length === 0)
  386. return { ok: false, errCode: 30000, errMsg: 'data is null' }
  387. let buffer
  388. if (isHex) {
  389. buffer = new ArrayBuffer(str.length / 2)
  390. let x = new Uint8Array(buffer)
  391. for (let i = 0; i < x.length; i++) {
  392. x[i] = parseInt(str.substr(2 * i, 2), 16)
  393. }
  394. } else {
  395. buffer = new Uint8Array(strToUtf8Bytes(str)).buffer
  396. }
  397. return await _writeBLECharacteristicValue(buffer)
  398. }
  399. const utf8BytesToStr = utf8Bytes => {
  400. let unicodeStr = ''
  401. for (let pos = 0; pos < utf8Bytes.length;) {
  402. let flag = utf8Bytes[pos]
  403. let unicode = 0
  404. if (flag >>> 7 === 0) {
  405. unicodeStr += String.fromCharCode(utf8Bytes[pos])
  406. pos += 1
  407. }
  408. // else if ((flag & 0xFC) === 0xFC) {
  409. // unicode = (utf8Bytes[pos] & 0x3) << 30
  410. // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24
  411. // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18
  412. // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12
  413. // unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6
  414. // unicode |= (utf8Bytes[pos + 5] & 0x3F)
  415. // unicodeStr += String.fromCharCode(unicode)
  416. // pos += 6
  417. // }
  418. // else if ((flag & 0xF8) === 0xF8) {
  419. // unicode = (utf8Bytes[pos] & 0x7) << 24
  420. // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18
  421. // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12
  422. // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6
  423. // unicode |= (utf8Bytes[pos + 4] & 0x3F)
  424. // unicodeStr += String.fromCharCode(unicode)
  425. // pos += 5
  426. // }
  427. else if ((flag & 0xf0) === 0xf0) {
  428. unicode = (utf8Bytes[pos] & 0xf) << 18
  429. unicode |= (utf8Bytes[pos + 1] & 0x3f) << 12
  430. unicode |= (utf8Bytes[pos + 2] & 0x3f) << 6
  431. unicode |= utf8Bytes[pos + 3] & 0x3f
  432. unicodeStr += String.fromCharCode(unicode)
  433. pos += 4
  434. } else if ((flag & 0xe0) === 0xe0) {
  435. unicode = (utf8Bytes[pos] & 0x1f) << 12
  436. unicode |= (utf8Bytes[pos + 1] & 0x3f) << 6
  437. unicode |= utf8Bytes[pos + 2] & 0x3f
  438. unicodeStr += String.fromCharCode(unicode)
  439. pos += 3
  440. } else if ((flag & 0xc0) === 0xc0) {
  441. //110
  442. unicode = (utf8Bytes[pos] & 0x3f) << 6
  443. unicode |= utf8Bytes[pos + 1] & 0x3f
  444. unicodeStr += String.fromCharCode(unicode)
  445. pos += 2
  446. } else {
  447. unicodeStr += String.fromCharCode(utf8Bytes[pos])
  448. pos += 1
  449. }
  450. }
  451. return unicodeStr
  452. }
  453. const strToUtf8Bytes = str => {
  454. let bytes = []
  455. for (let i = 0; i < str.length; ++i) {
  456. let code = str.charCodeAt(i)
  457. if (code >= 0x10000 && code <= 0x10ffff) {
  458. bytes.push((code >> 18) | 0xf0) // 第一个字节
  459. bytes.push(((code >> 12) & 0x3f) | 0x80)
  460. bytes.push(((code >> 6) & 0x3f) | 0x80)
  461. bytes.push((code & 0x3f) | 0x80)
  462. } else if (code >= 0x800 && code <= 0xffff) {
  463. bytes.push((code >> 12) | 0xe0)
  464. bytes.push(((code >> 6) & 0x3f) | 0x80)
  465. bytes.push((code & 0x3f) | 0x80)
  466. } else if (code >= 0x80 && code <= 0x7ff) {
  467. bytes.push((code >> 6) | 0xc0)
  468. bytes.push((code & 0x3f) | 0x80)
  469. } else {
  470. bytes.push(code)
  471. }
  472. }
  473. return bytes
  474. }
  475. module.exports = {
  476. onBluetoothAdapterStateChange,
  477. openBluetoothAdapter,
  478. onBluetoothDeviceFound,
  479. startBluetoothDevicesDiscovery,
  480. stopBluetoothDevicesDiscovery,
  481. onBLEConnectionStateChange,
  482. createBLEConnection,
  483. closeBLEConnection,
  484. onBLECharacteristicValueChange,
  485. writeBLECharacteristicValue,
  486. }