| @ -0,0 +1,33 @@ | |||
| // app.js | |||
| import { globalData } from "./utils/global"; | |||
| const EventEmitter2 = require("./miniprogram_npm/eventemitter2/index").EventEmitter2; | |||
| const emitter = new EventEmitter2(); | |||
| App({ | |||
| onLaunch() { | |||
| // 展示本地存储能力 | |||
| const logs = wx.getStorageSync('logs') || [] | |||
| logs.unshift(Date.now()) | |||
| wx.setStorageSync('logs', logs) | |||
| // 登录 | |||
| wx.login({ | |||
| success: res => { | |||
| // 发送 res.code 到后台换取 openId, sessionKey, unionId | |||
| } | |||
| }) | |||
| }, | |||
| globalData: Object.assign( | |||
| { | |||
| ble: "",//全局蓝牙实例 | |||
| emitter: emitter, //全局订阅函数 | |||
| blueStatus: false, | |||
| userInfo: null | |||
| }, | |||
| globalData | |||
| ), | |||
| }) | |||
| @ -0,0 +1,16 @@ | |||
| { | |||
| "pages":[ | |||
| "pages/asyntest/asyntest", | |||
| "pages/lanyatest/lanyatest", | |||
| "pages/index/index", | |||
| "pages/logs/logs" | |||
| ], | |||
| "window":{ | |||
| "backgroundTextStyle":"light", | |||
| "navigationBarBackgroundColor": "#fff", | |||
| "navigationBarTitleText": "Weixin", | |||
| "navigationBarTextStyle":"black" | |||
| }, | |||
| "style": "v2", | |||
| "sitemapLocation": "sitemap.json" | |||
| } | |||
| @ -0,0 +1,10 @@ | |||
| /**app.wxss**/ | |||
| .container { | |||
| height: 100%; | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| padding: 200rpx 0; | |||
| box-sizing: border-box; | |||
| } | |||
| @ -0,0 +1,800 @@ | |||
| module.exports = (function() { | |||
| var __MODS__ = {}; | |||
| var __DEFINE__ = function(modId, func, req) { var m = { exports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; | |||
| var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = { exports: {} }; __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); if(typeof m.exports === "object") { __MODS__[modId].m.exports.__proto__ = m.exports.__proto__; Object.keys(m.exports).forEach(function(k) { __MODS__[modId].m.exports[k] = m.exports[k]; var desp = Object.getOwnPropertyDescriptor(m.exports, k); if(desp && desp.configurable) Object.defineProperty(m.exports, k, { set: function(val) { __MODS__[modId].m.exports[k] = val; }, get: function() { return __MODS__[modId].m.exports[k]; } }); }); if(m.exports.__esModule) Object.defineProperty(__MODS__[modId].m.exports, "__esModule", { value: true }); } else { __MODS__[modId].m.exports = m.exports; } } return __MODS__[modId].m.exports; }; | |||
| var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; | |||
| var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; | |||
| __DEFINE__(1585827830357, function(require, module, exports) { | |||
| /*! | |||
| * EventEmitter2 | |||
| * https://github.com/hij1nx/EventEmitter2 | |||
| * | |||
| * Copyright (c) 2013 hij1nx | |||
| * Licensed under the MIT license. | |||
| */ | |||
| ;!function(undefined) { | |||
| var isArray = Array.isArray ? Array.isArray : function _isArray(obj) { | |||
| return Object.prototype.toString.call(obj) === "[object Array]"; | |||
| }; | |||
| var defaultMaxListeners = 10; | |||
| function init() { | |||
| this._events = {}; | |||
| if (this._conf) { | |||
| configure.call(this, this._conf); | |||
| } | |||
| } | |||
| function configure(conf) { | |||
| if (conf) { | |||
| this._conf = conf; | |||
| conf.delimiter && (this.delimiter = conf.delimiter); | |||
| this._maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners; | |||
| conf.wildcard && (this.wildcard = conf.wildcard); | |||
| conf.newListener && (this._newListener = conf.newListener); | |||
| conf.removeListener && (this._removeListener = conf.removeListener); | |||
| conf.verboseMemoryLeak && (this.verboseMemoryLeak = conf.verboseMemoryLeak); | |||
| if (this.wildcard) { | |||
| this.listenerTree = {}; | |||
| } | |||
| } else { | |||
| this._maxListeners = defaultMaxListeners; | |||
| } | |||
| } | |||
| function logPossibleMemoryLeak(count, eventName) { | |||
| var errorMsg = '(node) warning: possible EventEmitter memory ' + | |||
| 'leak detected. ' + count + ' listeners added. ' + | |||
| 'Use emitter.setMaxListeners() to increase limit.'; | |||
| if(this.verboseMemoryLeak){ | |||
| errorMsg += ' Event name: ' + eventName + '.'; | |||
| } | |||
| if(typeof process !== 'undefined' && process.emitWarning){ | |||
| var e = new Error(errorMsg); | |||
| e.name = 'MaxListenersExceededWarning'; | |||
| e.emitter = this; | |||
| e.count = count; | |||
| process.emitWarning(e); | |||
| } else { | |||
| console.error(errorMsg); | |||
| if (console.trace){ | |||
| console.trace(); | |||
| } | |||
| } | |||
| } | |||
| function EventEmitter(conf) { | |||
| this._events = {}; | |||
| this._newListener = false; | |||
| this._removeListener = false; | |||
| this.verboseMemoryLeak = false; | |||
| configure.call(this, conf); | |||
| } | |||
| EventEmitter.EventEmitter2 = EventEmitter; // backwards compatibility for exporting EventEmitter property | |||
| // | |||
| // Attention, function return type now is array, always ! | |||
| // It has zero elements if no any matches found and one or more | |||
| // elements (leafs) if there are matches | |||
| // | |||
| function searchListenerTree(handlers, type, tree, i) { | |||
| if (!tree) { | |||
| return []; | |||
| } | |||
| var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached, | |||
| typeLength = type.length, currentType = type[i], nextType = type[i+1]; | |||
| if (i === typeLength && tree._listeners) { | |||
| // | |||
| // If at the end of the event(s) list and the tree has listeners | |||
| // invoke those listeners. | |||
| // | |||
| if (typeof tree._listeners === 'function') { | |||
| handlers && handlers.push(tree._listeners); | |||
| return [tree]; | |||
| } else { | |||
| for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) { | |||
| handlers && handlers.push(tree._listeners[leaf]); | |||
| } | |||
| return [tree]; | |||
| } | |||
| } | |||
| if ((currentType === '*' || currentType === '**') || tree[currentType]) { | |||
| // | |||
| // If the event emitted is '*' at this part | |||
| // or there is a concrete match at this patch | |||
| // | |||
| if (currentType === '*') { | |||
| for (branch in tree) { | |||
| if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { | |||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1)); | |||
| } | |||
| } | |||
| return listeners; | |||
| } else if(currentType === '**') { | |||
| endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*')); | |||
| if(endReached && tree._listeners) { | |||
| // The next element has a _listeners, add it to the handlers. | |||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength)); | |||
| } | |||
| for (branch in tree) { | |||
| if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { | |||
| if(branch === '*' || branch === '**') { | |||
| if(tree[branch]._listeners && !endReached) { | |||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength)); | |||
| } | |||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); | |||
| } else if(branch === nextType) { | |||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2)); | |||
| } else { | |||
| // No match on this one, shift into the tree but not in the type array. | |||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); | |||
| } | |||
| } | |||
| } | |||
| return listeners; | |||
| } | |||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1)); | |||
| } | |||
| xTree = tree['*']; | |||
| if (xTree) { | |||
| // | |||
| // If the listener tree will allow any match for this part, | |||
| // then recursively explore all branches of the tree | |||
| // | |||
| searchListenerTree(handlers, type, xTree, i+1); | |||
| } | |||
| xxTree = tree['**']; | |||
| if(xxTree) { | |||
| if(i < typeLength) { | |||
| if(xxTree._listeners) { | |||
| // If we have a listener on a '**', it will catch all, so add its handler. | |||
| searchListenerTree(handlers, type, xxTree, typeLength); | |||
| } | |||
| // Build arrays of matching next branches and others. | |||
| for(branch in xxTree) { | |||
| if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) { | |||
| if(branch === nextType) { | |||
| // We know the next element will match, so jump twice. | |||
| searchListenerTree(handlers, type, xxTree[branch], i+2); | |||
| } else if(branch === currentType) { | |||
| // Current node matches, move into the tree. | |||
| searchListenerTree(handlers, type, xxTree[branch], i+1); | |||
| } else { | |||
| isolatedBranch = {}; | |||
| isolatedBranch[branch] = xxTree[branch]; | |||
| searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1); | |||
| } | |||
| } | |||
| } | |||
| } else if(xxTree._listeners) { | |||
| // We have reached the end and still on a '**' | |||
| searchListenerTree(handlers, type, xxTree, typeLength); | |||
| } else if(xxTree['*'] && xxTree['*']._listeners) { | |||
| searchListenerTree(handlers, type, xxTree['*'], typeLength); | |||
| } | |||
| } | |||
| return listeners; | |||
| } | |||
| function growListenerTree(type, listener) { | |||
| type = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |||
| // | |||
| // Looks for two consecutive '**', if so, don't add the event at all. | |||
| // | |||
| for(var i = 0, len = type.length; i+1 < len; i++) { | |||
| if(type[i] === '**' && type[i+1] === '**') { | |||
| return; | |||
| } | |||
| } | |||
| var tree = this.listenerTree; | |||
| var name = type.shift(); | |||
| while (name !== undefined) { | |||
| if (!tree[name]) { | |||
| tree[name] = {}; | |||
| } | |||
| tree = tree[name]; | |||
| if (type.length === 0) { | |||
| if (!tree._listeners) { | |||
| tree._listeners = listener; | |||
| } | |||
| else { | |||
| if (typeof tree._listeners === 'function') { | |||
| tree._listeners = [tree._listeners]; | |||
| } | |||
| tree._listeners.push(listener); | |||
| if ( | |||
| !tree._listeners.warned && | |||
| this._maxListeners > 0 && | |||
| tree._listeners.length > this._maxListeners | |||
| ) { | |||
| tree._listeners.warned = true; | |||
| logPossibleMemoryLeak.call(this, tree._listeners.length, name); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| name = type.shift(); | |||
| } | |||
| return true; | |||
| } | |||
| // By default EventEmitters will print a warning if more than | |||
| // 10 listeners are added to it. This is a useful default which | |||
| // helps finding memory leaks. | |||
| // | |||
| // Obviously not all Emitters should be limited to 10. This function allows | |||
| // that to be increased. Set to zero for unlimited. | |||
| EventEmitter.prototype.delimiter = '.'; | |||
| EventEmitter.prototype.setMaxListeners = function(n) { | |||
| if (n !== undefined) { | |||
| this._maxListeners = n; | |||
| if (!this._conf) this._conf = {}; | |||
| this._conf.maxListeners = n; | |||
| } | |||
| }; | |||
| EventEmitter.prototype.event = ''; | |||
| EventEmitter.prototype.once = function(event, fn) { | |||
| return this._once(event, fn, false); | |||
| }; | |||
| EventEmitter.prototype.prependOnceListener = function(event, fn) { | |||
| return this._once(event, fn, true); | |||
| }; | |||
| EventEmitter.prototype._once = function(event, fn, prepend) { | |||
| this._many(event, 1, fn, prepend); | |||
| return this; | |||
| }; | |||
| EventEmitter.prototype.many = function(event, ttl, fn) { | |||
| return this._many(event, ttl, fn, false); | |||
| } | |||
| EventEmitter.prototype.prependMany = function(event, ttl, fn) { | |||
| return this._many(event, ttl, fn, true); | |||
| } | |||
| EventEmitter.prototype._many = function(event, ttl, fn, prepend) { | |||
| var self = this; | |||
| if (typeof fn !== 'function') { | |||
| throw new Error('many only accepts instances of Function'); | |||
| } | |||
| function listener() { | |||
| if (--ttl === 0) { | |||
| self.off(event, listener); | |||
| } | |||
| return fn.apply(this, arguments); | |||
| } | |||
| listener._origin = fn; | |||
| this._on(event, listener, prepend); | |||
| return self; | |||
| }; | |||
| EventEmitter.prototype.emit = function() { | |||
| if (!this._events && !this._all) { | |||
| return false; | |||
| } | |||
| this._events || init.call(this); | |||
| var type = arguments[0]; | |||
| if (type === 'newListener' && !this._newListener) { | |||
| if (!this._events.newListener) { | |||
| return false; | |||
| } | |||
| } | |||
| var al = arguments.length; | |||
| var args,l,i,j; | |||
| var handler; | |||
| if (this._all && this._all.length) { | |||
| handler = this._all.slice(); | |||
| if (al > 3) { | |||
| args = new Array(al); | |||
| for (j = 0; j < al; j++) args[j] = arguments[j]; | |||
| } | |||
| for (i = 0, l = handler.length; i < l; i++) { | |||
| this.event = type; | |||
| switch (al) { | |||
| case 1: | |||
| handler[i].call(this, type); | |||
| break; | |||
| case 2: | |||
| handler[i].call(this, type, arguments[1]); | |||
| break; | |||
| case 3: | |||
| handler[i].call(this, type, arguments[1], arguments[2]); | |||
| break; | |||
| default: | |||
| handler[i].apply(this, args); | |||
| } | |||
| } | |||
| } | |||
| if (this.wildcard) { | |||
| handler = []; | |||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |||
| searchListenerTree.call(this, handler, ns, this.listenerTree, 0); | |||
| } else { | |||
| handler = this._events[type]; | |||
| if (typeof handler === 'function') { | |||
| this.event = type; | |||
| switch (al) { | |||
| case 1: | |||
| handler.call(this); | |||
| break; | |||
| case 2: | |||
| handler.call(this, arguments[1]); | |||
| break; | |||
| case 3: | |||
| handler.call(this, arguments[1], arguments[2]); | |||
| break; | |||
| default: | |||
| args = new Array(al - 1); | |||
| for (j = 1; j < al; j++) args[j - 1] = arguments[j]; | |||
| handler.apply(this, args); | |||
| } | |||
| return true; | |||
| } else if (handler) { | |||
| // need to make copy of handlers because list can change in the middle | |||
| // of emit call | |||
| handler = handler.slice(); | |||
| } | |||
| } | |||
| if (handler && handler.length) { | |||
| if (al > 3) { | |||
| args = new Array(al - 1); | |||
| for (j = 1; j < al; j++) args[j - 1] = arguments[j]; | |||
| } | |||
| for (i = 0, l = handler.length; i < l; i++) { | |||
| this.event = type; | |||
| switch (al) { | |||
| case 1: | |||
| handler[i].call(this); | |||
| break; | |||
| case 2: | |||
| handler[i].call(this, arguments[1]); | |||
| break; | |||
| case 3: | |||
| handler[i].call(this, arguments[1], arguments[2]); | |||
| break; | |||
| default: | |||
| handler[i].apply(this, args); | |||
| } | |||
| } | |||
| return true; | |||
| } else if (!this._all && type === 'error') { | |||
| if (arguments[1] instanceof Error) { | |||
| throw arguments[1]; // Unhandled 'error' event | |||
| } else { | |||
| throw new Error("Uncaught, unspecified 'error' event."); | |||
| } | |||
| return false; | |||
| } | |||
| return !!this._all; | |||
| }; | |||
| EventEmitter.prototype.emitAsync = function() { | |||
| if (!this._events && !this._all) { | |||
| return false; | |||
| } | |||
| this._events || init.call(this); | |||
| var type = arguments[0]; | |||
| if (type === 'newListener' && !this._newListener) { | |||
| if (!this._events.newListener) { return Promise.resolve([false]); } | |||
| } | |||
| var promises= []; | |||
| var al = arguments.length; | |||
| var args,l,i,j; | |||
| var handler; | |||
| if (this._all) { | |||
| if (al > 3) { | |||
| args = new Array(al); | |||
| for (j = 1; j < al; j++) args[j] = arguments[j]; | |||
| } | |||
| for (i = 0, l = this._all.length; i < l; i++) { | |||
| this.event = type; | |||
| switch (al) { | |||
| case 1: | |||
| promises.push(this._all[i].call(this, type)); | |||
| break; | |||
| case 2: | |||
| promises.push(this._all[i].call(this, type, arguments[1])); | |||
| break; | |||
| case 3: | |||
| promises.push(this._all[i].call(this, type, arguments[1], arguments[2])); | |||
| break; | |||
| default: | |||
| promises.push(this._all[i].apply(this, args)); | |||
| } | |||
| } | |||
| } | |||
| if (this.wildcard) { | |||
| handler = []; | |||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |||
| searchListenerTree.call(this, handler, ns, this.listenerTree, 0); | |||
| } else { | |||
| handler = this._events[type]; | |||
| } | |||
| if (typeof handler === 'function') { | |||
| this.event = type; | |||
| switch (al) { | |||
| case 1: | |||
| promises.push(handler.call(this)); | |||
| break; | |||
| case 2: | |||
| promises.push(handler.call(this, arguments[1])); | |||
| break; | |||
| case 3: | |||
| promises.push(handler.call(this, arguments[1], arguments[2])); | |||
| break; | |||
| default: | |||
| args = new Array(al - 1); | |||
| for (j = 1; j < al; j++) args[j - 1] = arguments[j]; | |||
| promises.push(handler.apply(this, args)); | |||
| } | |||
| } else if (handler && handler.length) { | |||
| handler = handler.slice(); | |||
| if (al > 3) { | |||
| args = new Array(al - 1); | |||
| for (j = 1; j < al; j++) args[j - 1] = arguments[j]; | |||
| } | |||
| for (i = 0, l = handler.length; i < l; i++) { | |||
| this.event = type; | |||
| switch (al) { | |||
| case 1: | |||
| promises.push(handler[i].call(this)); | |||
| break; | |||
| case 2: | |||
| promises.push(handler[i].call(this, arguments[1])); | |||
| break; | |||
| case 3: | |||
| promises.push(handler[i].call(this, arguments[1], arguments[2])); | |||
| break; | |||
| default: | |||
| promises.push(handler[i].apply(this, args)); | |||
| } | |||
| } | |||
| } else if (!this._all && type === 'error') { | |||
| if (arguments[1] instanceof Error) { | |||
| return Promise.reject(arguments[1]); // Unhandled 'error' event | |||
| } else { | |||
| return Promise.reject("Uncaught, unspecified 'error' event."); | |||
| } | |||
| } | |||
| return Promise.all(promises); | |||
| }; | |||
| EventEmitter.prototype.on = function(type, listener) { | |||
| return this._on(type, listener, false); | |||
| }; | |||
| EventEmitter.prototype.prependListener = function(type, listener) { | |||
| return this._on(type, listener, true); | |||
| }; | |||
| EventEmitter.prototype.onAny = function(fn) { | |||
| return this._onAny(fn, false); | |||
| }; | |||
| EventEmitter.prototype.prependAny = function(fn) { | |||
| return this._onAny(fn, true); | |||
| }; | |||
| EventEmitter.prototype.addListener = EventEmitter.prototype.on; | |||
| EventEmitter.prototype._onAny = function(fn, prepend){ | |||
| if (typeof fn !== 'function') { | |||
| throw new Error('onAny only accepts instances of Function'); | |||
| } | |||
| if (!this._all) { | |||
| this._all = []; | |||
| } | |||
| // Add the function to the event listener collection. | |||
| if(prepend){ | |||
| this._all.unshift(fn); | |||
| }else{ | |||
| this._all.push(fn); | |||
| } | |||
| return this; | |||
| } | |||
| EventEmitter.prototype._on = function(type, listener, prepend) { | |||
| if (typeof type === 'function') { | |||
| this._onAny(type, listener); | |||
| return this; | |||
| } | |||
| if (typeof listener !== 'function') { | |||
| throw new Error('on only accepts instances of Function'); | |||
| } | |||
| this._events || init.call(this); | |||
| // To avoid recursion in the case that type == "newListeners"! Before | |||
| // adding it to the listeners, first emit "newListeners". | |||
| if (this._newListener) | |||
| this.emit('newListener', type, listener); | |||
| if (this.wildcard) { | |||
| growListenerTree.call(this, type, listener); | |||
| return this; | |||
| } | |||
| if (!this._events[type]) { | |||
| // Optimize the case of one listener. Don't need the extra array object. | |||
| this._events[type] = listener; | |||
| } | |||
| else { | |||
| if (typeof this._events[type] === 'function') { | |||
| // Change to array. | |||
| this._events[type] = [this._events[type]]; | |||
| } | |||
| // If we've already got an array, just add | |||
| if(prepend){ | |||
| this._events[type].unshift(listener); | |||
| }else{ | |||
| this._events[type].push(listener); | |||
| } | |||
| // Check for listener leak | |||
| if ( | |||
| !this._events[type].warned && | |||
| this._maxListeners > 0 && | |||
| this._events[type].length > this._maxListeners | |||
| ) { | |||
| this._events[type].warned = true; | |||
| logPossibleMemoryLeak.call(this, this._events[type].length, type); | |||
| } | |||
| } | |||
| return this; | |||
| } | |||
| EventEmitter.prototype.off = function(type, listener) { | |||
| if (typeof listener !== 'function') { | |||
| throw new Error('removeListener only takes instances of Function'); | |||
| } | |||
| var handlers,leafs=[]; | |||
| if(this.wildcard) { | |||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |||
| leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); | |||
| } | |||
| else { | |||
| // does not use listeners(), so no side effect of creating _events[type] | |||
| if (!this._events[type]) return this; | |||
| handlers = this._events[type]; | |||
| leafs.push({_listeners:handlers}); | |||
| } | |||
| for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { | |||
| var leaf = leafs[iLeaf]; | |||
| handlers = leaf._listeners; | |||
| if (isArray(handlers)) { | |||
| var position = -1; | |||
| for (var i = 0, length = handlers.length; i < length; i++) { | |||
| if (handlers[i] === listener || | |||
| (handlers[i].listener && handlers[i].listener === listener) || | |||
| (handlers[i]._origin && handlers[i]._origin === listener)) { | |||
| position = i; | |||
| break; | |||
| } | |||
| } | |||
| if (position < 0) { | |||
| continue; | |||
| } | |||
| if(this.wildcard) { | |||
| leaf._listeners.splice(position, 1); | |||
| } | |||
| else { | |||
| this._events[type].splice(position, 1); | |||
| } | |||
| if (handlers.length === 0) { | |||
| if(this.wildcard) { | |||
| delete leaf._listeners; | |||
| } | |||
| else { | |||
| delete this._events[type]; | |||
| } | |||
| } | |||
| if (this._removeListener) | |||
| this.emit("removeListener", type, listener); | |||
| return this; | |||
| } | |||
| else if (handlers === listener || | |||
| (handlers.listener && handlers.listener === listener) || | |||
| (handlers._origin && handlers._origin === listener)) { | |||
| if(this.wildcard) { | |||
| delete leaf._listeners; | |||
| } | |||
| else { | |||
| delete this._events[type]; | |||
| } | |||
| if (this._removeListener) | |||
| this.emit("removeListener", type, listener); | |||
| } | |||
| } | |||
| function recursivelyGarbageCollect(root) { | |||
| if (root === undefined) { | |||
| return; | |||
| } | |||
| var keys = Object.keys(root); | |||
| for (var i in keys) { | |||
| var key = keys[i]; | |||
| var obj = root[key]; | |||
| if ((obj instanceof Function) || (typeof obj !== "object") || (obj === null)) | |||
| continue; | |||
| if (Object.keys(obj).length > 0) { | |||
| recursivelyGarbageCollect(root[key]); | |||
| } | |||
| if (Object.keys(obj).length === 0) { | |||
| delete root[key]; | |||
| } | |||
| } | |||
| } | |||
| recursivelyGarbageCollect(this.listenerTree); | |||
| return this; | |||
| }; | |||
| EventEmitter.prototype.offAny = function(fn) { | |||
| var i = 0, l = 0, fns; | |||
| if (fn && this._all && this._all.length > 0) { | |||
| fns = this._all; | |||
| for(i = 0, l = fns.length; i < l; i++) { | |||
| if(fn === fns[i]) { | |||
| fns.splice(i, 1); | |||
| if (this._removeListener) | |||
| this.emit("removeListenerAny", fn); | |||
| return this; | |||
| } | |||
| } | |||
| } else { | |||
| fns = this._all; | |||
| if (this._removeListener) { | |||
| for(i = 0, l = fns.length; i < l; i++) | |||
| this.emit("removeListenerAny", fns[i]); | |||
| } | |||
| this._all = []; | |||
| } | |||
| return this; | |||
| }; | |||
| EventEmitter.prototype.removeListener = EventEmitter.prototype.off; | |||
| EventEmitter.prototype.removeAllListeners = function(type) { | |||
| if (type === undefined) { | |||
| !this._events || init.call(this); | |||
| return this; | |||
| } | |||
| if (this.wildcard) { | |||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |||
| var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); | |||
| for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { | |||
| var leaf = leafs[iLeaf]; | |||
| leaf._listeners = null; | |||
| } | |||
| } | |||
| else if (this._events) { | |||
| this._events[type] = null; | |||
| } | |||
| return this; | |||
| }; | |||
| EventEmitter.prototype.listeners = function(type) { | |||
| if (this.wildcard) { | |||
| var handlers = []; | |||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |||
| searchListenerTree.call(this, handlers, ns, this.listenerTree, 0); | |||
| return handlers; | |||
| } | |||
| this._events || init.call(this); | |||
| if (!this._events[type]) this._events[type] = []; | |||
| if (!isArray(this._events[type])) { | |||
| this._events[type] = [this._events[type]]; | |||
| } | |||
| return this._events[type]; | |||
| }; | |||
| EventEmitter.prototype.eventNames = function(){ | |||
| return Object.keys(this._events); | |||
| } | |||
| EventEmitter.prototype.listenerCount = function(type) { | |||
| return this.listeners(type).length; | |||
| }; | |||
| EventEmitter.prototype.listenersAny = function() { | |||
| if(this._all) { | |||
| return this._all; | |||
| } | |||
| else { | |||
| return []; | |||
| } | |||
| }; | |||
| if (typeof define === 'function' && define.amd) { | |||
| // AMD. Register as an anonymous module. | |||
| define(function() { | |||
| return EventEmitter; | |||
| }); | |||
| } else if (typeof exports === 'object') { | |||
| // CommonJS | |||
| module.exports = EventEmitter; | |||
| } | |||
| else { | |||
| // Browser global. | |||
| window.EventEmitter2 = EventEmitter; | |||
| } | |||
| }(); | |||
| }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) | |||
| return __REQUIRE__(1585827830357); | |||
| })() | |||
| //# sourceMappingURL=index.js.map | |||
| @ -0,0 +1,191 @@ | |||
| const app = getApp(); | |||
| var longUtil = require('./../../utils/long'); | |||
| var util = require('../../utils/util'); | |||
| import { BLE } from "./../../utils/btls/ble"; | |||
| const emitter = app.globalData.emitter | |||
| Page({ | |||
| /** | |||
| * 页面的初始数据 | |||
| */ | |||
| data: { | |||
| baseUrlImg: app.globalData.baseUrlImg, | |||
| bleStatus: false, | |||
| respond: [], | |||
| blueyes: false, //是否重发同步指令 | |||
| connectStatus: "", | |||
| info:"未初始化蓝牙适配器", | |||
| }, | |||
| btntest1(event){ | |||
| var that = this; | |||
| this.startconnect() | |||
| }, | |||
| btntest2(event){ | |||
| var that = this | |||
| let userTelephone = "13028878230" | |||
| let userPassword = "130288" | |||
| let pwd = "12344321" | |||
| let openLockType = 2 | |||
| this.openLock(userTelephone, userPassword, pwd, openLockType) | |||
| }, | |||
| //获取输入框的数据 | |||
| getmsg(event){ | |||
| this.setData({ | |||
| sendmsg:event.detail.value | |||
| }) | |||
| }, | |||
| btntest3(event){ | |||
| var that = this; | |||
| this.closeblue() | |||
| }, | |||
| startconnect() { | |||
| //if(checkWechatVersion()) | |||
| { | |||
| const ble = new BLE("yxwl01680000004895", emitter) | |||
| app.globalData.ble = ble | |||
| this.watchBLE() | |||
| app.globalData.ble.init() | |||
| } | |||
| }, | |||
| watchBLE() { | |||
| if (app.globalData.ble) { | |||
| app.globalData.ble.listen(res => { | |||
| if (res.type == 'connect') { | |||
| if (res.data == "未打开适配器") { | |||
| wx.showModal({ | |||
| title: "提示", | |||
| content: "请检查手机蓝牙和定位功能是否打开?", | |||
| showCancel: false, | |||
| confirmText: "确定", | |||
| }); | |||
| } else { | |||
| this.setData({ | |||
| info: res.data | |||
| }) | |||
| app.globalData.bleStatus = res.data | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| }, | |||
| closeblue() { | |||
| wx.showLoading({ | |||
| title: "正在停止...", | |||
| }); | |||
| app.globalData.ble.close() | |||
| setTimeout(() => { | |||
| wx.hideLoading() | |||
| }, 1000); | |||
| app.globalData.bleStatus = false | |||
| this.setData({ | |||
| bleStatus: false, | |||
| respond: [] | |||
| }) | |||
| }, | |||
| openLock(userTelephone, userPassword, pwd, openLockType) { | |||
| wx.showLoading({ | |||
| title: "正在发送...", | |||
| }); | |||
| setTimeout(() => { | |||
| wx.hideLoading() | |||
| }, 1000); | |||
| let cmd = 0x41 | |||
| let ut = util.stringToUint8Array(userTelephone) | |||
| let up = util.stringToUint8Array(userPassword) | |||
| let p = util.hexStringToBytesWithPadding(pwd, 10, 0xff) | |||
| let length = 1+11+6+10+1 | |||
| let b = new Uint8Array(length) | |||
| b[0] = cmd | |||
| b.set(ut, 1) | |||
| b.set(up, 12) | |||
| b.set(p, 18) | |||
| b[28] = openLockType | |||
| console.log(b) | |||
| app.globalData.ble.send(cmd, b, length) | |||
| }, | |||
| }) | |||
| /** | |||
| * 版本比较 | |||
| */ | |||
| function versionCompare (ver1, ver2) { //版本比较 | |||
| // console.log("ver1" + ver1 + 'ver2' + ver2); | |||
| var version1pre = parseFloat(ver1) | |||
| var version2pre = parseFloat(ver2) | |||
| var version1next = parseInt(ver1.replace(version1pre + ".", "")) | |||
| var version2next = parseInt(ver2.replace(version2pre + ".", "")) | |||
| if (version1pre > version2pre) | |||
| return true | |||
| else if (version1pre < version2pre) | |||
| return false | |||
| else { | |||
| if (version1next > version2next) | |||
| return true | |||
| else | |||
| return false | |||
| } | |||
| } | |||
| /** | |||
| * 微信版本检测 | |||
| * Android从微信 6.5.7 开始支持,iOS从微信 6.5.6 开始支持 | |||
| */ | |||
| function checkWechatVersion() { | |||
| wx.getSystemInfo({ | |||
| success: function (res) { | |||
| let si = res; | |||
| if (si.platform == 'android' && versionCompare('6.5.7', si.version)) { | |||
| console.log("当前安卓微信版本过低,请更新至最新版本体验"); | |||
| wx.showModal({ | |||
| title: '提示', | |||
| content: '当前微信版本过低,请更新至最新版本体验', | |||
| showCancel: false | |||
| }) | |||
| return false; | |||
| }else if (si.platform == 'ios' && versionCompare('6.5.6', si.version)) { | |||
| console.log("当前苹果微信版本过低,请更新至最新版本体验"); | |||
| wx.showModal({ | |||
| title: '提示', | |||
| content: '当前微信版本过低,请更新至最新版本体验', | |||
| showCancel: false | |||
| }) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| }) | |||
| } | |||
| @ -0,0 +1,3 @@ | |||
| { | |||
| "usingComponents": {} | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| <!--pages/asyntest/asyntest.wxml--> | |||
| <view class="contentview"> | |||
| <view class='myview' > | |||
| {{info}} | |||
| </view> | |||
| <button type="primary" class="button" bindtap="btntest1">初始化蓝牙</button> | |||
| <view class="section"> | |||
| <input placeholder='请输入要发送的信息' bindinput='getmsg'/> | |||
| </view> | |||
| <button type="primary" class="button" bindtap="btntest2">微信发送消息</button> | |||
| <button type="primary" class="button" bindtap="btntest3">断开蓝牙设备</button> | |||
| </view> | |||
| @ -0,0 +1,28 @@ | |||
| /* pages/asyntest/asyntest.wxss */ | |||
| .vertical{ | |||
| display: flex; | |||
| flex-direction: column; | |||
| } | |||
| /**index.wxss**/ | |||
| .horizontal{ | |||
| display: flex; | |||
| flex-direction: row; | |||
| } | |||
| .btinfo{ | |||
| height:100px; | |||
| } | |||
| .contentview { | |||
| margin: 0 10px; | |||
| } | |||
| .button { | |||
| margin: 5px; | |||
| } | |||
| .myview{ | |||
| height:200px; | |||
| word-break:break-all;/* 自动换行 */ | |||
| } | |||
| @ -0,0 +1,212 @@ | |||
| const app = getApp() | |||
| function inArray(arr, key, val) { | |||
| for (let i = 0; i < arr.length; i++) { | |||
| if (arr[i][key] === val) { | |||
| return i; | |||
| } | |||
| } | |||
| return -1; | |||
| } | |||
| // ArrayBuffer转16进度字符串示例 | |||
| function ab2hex(buffer) { | |||
| var hexArr = Array.prototype.map.call( | |||
| new Uint8Array(buffer), | |||
| function (bit) { | |||
| return ('00' + bit.toString(16)).slice(-2) | |||
| } | |||
| ) | |||
| return hexArr.join(''); | |||
| } | |||
| Page({ | |||
| data: { | |||
| devices: [], | |||
| connected: false, | |||
| chs: [], | |||
| }, | |||
| openBluetoothAdapter() { | |||
| wx.openBluetoothAdapter({ | |||
| success: (res) => { | |||
| console.log('openBluetoothAdapter success', res) | |||
| this.startBluetoothDevicesDiscovery() | |||
| }, | |||
| fail: (res) => { | |||
| if (res.errCode === 10001) { | |||
| wx.onBluetoothAdapterStateChange(function (res) { | |||
| console.log('onBluetoothAdapterStateChange', res) | |||
| if (res.available) { | |||
| this.startBluetoothDevicesDiscovery() | |||
| } | |||
| }) | |||
| } | |||
| } | |||
| }) | |||
| }, | |||
| getBluetoothAdapterState() { | |||
| wx.getBluetoothAdapterState({ | |||
| success: (res) => { | |||
| console.log('getBluetoothAdapterState', res) | |||
| if (res.discovering) { | |||
| this.onBluetoothDeviceFound() | |||
| } else if (res.available) { | |||
| this.startBluetoothDevicesDiscovery() | |||
| } | |||
| } | |||
| }) | |||
| }, | |||
| startBluetoothDevicesDiscovery() { | |||
| if (this._discoveryStarted) { | |||
| return | |||
| } | |||
| this._discoveryStarted = true | |||
| wx.startBluetoothDevicesDiscovery({ | |||
| allowDuplicatesKey: true, | |||
| success: (res) => { | |||
| console.log('startBluetoothDevicesDiscovery success', res) | |||
| this.onBluetoothDeviceFound() | |||
| }, | |||
| }) | |||
| }, | |||
| stopBluetoothDevicesDiscovery() { | |||
| wx.stopBluetoothDevicesDiscovery() | |||
| }, | |||
| onBluetoothDeviceFound() { | |||
| wx.onBluetoothDeviceFound((res) => { | |||
| res.devices.forEach(device => { | |||
| if (!device.name && !device.localName) { | |||
| return | |||
| } | |||
| const foundDevices = this.data.devices | |||
| const idx = inArray(foundDevices, 'deviceId', device.deviceId) | |||
| const data = {} | |||
| if (idx === -1) { | |||
| data[`devices[${foundDevices.length}]`] = device | |||
| } else { | |||
| data[`devices[${idx}]`] = device | |||
| } | |||
| this.setData(data) | |||
| }) | |||
| }) | |||
| }, | |||
| createBLEConnection(e) { | |||
| const ds = e.currentTarget.dataset | |||
| const deviceId = ds.deviceId | |||
| const name = ds.name | |||
| wx.createBLEConnection({ | |||
| deviceId, | |||
| success: (res) => { | |||
| this.setData({ | |||
| connected: true, | |||
| name, | |||
| deviceId, | |||
| }) | |||
| this.getBLEDeviceServices(deviceId) | |||
| } | |||
| }) | |||
| this.stopBluetoothDevicesDiscovery() | |||
| }, | |||
| closeBLEConnection() { | |||
| wx.closeBLEConnection({ | |||
| deviceId: this.data.deviceId | |||
| }) | |||
| this.setData({ | |||
| connected: false, | |||
| chs: [], | |||
| canWrite: false, | |||
| }) | |||
| }, | |||
| getBLEDeviceServices(deviceId) { | |||
| wx.getBLEDeviceServices({ | |||
| deviceId, | |||
| success: (res) => { | |||
| for (let i = 0; i < res.services.length; i++) { | |||
| if (res.services[i].isPrimary) { | |||
| this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid) | |||
| return | |||
| } | |||
| } | |||
| } | |||
| }) | |||
| }, | |||
| getBLEDeviceCharacteristics(deviceId, serviceId) { | |||
| wx.getBLEDeviceCharacteristics({ | |||
| deviceId, | |||
| serviceId, | |||
| success: (res) => { | |||
| console.log('getBLEDeviceCharacteristics success', res.characteristics) | |||
| for (let i = 0; i < res.characteristics.length; i++) { | |||
| let item = res.characteristics[i] | |||
| if (item.properties.read) { | |||
| wx.readBLECharacteristicValue({ | |||
| deviceId, | |||
| serviceId, | |||
| characteristicId: item.uuid, | |||
| }) | |||
| } | |||
| if (item.properties.write) { | |||
| this.setData({ | |||
| canWrite: true | |||
| }) | |||
| this._deviceId = deviceId | |||
| this._serviceId = serviceId | |||
| this._characteristicId = item.uuid | |||
| this.writeBLECharacteristicValue() | |||
| } | |||
| if (item.properties.notify || item.properties.indicate) { | |||
| wx.notifyBLECharacteristicValueChange({ | |||
| deviceId, | |||
| serviceId, | |||
| characteristicId: item.uuid, | |||
| state: true, | |||
| }) | |||
| } | |||
| } | |||
| }, | |||
| fail(res) { | |||
| console.error('getBLEDeviceCharacteristics', res) | |||
| } | |||
| }) | |||
| // 操作之前先监听,保证第一时间获取数据 | |||
| wx.onBLECharacteristicValueChange((characteristic) => { | |||
| const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId) | |||
| const data = {} | |||
| if (idx === -1) { | |||
| data[`chs[${this.data.chs.length}]`] = { | |||
| uuid: characteristic.characteristicId, | |||
| value: ab2hex(characteristic.value) | |||
| } | |||
| } else { | |||
| data[`chs[${idx}]`] = { | |||
| uuid: characteristic.characteristicId, | |||
| value: ab2hex(characteristic.value) | |||
| } | |||
| } | |||
| // data[`chs[${this.data.chs.length}]`] = { | |||
| // uuid: characteristic.characteristicId, | |||
| // value: ab2hex(characteristic.value) | |||
| // } | |||
| this.setData(data) | |||
| }) | |||
| }, | |||
| writeBLECharacteristicValue() { | |||
| // 向蓝牙设备发送一个0x00的16进制数据 | |||
| let buffer = new ArrayBuffer(1) | |||
| let dataView = new DataView(buffer) | |||
| dataView.setUint8(0, Math.random() * 255 | 0) | |||
| wx.writeBLECharacteristicValue({ | |||
| deviceId: this._deviceId, | |||
| serviceId: this._deviceId, | |||
| characteristicId: this._characteristicId, | |||
| value: buffer, | |||
| }) | |||
| }, | |||
| closeBluetoothAdapter() { | |||
| wx.closeBluetoothAdapter() | |||
| this._discoveryStarted = false | |||
| }, | |||
| }) | |||
| @ -0,0 +1,3 @@ | |||
| { | |||
| "usingComponents": {} | |||
| } | |||
| @ -0,0 +1,41 @@ | |||
| <wxs module="utils"> | |||
| module.exports.max = function(n1, n2) { | |||
| return Math.max(n1, n2) | |||
| } | |||
| module.exports.len = function(arr) { | |||
| arr = arr || [] | |||
| return arr.length | |||
| } | |||
| </wxs> | |||
| <button bindtap="openBluetoothAdapter">开始扫描</button> | |||
| <button bindtap="stopBluetoothDevicesDiscovery">停止扫描</button> | |||
| <button bindtap="closeBluetoothAdapter">结束流程</button> | |||
| <view class="devices_summary">已发现 {{devices.length}} 个外围设备:</view> | |||
| <scroll-view class="device_list" scroll-y scroll-with-animation> | |||
| <view wx:for="{{devices}}" wx:key="index" | |||
| data-device-id="{{item.deviceId}}" | |||
| data-name="{{item.name || item.localName}}" | |||
| bindtap="createBLEConnection" | |||
| class="device_item" | |||
| hover-class="device_item_hover"> | |||
| <view style="font-size: 16px; color: #333;">{{item.name}}</view> | |||
| <view style="font-size: 10px">信号强度: {{item.RSSI}}dBm ({{utils.max(0, item.RSSI + 100)}}%)</view> | |||
| <view style="font-size: 10px">UUID: {{item.deviceId}}</view> | |||
| <view style="font-size: 10px">Service数量: {{utils.len(item.advertisServiceUUIDs)}}</view> | |||
| </view> | |||
| </scroll-view> | |||
| <view class="connected_info" wx:if="{{connected}}"> | |||
| <view> | |||
| <text>已连接到 {{name}}</text> | |||
| <view class="operation"> | |||
| <button wx:if="{{canWrite}}" size="mini" bindtap="writeBLECharacteristicValue">写数据</button> | |||
| <button size="mini" bindtap="closeBLEConnection">断开连接</button> | |||
| </view> | |||
| </view> | |||
| <view wx:for="{{chs}}" wx:key="index" style="font-size: 12px; margin-top: 10px;"> | |||
| <view>特性UUID: {{item.uuid}}</view> | |||
| <view>特性值: {{item.value}}</view> | |||
| </view> | |||
| </view> | |||
| @ -0,0 +1,41 @@ | |||
| page { | |||
| color: #333; | |||
| } | |||
| .devices_summary { | |||
| margin-top: 30px; | |||
| padding: 10px; | |||
| font-size: 16px; | |||
| } | |||
| .device_list { | |||
| height: 300px; | |||
| margin: 50px 5px; | |||
| margin-top: 0; | |||
| border: 1px solid #EEE; | |||
| border-radius: 5px; | |||
| width: auto; | |||
| } | |||
| .device_item { | |||
| border-bottom: 1px solid #EEE; | |||
| padding: 10px; | |||
| color: #666; | |||
| } | |||
| .device_item_hover { | |||
| background-color: rgba(0, 0, 0, .1); | |||
| } | |||
| .connected_info { | |||
| position: fixed; | |||
| bottom: 0; | |||
| width: 100%; | |||
| background-color: #F0F0F0; | |||
| padding: 10px; | |||
| padding-bottom: 20px; | |||
| margin-bottom: env(safe-area-inset-bottom); | |||
| font-size: 14px; | |||
| min-height: 100px; | |||
| box-shadow: 0px 0px 3px 0px; | |||
| } | |||
| .connected_info .operation { | |||
| position: absolute; | |||
| display: inline-block; | |||
| right: 30px; | |||
| } | |||
| @ -0,0 +1,637 @@ | |||
| // pages/lanyatest/lanyatest.js | |||
| var longUtil = require('./../../utils/long.js'); | |||
| Page({ | |||
| /** | |||
| * 页面的初始数据 | |||
| */ | |||
| data: { | |||
| info:"未初始化蓝牙适配器", | |||
| deviceName:"yxwl01680000004895", | |||
| connectedDeviceId:"", | |||
| deviceId:"", | |||
| services:"", | |||
| mtuSize:512, | |||
| servicesUUID:"000018f0-0000-1000-8000-00805f9b34fb", | |||
| notifyUUID:"00002af0-0000-1000-8000-00805f9b34fb", | |||
| writeUUID:"00002af1-0000-1000-8000-00805f9b34fb", | |||
| serviceId:"", | |||
| notifyCharacteristicsId:"", | |||
| writeCharacteristicsId: "", | |||
| userTelephone:"13028878230", | |||
| userPassword:"130288", | |||
| pwd:"12344321", | |||
| openLockType:2, | |||
| sendmsg:"8989415B017BC796C6F8001DA0B0AAB7AFABB1B6B9B1BAB7B0BE8989A9AB8BABBE80EEEEE2E2E6E6EFE4", | |||
| systemInfo:{}, | |||
| }, | |||
| lanyatest1(event){ | |||
| var that = this; | |||
| wx.getSystemInfo({ | |||
| success: function (res) { | |||
| console.log(res.model) | |||
| console.log(res.pixelRatio) | |||
| console.log(res.windowWidth) | |||
| console.log(res.windowHeight) | |||
| console.log(res.language) | |||
| console.log(res.version) | |||
| console.log(res.platform) | |||
| console.log(res.environment) | |||
| that.setData({ | |||
| systemInfo:res | |||
| }); | |||
| } | |||
| }) | |||
| checkWechatVersion(that.data.systemInfo) | |||
| wx.openBluetoothAdapter({ | |||
| success: function (res) { | |||
| console.log('初始化蓝牙适配器成功') | |||
| //页面日志显示 | |||
| that.setData({ | |||
| info: '初始化蓝牙适配器成功' | |||
| }) | |||
| }, | |||
| fail: function (res) { | |||
| console.log('请打开蓝牙和定位功能') | |||
| that.setData({ | |||
| info: '请打开蓝牙和定位功能' | |||
| }) | |||
| } | |||
| }) | |||
| }, | |||
| lanyatest2(event){ | |||
| var that = this; | |||
| wx.getBluetoothAdapterState({ | |||
| success: function (res) { | |||
| //打印相关信息 | |||
| console.log(JSON.stringify(res.errMsg) + "\n蓝牙是否可用:" + res.available); | |||
| that.setData({ | |||
| info: JSON.stringify(res.errMsg) +"\n蓝牙是否可用:" + res.available | |||
| }) | |||
| }, | |||
| fail: function (res) { | |||
| //打印相关信息 | |||
| console.log(JSON.stringify(res.errMsg) + "\n蓝牙是否可用:" + res.available); | |||
| that.setData({ | |||
| info: JSON.stringify(res.errMsg) + "\n蓝牙是否可用:" + res.available | |||
| }) | |||
| } | |||
| }) | |||
| }, | |||
| lanyatest3(event){ | |||
| var that = this; | |||
| wx.startBluetoothDevicesDiscovery({ | |||
| services: [that.data.servicesUUID], //如果填写了此UUID,那么只会搜索出含有这个UUID的设备 | |||
| success: function (res) { | |||
| that.setData({ | |||
| info: "搜索设备" + JSON.stringify(res), | |||
| }) | |||
| console.log('搜索设备返回' + JSON.stringify(res)) | |||
| } | |||
| }) | |||
| }, | |||
| lanyatest4(event){ | |||
| var that = this; | |||
| var pos; | |||
| wx.getBluetoothDevices({ | |||
| success: function (res) { | |||
| for (var i = 0; i < res.devices.length; i++) { | |||
| pos = that.data.deviceName.indexOf(res.devices[i].name); | |||
| if(pos != -1) | |||
| { | |||
| that.setData({ | |||
| connectedDeviceId: res.devices[i].deviceId | |||
| }) | |||
| console.log('设备号:' + res.devices[i].name + " Mac地址: " + that.data.connectedDeviceId + " pos: " + pos + "\n") | |||
| break; | |||
| } | |||
| } | |||
| that.setData({ | |||
| info: "设备列表\n" + JSON.stringify(res.devices), | |||
| devices: res.devices | |||
| }) | |||
| console.log('搜设备数目:' + res.devices.length) | |||
| console.log('设备信息:\n' + JSON.stringify(res.devices)+"\n") | |||
| } | |||
| }) | |||
| }, | |||
| lanyaconnect(event){ | |||
| var that = this; | |||
| wx.createBLEConnection({ | |||
| deviceId: that.data.connectedDeviceId, | |||
| success: function (res) { | |||
| console.log('调试信息:' + res.errMsg); | |||
| that.setData({ | |||
| info: "MAC地址:" + event.currentTarget.id + ' 调试信息:' + res.errMsg, | |||
| }) | |||
| if (that.data.systemInfo.platform == 'android') | |||
| { | |||
| wx.setBLEMTU({ | |||
| deviceId: that.data.connectedDeviceId, | |||
| mtu:that.data.mtuSize, | |||
| success:(res)=>{ | |||
| console.log("setBLEMTU success >> " + JSON.stringify(res)) | |||
| }, | |||
| fail:(res)=>{ | |||
| console.log("setBLEMTU fail >> " + JSON.stringify(res)) | |||
| } | |||
| }) | |||
| } | |||
| }, | |||
| fail: function () { | |||
| console.log("连接失败"); | |||
| }, | |||
| }) | |||
| }, | |||
| lanyatest6(event){ | |||
| var that = this; | |||
| wx.stopBluetoothDevicesDiscovery({ | |||
| success: function (res) { | |||
| console.log("停止搜索" + JSON.stringify(res.errMsg)); | |||
| that.setData({ | |||
| info: "停止搜索" + JSON.stringify(res.errMsg), | |||
| }) | |||
| } | |||
| }) | |||
| }, | |||
| lanyatest7(event){ | |||
| var that = this; | |||
| wx.getBLEDeviceServices({ | |||
| // 这里的 deviceId 需要在上面的 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取 | |||
| deviceId: that.data.connectedDeviceId, | |||
| success: function (res) { | |||
| console.log('services UUID:\n', JSON.stringify(res.services)); | |||
| for (var i = 0; i < res.services.length; i++) { | |||
| console.log("第"+(i+1) + "个UUID:" + res.services[i].uuid+"\n") | |||
| } | |||
| that.setData({ | |||
| services: res.services, | |||
| info: JSON.stringify(res.services), | |||
| }) | |||
| } | |||
| }) | |||
| }, | |||
| lanyatest8(event){ | |||
| var that = this; | |||
| var myUUID = toPlatformString(that.data.systemInfo, that.data.servicesUUID);//具有写、通知属性的服务uuid | |||
| console.log(' UUID: ' + myUUID + " mac: " + that.data.connectedDeviceId) | |||
| wx.getBLEDeviceCharacteristics({ | |||
| // 这里的 deviceId 需要在上面的接口中获取 | |||
| deviceId: that.data.connectedDeviceId, | |||
| // 这里的 serviceId 需要在上面的 接口中获取 | |||
| serviceId: myUUID, | |||
| success: function (res) { | |||
| console.log("%c getBLEDeviceCharacteristics", "color:red;"); | |||
| for (var i = 0; i < res.characteristics.length; i++) { | |||
| console.log('特征值:' + res.characteristics[i].uuid) | |||
| if (res.characteristics[i].properties.notify) { | |||
| console.log("notifyServicweId:", myUUID); | |||
| console.log("notifyCharacteristicsId:", res.characteristics[i].uuid); | |||
| that.setData({ | |||
| notifyServicweId: myUUID, | |||
| notifyCharacteristicsId: that.data.notifyUUID,//手动设置notifyCharacteristicsId为这个UUID,为了方便写死在这里 | |||
| }) | |||
| } | |||
| if (res.characteristics[i].properties.write) { | |||
| console.log("writeServicweId:", myUUID); | |||
| console.log("writeCharacteristicsId:", res.characteristics[i].uuid); | |||
| that.setData({ | |||
| writeServicweId: myUUID, | |||
| //writeCharacteristicsId: res.characteristics[i].uuid, | |||
| writeCharacteristicsId: that.data.writeUUID,//手动设置writeCharacteristicsId为这个UUID,为了方便写死在这里 | |||
| }) | |||
| } | |||
| } | |||
| console.log('device getBLEDeviceCharacteristics:', res.characteristics); | |||
| that.setData({ | |||
| msg: JSON.stringify(res.characteristics), | |||
| }) | |||
| }, | |||
| fail: function () { | |||
| console.log("fail"); | |||
| }, | |||
| }) | |||
| }, | |||
| lanyatest9(event){ | |||
| var that = this; | |||
| var notifyServicweId = toPlatformString(that.data.systemInfo, that.data.servicesUUID); //具有写、通知属性的服务uuid | |||
| var notifyCharacteristicsId = toPlatformString(that.data.systemInfo, that.data.notifyCharacteristicsId); | |||
| console.log("启用notify的serviceId", notifyServicweId); | |||
| console.log("启用notify的notifyCharacteristicsId", notifyCharacteristicsId); | |||
| wx.notifyBLECharacteristicValueChange({ | |||
| state: true, // 启用 notify 功能 | |||
| deviceId: that.data.connectedDeviceId, | |||
| // 这里的 serviceId 就是that.data.servicesUUID | |||
| serviceId: notifyServicweId, | |||
| characteristicId: notifyCharacteristicsId, | |||
| success: function (res) { | |||
| console.log('notifyBLECharacteristicValueChange success', res.errMsg) | |||
| var msg = '启动notify:' + res.errMsg | |||
| that.setData({ | |||
| info: msg | |||
| }) | |||
| }, | |||
| fail: function () { | |||
| console.log('启动notify:' + res.errMsg); | |||
| }, | |||
| }) | |||
| }, | |||
| lanyatest10(event){ | |||
| var that = this; | |||
| console.log("开始接收数据"); | |||
| wx.onBLECharacteristicValueChange(function (res) { | |||
| console.log("characteristicId:" + res.characteristicId) | |||
| console.log("serviceId: " + res.serviceId) | |||
| console.log("deviceId: " + res.deviceId) | |||
| console.log("Length: " + res.value.byteLength) | |||
| console.log("hex value: " + ab2hex(res.value)) | |||
| that.setData({ | |||
| info: that.data.info + ab2hex(res.value) | |||
| }) | |||
| }) | |||
| }, | |||
| lanyatest11(event){ | |||
| var that = this | |||
| //var hex = that.data.sendmsg //要发送的信息 | |||
| var myUUID = toPlatformString(that.data.systemInfo, that.data.servicesUUID); //具有写、通知属性的服务uuid | |||
| var writeCharId = toPlatformString(that.data.systemInfo, that.data.writeCharacteristicsId); | |||
| //console.log('要发送的信息是:'+hex) | |||
| //var typedArray1 = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) { | |||
| // return parseInt(h, 16) | |||
| //})) | |||
| //console.log(typedArray1) | |||
| var typedArray = packData(that.data.userTelephone, that.data.userPassword, that.data.pwd, that.data.openLockType) | |||
| console.log(typedArray) | |||
| wx.writeBLECharacteristicValue({ | |||
| deviceId: that.data.connectedDeviceId, | |||
| serviceId: myUUID, | |||
| characteristicId: writeCharId, | |||
| // 这里的value是ArrayBuffer类型 | |||
| value: typedArray.buffer, | |||
| success: function (res) { | |||
| console.log('写入成功', res.errMsg) | |||
| }, | |||
| fail(res){ | |||
| console.log('写入失败', res.errMsg) | |||
| } | |||
| }) | |||
| }, | |||
| //获取输入框的数据 | |||
| getmsg(event){ | |||
| this.setData({ | |||
| sendmsg:event.detail.value | |||
| }) | |||
| }, | |||
| lanyatest12(event){ | |||
| var that = this; | |||
| wx.closeBLEConnection({ | |||
| deviceId: that.data.connectedDeviceId, | |||
| success: function (res) { | |||
| that.setData({ | |||
| connectedDeviceId: "", | |||
| }) | |||
| console.log('断开蓝牙设备成功:' + res.errMsg) | |||
| }, | |||
| fail:function(res){ | |||
| console.log('断开蓝牙设备失败:' + res.errMsg) | |||
| } | |||
| }) | |||
| }, | |||
| }) | |||
| function toPlatformString(systemInfo, str) | |||
| { | |||
| if (systemInfo.platform == 'android') | |||
| return str.toLowerCase(); | |||
| else if (systemInfo.platform == 'ios') | |||
| return str.toUpperCase(); | |||
| else | |||
| return str; | |||
| } | |||
| /** | |||
| * 版本比较 | |||
| */ | |||
| function versionCompare (ver1, ver2) { //版本比较 | |||
| // console.log("ver1" + ver1 + 'ver2' + ver2); | |||
| var version1pre = parseFloat(ver1) | |||
| var version2pre = parseFloat(ver2) | |||
| var version1next = parseInt(ver1.replace(version1pre + ".", "")) | |||
| var version2next = parseInt(ver2.replace(version2pre + ".", "")) | |||
| if (version1pre > version2pre) | |||
| return true | |||
| else if (version1pre < version2pre) | |||
| return false | |||
| else { | |||
| if (version1next > version2next) | |||
| return true | |||
| else | |||
| return false | |||
| } | |||
| } | |||
| /** | |||
| * 微信版本检测 | |||
| * Android从微信 6.5.7 开始支持,iOS从微信 6.5.6 开始支持 | |||
| */ | |||
| function checkWechatVersion(systemInfo) { | |||
| if (systemInfo.platform == 'android' && versionCompare('6.5.7', systemInfo.version)) { | |||
| wx.showModal({ | |||
| title: '提示', | |||
| content: '当前微信版本过低,请更新至最新版本体验', | |||
| showCancel: false | |||
| }) | |||
| }else if (systemInfo.platform == 'ios' && versionCompare('6.5.6', systemInfo.version)) { | |||
| wx.showModal({ | |||
| title: '提示', | |||
| content: '当前微信版本过低,请更新至最新版本体验', | |||
| showCancel: false | |||
| }) | |||
| } | |||
| } | |||
| function Uint8ArrayToString(fileData){ | |||
| var dataString = ""; | |||
| for (var i = 0; i < fileData.length; i++) { | |||
| dataString += String.fromCharCode(fileData[i]); | |||
| } | |||
| return dataString | |||
| } | |||
| // 微信官方给的ArrayBuffer转十六进制示例 | |||
| function ab2hex(buffer) { | |||
| var hexArr = Array.prototype.map.call( | |||
| new Uint8Array(buffer), | |||
| function (bit) { | |||
| return ('00' + bit.toString(16)).slice(-2) | |||
| } | |||
| ) | |||
| return hexArr.join(','); | |||
| } | |||
| //转成可展会的文字 | |||
| function hexCharCodeToStr(hexCharCodeStr) { | |||
| var trimedStr = hexCharCodeStr.trim(); | |||
| var rawStr = trimedStr.substr(0, 2).toLowerCase() === '0x' ? trimedStr.substr(2) : trimedStr; | |||
| var len = rawStr.length; | |||
| var curCharCode; | |||
| var resultStr = []; | |||
| for (var i = 0; i < len; i = i + 2) { | |||
| curCharCode = parseInt(rawStr.substr(i, 2), 16); | |||
| resultStr.push(String.fromCharCode(curCharCode)); | |||
| } | |||
| return resultStr.join(''); | |||
| } | |||
| //转换成需要的格式 | |||
| function buf2string(buffer) { | |||
| var arr = Array.prototype.map.call(new Uint8Array(buffer), x => x) | |||
| return arr.map((char, i) => { | |||
| return String.fromCharCode(char); | |||
| }).join(''); | |||
| } | |||
| function stringToUint8Array(str){ | |||
| let arr = []; | |||
| for (let i = 0, j = str.length; i < j; ++i) { | |||
| arr.push(str.charCodeAt(i)); | |||
| } | |||
| let tmpUint8Array = new Uint8Array(arr); | |||
| return tmpUint8Array | |||
| } | |||
| function decodeHexNibble(c) { | |||
| if (c >= 48 && c <= 57) { | |||
| return c - 48; | |||
| } else if (c >= 65 && c <= 70) { | |||
| return c - 65 + 10; | |||
| } else { | |||
| return c >= 97 && c <= 102 ? c - 97 + 10 : -1; | |||
| } | |||
| } | |||
| function decodeHexByte(s, pos) { | |||
| let hi = decodeHexNibble(s.charCodeAt(pos)); | |||
| let lo = decodeHexNibble(s.charCodeAt(pos + 1)); | |||
| return ((hi << 4) + lo); | |||
| } | |||
| function hexStringToBytesWithPadding(str, byteLen, padding) { | |||
| let len = str.length | |||
| let length = 2 * byteLen | |||
| if (length >= len && (length & 1) == 0) { | |||
| if (length == 0) { | |||
| return [0]; | |||
| } else { | |||
| //let buffer = new ArrayBuffer(length >>> 1); | |||
| //let bytes = new Uint8Array(buffer); | |||
| let bytes = new Uint8Array(length >>> 1); | |||
| for(let i = 0; i < len; i += 2) { | |||
| bytes[i >>> 1] = decodeHexByte(str, i); | |||
| } | |||
| for(let i = 0; i < byteLen-len/2; i++) | |||
| bytes[len/2+i] = padding; | |||
| return bytes; | |||
| } | |||
| } | |||
| else | |||
| return [0]; | |||
| } | |||
| function hexStringToBytes(str) { | |||
| let s = str; | |||
| if(s.length < 12) | |||
| { | |||
| let tmp = 12-s.length | |||
| for(let i = 0; i< tmp; i++) | |||
| s = "0" + s; | |||
| } | |||
| else | |||
| s = str.substr(s.length-12, s.length); | |||
| let start = 0 | |||
| let length = s.length | |||
| if (length >= 0 && (length & 1) == 0) { | |||
| if (length == 0) { | |||
| return new byte[0]; | |||
| } else { | |||
| let bytes = new Uint8Array(length >>> 1); | |||
| for(let i = 0; i < length; i += 2) { | |||
| bytes[i >>> 1] = decodeHexByte(s, start + i); | |||
| } | |||
| return bytes; | |||
| } | |||
| } | |||
| else | |||
| return [0]; | |||
| } | |||
| function packData(userTelephone, userPassword, pwd, openLockType) | |||
| { | |||
| let cmd = 0x41 | |||
| let ut = stringToUint8Array(userTelephone) | |||
| let up = stringToUint8Array(userPassword) | |||
| let p = hexStringToBytesWithPadding(pwd, 10, 0xff) | |||
| let length = 1+11+6+10+1 | |||
| let b = new Uint8Array(length) | |||
| b[0] = cmd | |||
| b.set(ut, 1) | |||
| b.set(up, 12) | |||
| b.set(p, 18) | |||
| b[28] = openLockType | |||
| console.log(b) | |||
| return packMsg(0x41, b, length) | |||
| } | |||
| function packMsg(cmd, data, payloadLength) | |||
| { | |||
| let length = 13 + payloadLength | |||
| //let buffer = new ArrayBuffer(length); | |||
| let b = new Uint8Array(length); | |||
| b[0] = 0x89; | |||
| b[1] = 0x89; | |||
| b[2] = cmd; | |||
| let timestamp = new Date().getTime(); | |||
| let ts = timestamp.toString(16) | |||
| let t = hexStringToBytes(ts) | |||
| b.set(t, 4) | |||
| b[3] = b[9]; | |||
| b[10] = (payloadLength>>8)&0xff; | |||
| b[11] = payloadLength & 0xff; | |||
| let key = b[3]; | |||
| for (let i=0; i<payloadLength; i++) { | |||
| data[i] -= (payloadLength - i) ^ key; | |||
| data[i] = (data[i] ^ (key ^ i)); | |||
| } | |||
| b.set(data, 12) | |||
| let xor = b[0]; | |||
| for (let i=1; i<length-1; i++) | |||
| xor ^= b[i]; | |||
| b[length-1] = xor; | |||
| return b; | |||
| } | |||
| @ -0,0 +1,3 @@ | |||
| { | |||
| "usingComponents": {} | |||
| } | |||
| @ -0,0 +1,29 @@ | |||
| <!--pages/lanyatest/lanyatest.wxml--> | |||
| <view class="contentview"> | |||
| <view class='myview' > | |||
| {{info}} | |||
| </view> | |||
| <button type="primary" class="button" bindtap="lanyatest1">1初始化蓝牙</button> | |||
| <button type="primary" class="button" bindtap="lanyatest2">2获取蓝牙状态</button> | |||
| <button type="primary" class="button" bindtap="lanyatest3">3搜索周边设备</button> | |||
| <button type="primary" class="button" bindtap="lanyatest4">4获取所有设备</button> | |||
| <block wx:for="{{devices}}" wx:key="{{test}}"> | |||
| <button type="primary" class="button" id="{{item.deviceId}}" style='background-color:red' bindtap="lanyaconnect">5连接{{item.name}} </button> | |||
| </block> | |||
| <button type="primary" class="button" bindtap="lanyatest6">6停止搜索周边蓝牙设备</button> | |||
| <button type="primary" class="button" bindtap="lanyatest7">7获取所有service</button> | |||
| <button type="primary" class="button" bindtap="lanyatest8">8获取所有特征值</button> | |||
| <button type="primary" class="button" bindtap="lanyatest9">9启用特征值变化notify</button> | |||
| <button type="primary" class="button" bindtap="lanyatest10">10接收蓝牙返回消息</button> | |||
| <view class="section"> | |||
| <input placeholder='请输入要发送的信息' bindinput='getmsg'/> | |||
| </view> | |||
| <button type="primary" class="button" bindtap="lanyatest11">11微信发送消息</button> | |||
| <button type="primary" class="button" bindtap="lanyatest12">12断开蓝牙设备</button> | |||
| </view> | |||
| @ -0,0 +1,28 @@ | |||
| /* pages/lanyatest/lanyatest.wxss */ | |||
| .vertical{ | |||
| display: flex; | |||
| flex-direction: column; | |||
| } | |||
| /**index.wxss**/ | |||
| .horizontal{ | |||
| display: flex; | |||
| flex-direction: row; | |||
| } | |||
| .btinfo{ | |||
| height:100px; | |||
| } | |||
| .contentview { | |||
| margin: 0 10px; | |||
| } | |||
| .button { | |||
| margin: 5px; | |||
| } | |||
| .myview{ | |||
| height:200px; | |||
| word-break:break-all;/* 自动换行 */ | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| // logs.js | |||
| const util = require('../../utils/util.js') | |||
| Page({ | |||
| data: { | |||
| logs: [] | |||
| }, | |||
| onLoad() { | |||
| this.setData({ | |||
| logs: (wx.getStorageSync('logs') || []).map(log => { | |||
| return { | |||
| date: util.formatTime(new Date(log)), | |||
| timeStamp: log | |||
| } | |||
| }) | |||
| }) | |||
| } | |||
| }) | |||
| @ -0,0 +1,4 @@ | |||
| { | |||
| "navigationBarTitleText": "查看启动日志", | |||
| "usingComponents": {} | |||
| } | |||
| @ -0,0 +1,6 @@ | |||
| <!--logs.wxml--> | |||
| <view class="container log-list"> | |||
| <block wx:for="{{logs}}" wx:key="timeStamp" wx:for-item="log"> | |||
| <text class="log-item">{{index + 1}}. {{log.date}}</text> | |||
| </block> | |||
| </view> | |||
| @ -0,0 +1,8 @@ | |||
| .log-list { | |||
| display: flex; | |||
| flex-direction: column; | |||
| padding: 40rpx; | |||
| } | |||
| .log-item { | |||
| margin: 10rpx; | |||
| } | |||
| @ -0,0 +1,74 @@ | |||
| { | |||
| "description": "项目配置文件", | |||
| "packOptions": { | |||
| "ignore": [] | |||
| }, | |||
| "setting": { | |||
| "urlCheck": true, | |||
| "es6": true, | |||
| "enhance": true, | |||
| "postcss": true, | |||
| "preloadBackgroundData": false, | |||
| "minified": true, | |||
| "newFeature": false, | |||
| "coverView": true, | |||
| "nodeModules": false, | |||
| "autoAudits": false, | |||
| "showShadowRootInWxmlPanel": true, | |||
| "scopeDataCheck": false, | |||
| "uglifyFileName": false, | |||
| "checkInvalidKey": true, | |||
| "checkSiteMap": true, | |||
| "uploadWithSourceMap": true, | |||
| "compileHotReLoad": false, | |||
| "lazyloadPlaceholderEnable": false, | |||
| "useMultiFrameRuntime": true, | |||
| "useApiHook": true, | |||
| "useApiHostProcess": true, | |||
| "babelSetting": { | |||
| "ignore": [], | |||
| "disablePlugins": [], | |||
| "outputPath": "" | |||
| }, | |||
| "enableEngineNative": false, | |||
| "useIsolateContext": true, | |||
| "userConfirmedBundleSwitch": false, | |||
| "packNpmManually": false, | |||
| "packNpmRelationList": [], | |||
| "minifyWXSS": true, | |||
| "showES6CompileOption": false | |||
| }, | |||
| "compileType": "miniprogram", | |||
| "libVersion": "2.19.4", | |||
| "appid": "wx7775ba175d855273", | |||
| "projectname": "%E8%93%9D%E7%89%99%E6%B5%8B%E8%AF%95", | |||
| "debugOptions": { | |||
| "hidedInDevtools": [] | |||
| }, | |||
| "scripts": {}, | |||
| "staticServerOptions": { | |||
| "baseURL": "", | |||
| "servePath": "" | |||
| }, | |||
| "isGameTourist": false, | |||
| "condition": { | |||
| "search": { | |||
| "list": [] | |||
| }, | |||
| "conversation": { | |||
| "list": [] | |||
| }, | |||
| "game": { | |||
| "list": [] | |||
| }, | |||
| "plugin": { | |||
| "list": [] | |||
| }, | |||
| "gamePlugin": { | |||
| "list": [] | |||
| }, | |||
| "miniprogram": { | |||
| "list": [] | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,7 @@ | |||
| { | |||
| "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", | |||
| "rules": [{ | |||
| "action": "allow", | |||
| "page": "*" | |||
| }] | |||
| } | |||
| @ -0,0 +1,59 @@ | |||
| import BLEHandler from "./bleHandler" | |||
| class BLE extends BLEHandler { | |||
| constructor(blename, emitter) { | |||
| super(blename, emitter) | |||
| } | |||
| listen(callback) { | |||
| // 蓝牙事件注册,打开channel | |||
| this.emitter.removeAllListeners("channel") | |||
| this.emitter.on("channel", callback) | |||
| } | |||
| removeListen() { | |||
| // 移除所有蓝牙事件 | |||
| this.emitter.removeAllListeners("channel") | |||
| } | |||
| async init() { | |||
| let flow = false | |||
| // 打开蓝牙适配器状态监听 | |||
| this.onBLEConnectionStateChange() | |||
| // 蓝牙适配器初始化 | |||
| await this.openAdapter() | |||
| // 搜索蓝牙设备 | |||
| await this.startSearch() | |||
| // 获取设备ID | |||
| flow = await this.onBluetoothFound() | |||
| if (!flow) return | |||
| // 停止搜索设备 | |||
| await this.stopSearchBluetooth() | |||
| // 连接蓝牙 | |||
| await this.connectBlue(); | |||
| //安卓手机需要设置MTU Size | |||
| await this.setBLEMTU(); | |||
| // 获取serviceId | |||
| await this.getBLEServices() | |||
| // 设置特征值 | |||
| await this.getCharacteristics(); | |||
| // 订阅特征值 | |||
| await this.notifyBLECharacteristicValueChange() | |||
| // 打开传输监听,等待设备反馈数据 | |||
| this.onBLECharacteristicValueChange() | |||
| } | |||
| // 发送指令 | |||
| async send(cmd, payload) { | |||
| let flow = await this.sentOrder(cmd, payload) | |||
| return flow | |||
| } | |||
| async close() { | |||
| await this.closeBLEConnection() | |||
| await this.closeBLEAdapter() | |||
| } | |||
| } | |||
| export { BLE }; | |||
| @ -0,0 +1,296 @@ | |||
| import * as t from "./tools" | |||
| /* | |||
| import { HTTP } from "../server"; | |||
| */ | |||
| /** | |||
| * 蓝牙工具类 | |||
| * 封装小程序蓝牙流程方法 | |||
| * 处理事件通信 | |||
| */ | |||
| class BLEHandler { | |||
| constructor(blename, emitter) { | |||
| this.blename = blename //厂家自定义的蓝牙设备名称 | |||
| this.emitter = emitter | |||
| this.readCharacteristicId = ""; //能从硬件返回的信息中自动提取 | |||
| this.writeCharacteristicId = "00002af1-0000-1000-8000-00805f9b34fb"; //能从硬件返回的信息中自动提取 | |||
| this.notifyCharacteristicId = "00002af0-0000-1000-8000-00805f9b34fb"; //能从硬件返回的信息中自动提取 | |||
| this.deviceId = ""; //蓝牙Mac地址 | |||
| this.serviceId = "000018f0-0000-1000-8000-00805f9b34fb";//具体的serviceId要自己根据硬件提供设置。 | |||
| this.receivePacket = []; | |||
| this.recPktLength = 0; | |||
| this.recPktOffset = 0; | |||
| } | |||
| async openAdapter() { | |||
| let [err, res] = await t._openAdapter.call(this); | |||
| if (err != null) { | |||
| this.emitter.emit("channel", { | |||
| type: "connect", | |||
| data: "未打开适配器" | |||
| }) | |||
| return false; | |||
| } | |||
| return true | |||
| } | |||
| async startSearch() { | |||
| let [err, res] = await t._startSearch.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| this.emitter.emit("channel", { | |||
| type: "connect", | |||
| data: "正在连接中..." | |||
| }) | |||
| } | |||
| async onBluetoothFound() { | |||
| let [err, res] = await t._onBluetoothFound.call(this); | |||
| if (err != null) { | |||
| this.emitter.emit("channel", { | |||
| type: "connect", | |||
| data: "未找到设备" | |||
| }) | |||
| return false; | |||
| } | |||
| return true | |||
| } | |||
| async getBluetoothDevices() { | |||
| let [err, res] = await t._getBluetoothDevices.call(this); | |||
| if (err != null) { | |||
| this.emitter.emit("channel", { | |||
| type: "connect", | |||
| data: "未找到设备" | |||
| }) | |||
| return false; | |||
| } | |||
| return true | |||
| } | |||
| async stopSearchBluetooth() { | |||
| let [err, res] = await t._stopSearchBluetooth.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| } | |||
| async connectBlue() { | |||
| let [err, res] = await t._connectBlue.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| } | |||
| async setBLEMTU() { | |||
| let [err, res] = await t._setBLEMTU.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| } | |||
| async getBLEServices() { | |||
| let [err, res] = await t._getBLEServices.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| } | |||
| async getCharacteristics() { | |||
| let [err, res] = await t._getCharacteristics.call(this); | |||
| if (err != null) { | |||
| this.emitter.emit("channel", { | |||
| type: "connect", | |||
| data: "无法订阅特征值" | |||
| }) | |||
| // 取消连接 | |||
| this.closeBLEConnection() | |||
| this.closeBLEAdapter() | |||
| wx.setStorageSync("bluestatus", ""); | |||
| return false; | |||
| } | |||
| this.emitter.emit("channel", { | |||
| type: "connect", | |||
| data: "蓝牙已连接" | |||
| }) | |||
| wx.setStorageSync("bluestatus", "on"); | |||
| return true | |||
| } | |||
| async notifyBLECharacteristicValueChange() { | |||
| let [err, res] = await t._notifyBLECharacteristicValueChange.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| } | |||
| async closeBLEConnection() { | |||
| let [err, res] = await t._closeBLEConnection.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| } | |||
| async closeBLEAdapter() { | |||
| let [err, res] = await t._closeBLEAdapter.call(this); | |||
| if (err != null) { | |||
| return; | |||
| } | |||
| } | |||
| async sentOrder(cmd, payload) { | |||
| let data = t._sentOrder(cmd, payload) | |||
| console.log("-- 发送数据:", data) | |||
| let arrayBuffer = new Uint8Array(data).buffer; | |||
| let [err, res] = await t._writeBLECharacteristicValue.call(this, arrayBuffer) | |||
| if (err != null) { | |||
| return false | |||
| } | |||
| console.log("数据发送成功!") | |||
| return true | |||
| } | |||
| // 打开蓝牙适配器状态监听 | |||
| onBLEConnectionStateChange() { | |||
| wx.onBLEConnectionStateChange(res => { | |||
| // 该方法回调中可以用于处理连接意外断开等异常情况 | |||
| if (!res.connected) { | |||
| this.closeBLEAdapter() | |||
| wx.setStorageSync("bluestatus", ""); | |||
| this.emitter.emit("channel", { | |||
| type: "connect", | |||
| data: "蓝牙已断开" | |||
| }) | |||
| } | |||
| }, err => { | |||
| console.log('err', err) | |||
| }) | |||
| } | |||
| // 收到设备推送的notification | |||
| onBLECharacteristicValueChange() { | |||
| wx.onBLECharacteristicValueChange(res => { | |||
| //let arrbf = new Uint8Array(res.value) | |||
| //console.log(`收到硬件数据反馈!命令码为:${arrbf[2]}`) | |||
| //if (this._checkData(arrbf)) { | |||
| //} | |||
| let arrbf = new Uint8Array(res.value) | |||
| if (arrbf[0] == 0x89 && arrbf[1] == 0x89) | |||
| { | |||
| let dataLength = ((arrbf[10] << 8) & 0xff00) + (arrbf[11] & 0x00ff) | |||
| this.recPktLength = 13 + dataLength | |||
| this.receivePacket = new Uint8Array(this.recPktLength) | |||
| this.recPktOffset = 0 | |||
| console.log("开始接收数据:length= " + arrbf.length + " data= " + arrbf) | |||
| this.receivePacket.set(arrbf, this.recPktOffset) | |||
| this.recPktOffset += arrbf.length; | |||
| if( this.recPktOffset == this.recPktLength){ | |||
| this.getReceivePacket(this.receivePacket) | |||
| } | |||
| } | |||
| else | |||
| { | |||
| console.log("继续接收数据:length= " + arrbf.length + " data= " + arrbf) | |||
| this.receivePacket.set(arrbf, this.recPktOffset) | |||
| this.recPktOffset += arrbf.length; | |||
| if( this.recPktOffset == this.recPktLength){ | |||
| this.getReceivePacket(this.receivePacket) | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| /* | |||
| _uploadInfo(message) { | |||
| console.log("-- 准备数据同步!", this._mapToArray(message)) | |||
| let bleorder = wx.getStorageSync("bleorder"); | |||
| let blecabinet = wx.getStorageSync("blecabinet") | |||
| HTTP({ | |||
| url: "cabinet/uploadBlueData", | |||
| methods: "post", | |||
| data: { | |||
| cabinetQrCode: blecabinet, | |||
| order: bleorder, | |||
| message: this._mapToArray(message) | |||
| } | |||
| }).then(res => { | |||
| console.log("✔ 数据同步成功!") | |||
| }, err => { | |||
| console.log('✘ 数据同步失败', err) | |||
| }) | |||
| } | |||
| */ | |||
| _mapToArray(arrbf) { | |||
| let arr = [] | |||
| arrbf.map(item => { | |||
| arr.push(item) | |||
| }) | |||
| return arr | |||
| } | |||
| // 校验数据正确性 | |||
| _checkData(arrbf) { | |||
| let packetLen = arrbf.length; | |||
| console.log("接收数据:" + arrbf) | |||
| // 校验帧头帧尾 | |||
| if (arrbf[0] != 0x89 || arrbf[1] != 0x89) { | |||
| console.log('不是该设备返回的包') | |||
| return false | |||
| } | |||
| if (packetLen < 14) { | |||
| console.log("包长太短,不是该设备返回的包"); | |||
| return false; | |||
| } | |||
| let dataLength = ((arrbf[10] << 8) & 0xff00) + (arrbf[11] & 0x00ff); | |||
| if (dataLength != (packetLen - 13)) { | |||
| let calLen = packetLen - 13; | |||
| console.log("✘ 数据长度错误: " + dataLength + " is not " + calLen); | |||
| return false; | |||
| } | |||
| //校验包 | |||
| let check = arrbf[0]; | |||
| for (let i = 1; i < packetLen - 1; i++) | |||
| check ^= arrbf[i]; | |||
| if (check != arrbf[packetLen - 1]) { | |||
| console.log("crc校验错误,请重发." + check); | |||
| return false; | |||
| } | |||
| console.log('✔ 数据校验成功,接收完整!') | |||
| return true | |||
| } | |||
| getReceivePacket(arrbf) | |||
| { | |||
| let packetLen = arrbf.length; | |||
| console.log("接收数据:" + arrbf) | |||
| // 校验帧头帧尾 | |||
| if (arrbf[0] != 0x89 || arrbf[1] != 0x89) { | |||
| console.log('不是该设备返回的包') | |||
| return false | |||
| } | |||
| if (packetLen < 14) { | |||
| console.log("包长太短,不是该设备返回的包"); | |||
| return false; | |||
| } | |||
| let dataLength = ((arrbf[10] << 8) & 0xff00) + (arrbf[11] & 0x00ff); | |||
| if (dataLength != (packetLen - 13)) { | |||
| let calLen = packetLen - 13; | |||
| console.log("✘ 数据长度错误: " + dataLength + " is not " + calLen); | |||
| return false; | |||
| } | |||
| //校验包 | |||
| let check = arrbf[0]; | |||
| for (let i = 1; i < packetLen - 1; i++) | |||
| check ^= arrbf[i]; | |||
| if (check != arrbf[packetLen - 1]) { | |||
| console.log("crc校验错误,请重发." + check); | |||
| return false; | |||
| } | |||
| console.log('✔ 数据校验成功,接收完整! packet= ' + arrbf) | |||
| return true | |||
| } | |||
| } | |||
| export default BLEHandler | |||
| @ -0,0 +1,44 @@ | |||
| export default function (err) { | |||
| if (err && err.errCode) { | |||
| // 微信BLE蓝牙错误码 | |||
| switch (err.errCode) { | |||
| case 10001: | |||
| return err.errCode + ":蓝牙适配失败,请检查手机蓝牙和定位功能是否打开" | |||
| case 10002: | |||
| return err.errCode + ":没有找到指定设备" | |||
| case 10003: | |||
| return err.errCode + ":连接失败" | |||
| case 10004: | |||
| return err.errCode + ":没有找到指定服务" | |||
| case 10005: | |||
| return err.errCode + ":没有找到指定特征值" | |||
| case 10006: | |||
| return err.errCode + ":当前连接已断开" | |||
| case 10007: | |||
| return err.errCode + ":当前特征值不支持此操作" | |||
| case 10008: | |||
| return err.errCode + ":其余所有系统上报的异常" | |||
| case 10009: | |||
| return err.errCode + ":Android 系统特有,系统版本低于 4.3 不支持 BLE" | |||
| case 10012: | |||
| return err.errCode + ":连接超时" | |||
| case 10013: | |||
| return err.errCode + ":连接 deviceId 为空或者是格式不正确" | |||
| default: | |||
| return "蓝牙功能暂不支持" | |||
| } | |||
| } else { | |||
| // 自定义错误码 | |||
| if (typeof err === 'string') { | |||
| switch (err) { | |||
| case 'device not found': | |||
| return "找不到该设备" | |||
| default: | |||
| break; | |||
| } | |||
| } else { | |||
| return "蓝牙功能暂不支持" | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,481 @@ | |||
| import errToString from "./error"; | |||
| var util = require('../util'); | |||
| let PRINT_SHOW = true //是否开启蓝牙调试 | |||
| let SYSTEM_INFO = {} | |||
| let MTU_SIZE = 512 | |||
| function toPlatformString(systemInfo, str) | |||
| { | |||
| if (systemInfo.platform == 'android') | |||
| return str.toLowerCase(); | |||
| else if (systemInfo.platform == 'ios') | |||
| return str.toUpperCase(); | |||
| else | |||
| return str; | |||
| } | |||
| function _openAdapter() { | |||
| print(`准备初始化蓝牙适配器...`); | |||
| wx.getSystemInfo({ | |||
| success: function (res) { | |||
| print(`model: ${res.model}`); | |||
| print(`pixelRatio: ${res.pixelRatio}`); | |||
| print(`windowWidth: ${res.windowWidth}`); | |||
| print(`windowHeight: ${res.windowHeight}`); | |||
| print(`language: ${res.language}`); | |||
| print(`version: ${res.version}`); | |||
| print(`platform: ${res.platform}`); | |||
| print(`environment: ${res.environment}`); | |||
| SYSTEM_INFO = res; | |||
| } | |||
| }) | |||
| return wx.openBluetoothAdapter().then( | |||
| (res) => { | |||
| print(`✔ 适配器初始化成功!`); | |||
| return [null, res] | |||
| }, | |||
| (err) => { | |||
| print(`✘ 初始化失败!${errToString(err)}`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| /** | |||
| * @param {Array<string>} services | |||
| * @param { Int } interval | |||
| */ | |||
| function _startSearch() { | |||
| print(`准备搜寻附近的蓝牙外围设备...`); | |||
| return promisify(wx.startBluetoothDevicesDiscovery, { | |||
| services: [this.serviceId], //如果填写了此UUID,那么只会搜索出含有这个UUID的设备 | |||
| allowDuplicatesKey: false, | |||
| interval: 1000 | |||
| }).then( | |||
| (res) => { | |||
| print(`✔ 搜索成功!`); | |||
| return [null, res] | |||
| }, | |||
| (err) => { | |||
| print(`✘ 搜索蓝牙设备失败!${errToString(err)}`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| /** | |||
| *@param {Array<string>} devices | |||
| *@deviceId 设备ID | |||
| */ | |||
| function _onBluetoothFound() { | |||
| print(`监听搜寻新设备事件...`); | |||
| return _onBluetoothFound_promise.call(this).then(res => { | |||
| print(`✔ 设备 ${this.blename} 查找成功!`); | |||
| return [null, res] | |||
| }, err => { | |||
| print(`✘ 设备 ${this.blename} 查找失败!`); | |||
| return [errToString(err), null] | |||
| }) | |||
| } | |||
| /** | |||
| * @param {Array} devices 查找到设备数组 | |||
| * @param {int} count 计数器-嗅探2次 | |||
| */ | |||
| function _onBluetoothFound_promise() { | |||
| let devices = [] | |||
| let count = 0 | |||
| return new Promise((resolve, reject) => { | |||
| wx.onBluetoothDeviceFound(res => { | |||
| devices.push(...res.devices) | |||
| print(`已嗅探设备数:${devices.length}...`) | |||
| print(`已嗅探设备信息:${JSON.stringify(devices)}`) | |||
| count++ | |||
| print(`已嗅探次数:${count}`) | |||
| //if (count > 1) | |||
| if(devices.length>0) | |||
| { | |||
| devices.forEach(element => { | |||
| if ((element.name && element.name == this.blename) || (element.localName && element.localName == this.blename)) { | |||
| this.deviceId = element.deviceId | |||
| print(`我的设备Mac:${this.deviceId} `) | |||
| print(`我的设备名:${element.name}, ${element.localName}`) | |||
| resolve(res) | |||
| } | |||
| }); | |||
| } | |||
| reject('device not found') | |||
| }, err => { | |||
| reject(err) | |||
| }) | |||
| }) | |||
| } | |||
| /** | |||
| *@param {Array<string>} devices | |||
| *@deviceId 设备ID | |||
| */ | |||
| function _getBluetoothDevices() { | |||
| print(`监听搜寻新设备事件...`); | |||
| let devices = [] | |||
| return wx.getBluetoothDevices().then( | |||
| (res) => { | |||
| devices.push(res.devices) | |||
| print(`已嗅探设备数:${res.devices.length}...`) | |||
| print(`已嗅探设备信息:${JSON.stringify(res.devices)}`) | |||
| if(devices.length>0) | |||
| { | |||
| devices.forEach(element => { | |||
| if ((element.name && element.name == this.blename) || (element.localName && element.localName == this.blename)) { | |||
| this.deviceId = element.deviceId | |||
| print(`我的设备Mac:${this.deviceId} `) | |||
| print(`我的设备名:${element.name}, ${element.localName}`) | |||
| print(`✔ 设备 ${this.blename} 查找成功!`); | |||
| return [null, res] | |||
| } | |||
| }); | |||
| } | |||
| return [ "暂时没有嗅探到设备 ${this.blename}, 继续嗅探...", null] | |||
| }, | |||
| (err) => { | |||
| print(`✘ 设备 ${this.blename} 查找失败!`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| function _getBluetoothDevices_1() { | |||
| print(`监听搜寻新设备事件...`); | |||
| return _getBluetoothDevices_promise.call(this).then(res => { | |||
| print(`✔ 设备 ${this.blename} 查找成功!`); | |||
| return [null, res] | |||
| }, err => { | |||
| print(`✘ 设备 ${this.blename} 查找失败!`); | |||
| return [errToString(err), null] | |||
| }) | |||
| } | |||
| /** | |||
| * @param {Array} devices 查找到设备数组 | |||
| * @param {int} count 计数器-嗅探2次 | |||
| */ | |||
| function _getBluetoothDevices_promise() { | |||
| let devices = [] | |||
| print(`1111`) | |||
| return new Promise((resolve, reject) => { | |||
| print(`2222`) | |||
| wx.getBluetoothDevices(res => { | |||
| print(`3333`) | |||
| devices.push(...res.devices) | |||
| print(`已嗅探设备数:${devices.length}...`) | |||
| print(`已嗅探设备信息:${JSON.stringify(devices)}`) | |||
| if(devices.length>0) | |||
| { | |||
| devices.forEach(element => { | |||
| if ((element.name && element.name == this.blename) || (element.localName && element.localName == this.blename)) { | |||
| this.deviceId = element.deviceId | |||
| print(`我的设备Mac:${this.deviceId} `) | |||
| print(`我的设备名:${element.name}, ${element.localName}`) | |||
| resolve(res) | |||
| } | |||
| }); | |||
| } | |||
| reject('device not found') | |||
| }, err => { | |||
| print(`4444`) | |||
| reject(err) | |||
| }) | |||
| }) | |||
| } | |||
| function _stopSearchBluetooth() { | |||
| print(`停止查找新设备...`); | |||
| return wx.stopBluetoothDevicesDiscovery().then( | |||
| (res) => { | |||
| print(`✔ 停止查找设备成功!`); | |||
| return [null, res] | |||
| }, | |||
| (err) => { | |||
| print(`✘ 停止查询设备失败!${errToString(err)}`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| function _connectBlue() { | |||
| print(`准备连接设备...`); | |||
| return promisify(wx.createBLEConnection, { | |||
| deviceId: this.deviceId, | |||
| }).then( | |||
| (res) => { | |||
| print(`✔ 连接蓝牙成功!`); | |||
| return [null, res] | |||
| }, | |||
| (err) => { | |||
| print(`✘ 连接蓝牙失败!${errToString(err)}`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| function _setBLEMTU() { | |||
| if (SYSTEM_INFO.platform != 'android') | |||
| { | |||
| print(`仅安卓手机需要设置MTU Size`); | |||
| return ["仅安卓手机需要设置MTU Size", null]; | |||
| } | |||
| print(`准备设置 MTU Size...`); | |||
| return promisify(wx.setBLEMTU, { | |||
| deviceId: this.deviceId, | |||
| mtu:MTU_SIZE | |||
| }).then( | |||
| (res) => { | |||
| //console.log("setBLEMTU success >> " + JSON.stringify(res)) | |||
| print(`✔ 设置MTU Size = ${MTU_SIZE} 成功!`); | |||
| return [null, res] | |||
| }, | |||
| (err) => { | |||
| //console.log("setBLEMTU fail >> " + JSON.stringify(res)) | |||
| print(`✘ 设置MTU Size 失败!`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| function _closeBLEConnection() { | |||
| print(`断开蓝牙连接...`) | |||
| return promisify(wx.closeBLEConnection, { | |||
| deviceId: this.deviceId, | |||
| }).then( | |||
| (res) => { | |||
| print(`✔ 断开蓝牙成功!`); | |||
| return [null, res] | |||
| }, | |||
| (err) => { | |||
| print(`✘ 断开蓝牙连接失败!${errToString(err)}`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| function _closeBLEAdapter() { | |||
| print(`释放蓝牙适配器...`) | |||
| return wx.closeBluetoothAdapter().then(res => { | |||
| print(`✔ 释放适配器成功!`) | |||
| return [null, res] | |||
| }, err => { | |||
| print(`✘ 释放适配器失败!${errToString(err)}`) | |||
| return [errToString(err), null] | |||
| }) | |||
| } | |||
| function _getBLEServices() { | |||
| print(`获取蓝牙设备所有服务...`) | |||
| return promisify(wx.getBLEDeviceServices, { | |||
| deviceId: this.deviceId | |||
| }).then(res => { | |||
| print(`✔ 获取service成功!`) | |||
| console.log('services UUID:\n', JSON.stringify(res.services)); | |||
| for (var i = 0; i < res.services.length; i++) { | |||
| console.log("第"+(i+1) + "个UUID:" + res.services[i].uuid+"\n") | |||
| } | |||
| return [null, res] | |||
| }, err => { | |||
| print(`✘ 获取service失败!${errToString(err)}`) | |||
| return [errToString(err), null] | |||
| }) | |||
| } | |||
| function _getCharacteristics() { | |||
| print(`开始获取特征值...`); | |||
| return promisify(wx.getBLEDeviceCharacteristics, { | |||
| deviceId: this.deviceId, | |||
| serviceId: toPlatformString(SYSTEM_INFO, this.serviceId),//具有写、通知属性的服务uuid | |||
| }).then( | |||
| (res) => { | |||
| print(`✔ 获取特征值成功!`); | |||
| for (let i = 0; i < res.characteristics.length; i++) { | |||
| let item = res.characteristics[i]; | |||
| if (item.properties.read) { | |||
| this.readCharacteristicId = item.uuid; | |||
| print(`readCharacteristicId:${item.uuid}`) | |||
| } | |||
| if (item.properties.write && !item.properties.read) { | |||
| this.writeCharacteristicId = item.uuid; | |||
| print(`writeCharacteristicId:${item.uuid}`) | |||
| } | |||
| if (item.properties.notify || item.properties.indicate) { | |||
| this.notifyCharacteristicId = item.uuid; | |||
| print(`notifyCharacteristicId:${item.uuid}`) | |||
| } | |||
| } | |||
| return [null, res] | |||
| }, | |||
| (err) => { | |||
| print(`✘ 获取特征值失败!${errToString(err)}`); | |||
| return [errToString(err), null] | |||
| } | |||
| ); | |||
| } | |||
| // 订阅特征值 | |||
| function _notifyBLECharacteristicValueChange() { | |||
| return promisify(wx.notifyBLECharacteristicValueChange, { | |||
| deviceId: this.deviceId, | |||
| serviceId: toPlatformString(SYSTEM_INFO, this.serviceId), | |||
| characteristicId: toPlatformString(SYSTEM_INFO, this.notifyCharacteristicId), | |||
| state: true | |||
| }).then(res => { | |||
| print(`✔ 订阅notify成功!`) | |||
| return [null, res] | |||
| }, err => { | |||
| print(`✘ 订阅notify失败!${errToString(err)}`) | |||
| return [errToString(err), null] | |||
| }) | |||
| } | |||
| /** | |||
| * 指令封装 | |||
| * @param {Array} data | |||
| */ | |||
| function _sentOrder(cmd, payload){ | |||
| print(`开始封装指令...`) | |||
| let data = payload | |||
| let payloadLength = data.length; | |||
| let length = 13 + payload.length | |||
| let b = new Uint8Array(length); | |||
| b[0] = 0x89; | |||
| b[1] = 0x89; | |||
| b[2] = cmd; | |||
| let timestamp = new Date().getTime(); | |||
| let ts = timestamp.toString(16) | |||
| let t = util.hexStringToBytes(ts) | |||
| b.set(t, 4) | |||
| b[3] = b[9]; | |||
| b[10] = (payloadLength>>8)&0xff; | |||
| b[11] = payloadLength & 0xff; | |||
| let key = b[3]; | |||
| for (let i=0; i<payloadLength; i++) { | |||
| data[i] -= (payloadLength - i) ^ key; | |||
| data[i] = (data[i] ^ (key ^ i)); | |||
| } | |||
| b.set(data, 12) | |||
| let xor = b[0]; | |||
| for (let i=1; i<length-1; i++) | |||
| xor ^= b[i]; | |||
| b[length-1] = xor; | |||
| print(`✔ 封装成功! ${b}`) | |||
| return b; | |||
| } | |||
| function _writeBLECharacteristicValue(data) { | |||
| return promisify(wx.writeBLECharacteristicValue, { | |||
| deviceId: this.deviceId, | |||
| serviceId: toPlatformString(SYSTEM_INFO, this.serviceId), | |||
| characteristicId: toPlatformString(SYSTEM_INFO, this.writeCharacteristicId), | |||
| value: data, | |||
| }).then(res => { | |||
| print(`✔ 写入数据成功!`) | |||
| return [null, res] | |||
| }, err => { | |||
| print(`✘ 写入数据失败!${errToString(err)}`) | |||
| return [errToString(err), null] | |||
| }) | |||
| } | |||
| /** | |||
| * 对微信接口的promise封装 | |||
| * @param {function} fn | |||
| * @param {object} args | |||
| */ | |||
| function promisify(fn, args) { | |||
| return new Promise((resolve, reject) => { | |||
| fn({ | |||
| ...(args || {}), | |||
| success: (res) => resolve(res), | |||
| fail: (err) => reject(err), | |||
| }); | |||
| }); | |||
| } | |||
| /** | |||
| * 对微信接口回调函数的封装 | |||
| * @param {function} fn | |||
| */ | |||
| function promisify_callback(fn) { | |||
| return new Promise((resolve, reject) => { | |||
| fn( | |||
| (res) => { | |||
| resolve(res); | |||
| }, | |||
| (rej) => { | |||
| reject(rej); | |||
| } | |||
| ); | |||
| }); | |||
| } | |||
| function print(str) { | |||
| PRINT_SHOW ? console.log(str) : null; | |||
| } | |||
| export { | |||
| print, | |||
| _getCharacteristics, | |||
| _connectBlue, | |||
| _setBLEMTU, | |||
| _getBLEServices, | |||
| _closeBLEConnection, | |||
| _closeBLEAdapter, | |||
| _stopSearchBluetooth, | |||
| _notifyBLECharacteristicValueChange, | |||
| _onBluetoothFound, | |||
| _getBluetoothDevices, | |||
| _startSearch, | |||
| _openAdapter, | |||
| _sentOrder, | |||
| _writeBLECharacteristicValue, | |||
| promisify, | |||
| promisify_callback, | |||
| }; | |||
| @ -0,0 +1,6 @@ | |||
| //公共配置项 | |||
| const globalData = { | |||
| appId: "", | |||
| baseUrlImg: "", | |||
| }; | |||
| export { globalData }; | |||
| @ -0,0 +1,153 @@ | |||
| function formatTime(date) { | |||
| const year = date.getFullYear() | |||
| const month = date.getMonth() + 1 | |||
| const day = date.getDate() | |||
| const hour = date.getHours() | |||
| const minute = date.getMinutes() | |||
| const second = date.getSeconds() | |||
| return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') | |||
| } | |||
| function formatNumber(n) { | |||
| n = n.toString() | |||
| return n[1] ? n : '0' + n | |||
| } | |||
| function uint8ArrayToString(fileData){ | |||
| let dataString = ""; | |||
| for (let i = 0; i < fileData.length; i++) { | |||
| dataString += String.fromCharCode(fileData[i]); | |||
| } | |||
| return dataString | |||
| } | |||
| function stringToUint8Array(str){ | |||
| let arr = []; | |||
| for (let i = 0, j = str.length; i < j; ++i) { | |||
| arr.push(str.charCodeAt(i)); | |||
| } | |||
| let tmpUint8Array = new Uint8Array(arr); | |||
| return tmpUint8Array | |||
| } | |||
| // 微信官方给的ArrayBuffer转十六进制示例 | |||
| function ab2hex(buffer) { | |||
| let hexArr = Array.prototype.map.call( | |||
| new Uint8Array(buffer), | |||
| function (bit) { | |||
| return ('00' + bit.toString(16)).slice(-2) | |||
| } | |||
| ) | |||
| return hexArr.join(','); | |||
| } | |||
| //转成可展会的文字 | |||
| function hexCharCodeToStr(hexCharCodeStr) { | |||
| let trimedStr = hexCharCodeStr.trim(); | |||
| let rawStr = trimedStr.substr(0, 2).toLowerCase() === '0x' ? trimedStr.substr(2) : trimedStr; | |||
| let len = rawStr.length; | |||
| let curCharCode; | |||
| let resultStr = []; | |||
| for (let i = 0; i < len; i = i + 2) { | |||
| curCharCode = parseInt(rawStr.substr(i, 2), 16); | |||
| resultStr.push(String.fromCharCode(curCharCode)); | |||
| } | |||
| return resultStr.join(''); | |||
| } | |||
| //转换成需要的格式 | |||
| function buf2string(buffer) { | |||
| let arr = Array.prototype.map.call(new Uint8Array(buffer), x => x) | |||
| return arr.map((char, i) => { | |||
| return String.fromCharCode(char); | |||
| }).join(''); | |||
| } | |||
| function decodeHexNibble(c) { | |||
| if (c >= 48 && c <= 57) { | |||
| return c - 48; | |||
| } else if (c >= 65 && c <= 70) { | |||
| return c - 65 + 10; | |||
| } else { | |||
| return c >= 97 && c <= 102 ? c - 97 + 10 : -1; | |||
| } | |||
| } | |||
| function decodeHexByte(s, pos) { | |||
| let hi = decodeHexNibble(s.charCodeAt(pos)); | |||
| let lo = decodeHexNibble(s.charCodeAt(pos + 1)); | |||
| return ((hi << 4) + lo); | |||
| } | |||
| function hexStringToBytesWithPadding(str, byteLen, padding) { | |||
| let len = str.length | |||
| let length = 2 * byteLen | |||
| if (length >= len && (length & 1) == 0) { | |||
| if (length == 0) { | |||
| return [0]; | |||
| } else { | |||
| let bytes = new Uint8Array(length >>> 1); | |||
| for(let i = 0; i < len; i += 2) { | |||
| bytes[i >>> 1] = decodeHexByte(str, i); | |||
| } | |||
| for(let i = 0; i < byteLen-len/2; i++) | |||
| bytes[len/2+i] = padding; | |||
| return bytes; | |||
| } | |||
| } | |||
| else | |||
| return [0]; | |||
| } | |||
| function hexStringToBytes(str) { | |||
| let s = str; | |||
| if(s.length < 12) | |||
| { | |||
| let tmp = 12-s.length | |||
| for(let i = 0; i< tmp; i++) | |||
| s = "0" + s; | |||
| } | |||
| else | |||
| s = str.substr(s.length-12, s.length); | |||
| let start = 0 | |||
| let length = s.length | |||
| if (length >= 0 && (length & 1) == 0) { | |||
| if (length == 0) { | |||
| return new byte[0]; | |||
| } else { | |||
| let bytes = new Uint8Array(length >>> 1); | |||
| for(let i = 0; i < length; i += 2) { | |||
| bytes[i >>> 1] = decodeHexByte(s, start + i); | |||
| } | |||
| return bytes; | |||
| } | |||
| } | |||
| else | |||
| return [0]; | |||
| } | |||
| module.exports = { | |||
| formatTime: formatTime, | |||
| uint8ArrayToString: uint8ArrayToString, | |||
| stringToUint8Array: stringToUint8Array, | |||
| ab2hex: ab2hex, | |||
| hexCharCodeToStr, | |||
| hexCharCodeToStr: buf2string, | |||
| decodeHexNibble, | |||
| decodeHexNibble: decodeHexByte, | |||
| hexStringToBytesWithPadding: hexStringToBytesWithPadding, | |||
| hexStringToBytes: hexStringToBytes | |||
| } | |||