予伏羌人也 發表於 2022-5-31 17:01:00

uni-app 蓝牙扫码适配

<h3 id="1前言">1.前言</h3>
<ul>
<li>蓝牙设备扫码的效率要高于手机摄像头</li>
<li>App需要进行对蓝牙扫码枪进行适配才能正常使用蓝牙设备枪,并兼容之前的摄像头扫码</li>
<li>适配的关键在于:扫码枪进行扫码时,需要对其进行事件监听,并拿到条码的值</li>
</ul>
<h3 id="2注意事项">2.注意事项</h3>
<ul>
<li>蓝牙模块是可选项,默认应该是关闭状态,需要时再手动开启</li>
<li>蓝牙模块的运行状态需要存储至全局变量中(vuex),以便所有的页面都能获知</li>
<li>蓝牙扫描附近的设备时会增加耗电,应在适当时机予以关闭</li>
<li>蓝牙设备连接后,需要保持屏幕常亮,蓝牙断开后,关闭屏幕常亮</li>
</ul>
<h3 id="3逻辑梳理">3.逻辑梳理</h3>
<ul>
<li>初始化蓝牙相关的全局数据(vuex)</li>
<li>在设置页面中展示蓝牙连接的状态并监听点击</li>
<li>获取开启此功能所依赖的权限,并展示到页面中(仅小程序)</li>
<li>获取已保存过的设备列表,用于自动连接已匹配过的设备</li>
<li>尝试执行初始化,如果已经执行过,则不再执行</li>
<li>初始化成功后,进行事件监听(监听蓝牙适配器状态变化事件,监听低功耗蓝牙连接状态的改变事件)</li>
<li>初始化成功后,开启搜寻附近的蓝牙外围设备(一段时间后进行关闭,节省电量)</li>
<li>则监听设备发现事件,一旦监听到新设备,则将其保存并渲染到页面(进行去重处理),对比之前已经保存过的设备列表,符合条件自动进行连接</li>
<li>将扫码到的附近的蓝牙设备渲染到页面中,监听点击事件进行手动连接</li>
<li>设备连接成功后,更新获取已保存过的设备列表,停止搜索设备,并根据deviceId获取设备服务(延时获取)</li>
<li>选中目标设备服务,获取其特征码,并选中目标特征码,开启notify 功能</li>
<li>监听特征值变化事件(扫码会触发此事件)</li>
<li>扫码数据触发时,需要对数据进行转换,特征值的数据格式是arrayBuffer,需要将其转换成字符串</li>
<li>处理二维码对应的字符串</li>
</ul>
<h3 id="4关于设备服务和特征码">4.关于设备服务和特征码</h3>
<ul>
<li>!!!获取设备服务时,需要延时获取,不然App端获取为空</li>
<li>监听扫码事件的关键在于获取正确的服务id和特征码id</li>
<li>如果不知道需要的是哪个服务和特征码,可以咨询设备厂家的技术客服</li>
<li>服务id和特征码id我是自己盲测出来的</li>
</ul>
<h3 id="5数据格式转换-arraybufferutf8tostr">5.数据格式转换 arrayBufferUTF8ToStr</h3>
<pre><code>arrayBufferUTF8ToStr(array) {
                var out, i, len, c;
                var char2, char3;
                if (array instanceof ArrayBuffer) {
                  array = new Uint8Array(array);
                }

                out = "";
                len = array.length;
                i = 0;
                while (i &lt; len) {
                  c = array;
                  switch (c &gt;&gt; 4) {
                        case 0:
                        case 1:
                        case 2:
                        case 3:
                        case 4:
                        case 5:
                        case 6:
                        case 7:
                            // 0xxxxxxx
                            out += String.fromCharCode(c);
                            break;
                        case 12:
                        case 13:
                            // 110x xxxx   10xx xxxx
                            char2 = array;
                            out += String.fromCharCode(((c &amp; 0x1F) &lt;&lt; 6) | (char2 &amp; 0x3F));
                            break;
                        case 14:
                            // 1110 xxxx10xx xxxx10xx xxxx
                            char2 = array;
                            char3 = array;
                            out += String.fromCharCode(((c &amp; 0x0F) &lt;&lt; 12) |
                              ((char2 &amp; 0x3F) &lt;&lt; 6) |
                              ((char3 &amp; 0x3F) &lt;&lt; 0));
                            break;
                  }
                }

                return out;
            },
</code></pre>
<h3 id="6处理扫码结果">6.处理扫码结果</h3>
<ul>
<li>每个页面处理扫码结果的逻辑是不一样的,为了减少对页面代码的改动,我的处理方式是这样的</li>
<li>每个页面新建一个处理扫码结果的方法</li>
<li>进行蓝牙扫描时,获取到当前页面,再拿到这个处理方法,有则执行</li>
</ul>
<pre><code>//转码
var _code = this.arrayBufferUTF8ToStr(res.value)
//去除左右空格
var code = _code.trim()
//拿到当前页面 (页面栈最后一个元素)
var now_page_index = getCurrentPages().length - 1
var now_page = getCurrentPages()
//执行页面扫码回调
if(typeof now_page.$vm.handleScanResult == 'function'){
      //执行回调
      console.log('执行当前页面 handleScanResult 方法')
      now_page.$vm.handleScanResult(code)
}
</code></pre>
<h3 id="7蓝牙相关数据初始化">7.蓝牙相关数据初始化</h3>
<ul>
<li>在vuex中初始化相关数据</li>
</ul>
<pre><code>const state = {
    connectedBluetoothDevices:[],//已经建立连接状态的设备 长度大于0表示设备连接成功
    bluetoothAdapterAvailable: false,//蓝牙适配器是否可用
    bluetoothAdapterDiscovering:false,//蓝牙适配器是否处于搜索状态
   
    bluetoothOpenNotify:false,//蓝牙扫码监听是否已经开启
    bluetoothCloseScanTimer:null,//关闭蓝牙扫描的延时定时器
}
</code></pre>
<ul>
<li>更新相关数据的方法</li>
</ul>
<pre><code>const mutations = {
    //更新 已经建立连接状态的设备 的数据
    updateConnectedBluetoothDevices(state,val){
      state.connectedBluetoothDevices = val
    },
    //更新 蓝牙适配器可用状态
    updateBluetoothAdapterAvailable(state,val){
      state.bluetoothAdapterAvailable = val
    },
    //更新 蓝牙适配器是否处于搜索状态
    updateBluetoothAdapterDiscovering(state,val){
      state.bluetoothAdapterDiscovering = val
    },
    //更新 蓝牙扫码监听是否已经开启
    updateBluetoothOpenNotify(state,val){
      state.bluetoothOpenNotify = val
    },
    //更新 关闭蓝牙扫描的延时定时器
    updateBlueClosetoothScanTimer(state,val){
      state.bluetoothCloseScanTimer = val
    },
    //重置蓝牙数据
    resetBluetoothData(state){
      state.connectedBluetoothDevices = []
      state.bluetoothAdapterAvailable = false
      state.bluetoothAdapterDiscovering = false
      state.bluetoothOpenNotify = false
    }
}
</code></pre>
<h3 id="8设备页面">8.设备页面</h3>
<pre><code>&lt;!-- #ifndef H5 --&gt;
&lt;view &gt;
   &lt;uni-list&gt;
         &lt;uni-list-item title="蓝牙连接" clickable :note="bluetoothAdapterStatus" @click="gotoBluetoothPage"&gt;&lt;/uni-list-item&gt;
   &lt;/uni-list&gt;
&lt;/view&gt;
&lt;!-- #endif --&gt;
</code></pre>
<ul>
<li>读取vuex中的数据,输入当前的蓝牙运行状态</li>
</ul>
<pre><code>computed:{
    //已经建立连接状态的设备
    connectedBluetoothDevices(){
         return this.$store.state.connectedBluetoothDevices
    },
    //蓝牙适配器是否可用
    bluetoothAdapterAvailable(){
         return this.$store.state.bluetoothAdapterAvailable
    },
    //蓝牙运行状态
    bluetoothAdapterStatus(){
          //如果不可用 显示
          if(!this.bluetoothAdapterAvailable){
               return '未开启'
         }else if(this.connectedBluetoothDevices.length &gt; 0){
               //如果已经开启 且已经连接了设备 则展示其名称
               returnthis.connectedBluetoothDevices.name
         }else{
            return '未连接设备'
         }
    },
}
</code></pre>
<h3 id="9蓝牙页面">9.蓝牙页面</h3>
<ul>
<li>展示开关</li>
<li>渲染设备列表</li>
<li>事件初始化</li>
</ul>
<pre><code>&lt;template&gt;
    &lt;view class="page-wrap"&gt;
      &lt;!-- 蓝牙适配器状态 --&gt;
      &lt;view class="flex-box" style="padding:0 8px;margin-bottom: 10px;"&gt;
            &lt;view style="font-size: 16px;"&gt;蓝牙适配器状态&lt;/view&gt;
            &lt;view&gt;
                &lt;u-switch :value="bluetoothAdapterAvailable" @change="bluetoothAdapterAvailableChange"&gt;&lt;/u-switch&gt;
            &lt;/view&gt;
      &lt;/view&gt;
      
      &lt;!--#ifdefMP-WEIXIN --&gt;
      &lt;!-- 权限状态 开始 --&gt;
      &lt;view&gt;
            &lt;view class="tip"&gt;确保下列权限都已开启&lt;/view&gt;
            &lt;view class="flex-box row-box"&gt;
                &lt;text&gt;蓝牙系统开关:&lt;/text&gt;
                &lt;view @click="handleClickEnabled"&gt;
                  &lt;u-switch disabled :value="bluetoothEnabled"&gt;&lt;/u-switch&gt;
                &lt;/view&gt;
            &lt;/view&gt;
            &lt;u-line&gt;&lt;/u-line&gt;
            
            &lt;view class="flex-box row-box"&gt;
                &lt;text&gt;地理位置的系统开关:&lt;/text&gt;
                &lt;view @click="handleClickEnabled"&gt;
                  &lt;u-switch disabled :value="locationEnabled"&gt;&lt;/u-switch&gt;
                &lt;/view&gt;
            &lt;/view&gt;
            &lt;u-line&gt;&lt;/u-line&gt;
            &lt;view class="flex-box row-box"&gt;
                &lt;text&gt;允许微信使用定位:&lt;/text&gt;
                &lt;view @click="handleClickEnabled"&gt;
                  &lt;u-switch disabled :value="locationAuthorized"&gt;&lt;/u-switch&gt;
                &lt;/view&gt;
            &lt;/view&gt;
            &lt;u-line&gt;&lt;/u-line&gt;
      &lt;/view&gt;
      &lt;!-- 权限状态 结束 --&gt;
      &lt;!--#endif --&gt;
      
      &lt;!-- 已保存设备列表 --&gt;
      &lt;view&gt;
            &lt;!-- 标题栏 --&gt;
            &lt;view class="flex-box" style="margin-top:10px;padding:5px 0px;"&gt;
                &lt;view class="tip"&gt;已保存设备列表&lt;/view&gt;
                &lt;view style="padding:0 8px 0 4px;" @click="clearSaveBluetoothDevice"&gt;
                  &lt;u-icon name="trash-fill" size="20" color="#999"&gt;&lt;/u-icon&gt;
                &lt;/view&gt;
            &lt;/view&gt;
            &lt;!-- 列表 --&gt;
            &lt;view style="padding:2px 8px;"&gt;
                &lt;!-- v-for="(device, index) in save_list" :key="index" --&gt;
                &lt;viewclass="bluetooth-box" :class="{'is-connected':deviceIsConnected(save.deviceId)}"
                v-for="(save, index) in saveBluetoothDevice" :key="index" @click="clickSaveDevice(save)"&gt;
                  &lt;view&gt;
                        &lt;view style="font-size: 18px;"&gt;{{save.name}}&lt;/view&gt;
                        &lt;view style="font-size: 14px;"&gt;
                            &lt;text&gt;{{deviceIsConnected(save.deviceId)? '已连接':'已保存'}}&lt;/text&gt;
                            &lt;text style="margin-left:10px;"&gt;{{getDeviceServicesStatus}}&lt;/text&gt;
                        &lt;/view&gt;
                  &lt;/view&gt;
                  &lt;view&gt;
                        &lt;u-icon name="arrow-right" size="22" :color="deviceIsConnected(save.deviceId)? '#fff':'#999'"&gt;&lt;/u-icon&gt;
                  &lt;/view&gt;
                &lt;/view&gt;
               
                &lt;view v-if="saveBluetoothDevice.length==0" style="text-align: center;padding:5px;color:#999;"&gt;空&lt;/view&gt;
            &lt;/view&gt;
      &lt;/view&gt;


      &lt;!-- 可用设备列表 --&gt;
      &lt;view&gt;
            &lt;!-- 标题栏 --&gt;
            &lt;view class="flex-box" style="margin-top:10px;padding:5px 0px;"&gt;
                &lt;view class="tip"&gt;可用设备列表&lt;/view&gt;
                &lt;view style="padding:0 8px 0 4px;" @click="reloadDeviceFindList"&gt;
                  &lt;u-loading-icon v-if="bluetoothAdapterDiscovering" mode="circle" size="20"&gt;&lt;/u-loading-icon&gt;
                  &lt;u-icon v-else name="reload" size="20"&gt;&lt;/u-icon&gt;
                &lt;/view&gt;
            &lt;/view&gt;
            &lt;!-- 列表 --&gt;
            &lt;view&gt;
                &lt;view v-for="(device, index) in bluetoothDeviceFindList" :key="index"&gt;
                  &lt;view class="device-box" :class="{'is-connected2':deviceIsConnected(device.deviceId)}"
                  @click="clickDeviceToConnect(device)"&gt;
                        &lt;text&gt;{{device | showDeviceName}}&lt;/text&gt;
                        &lt;text v-if="deviceIsConnected(device.deviceId)"&gt;已连接&lt;/text&gt;
                  &lt;/view&gt;
                  &lt;u-line&gt;&lt;/u-line&gt;
                &lt;/view&gt;
            &lt;/view&gt;
      &lt;/view&gt;
      
    &lt;/view&gt;
&lt;/template&gt;

&lt;script&gt;
    export default {
      data() {
            return {
                bluetoothEnabled: false, //蓝牙系统开关
                locationEnabled: false, //地理位置的系统开关
                locationAuthorized: false, //允许微信使用定位的开关
               
                saveBluetoothDevice:[],//已保存过的蓝牙设备列表
                saveBluetoothDevice_key:"saveBluetoothDevice",//保存到本地存储中的key

                bluetoothDeviceFindList:[],//扫描到的附近的蓝牙设备
                bluetoothConnecting:false,//是否是连接进行中介于 未连接 与成功连接 之间的状态
                delayGetServerTimer:null,//延时获取服务的定时器
            }
      },
      onLoad() {
            //1.获取所需权限
            this.getNeedAuthority()
            //2.获取已保存的蓝牙设备列表
            this.getSaveBluetoothDevice()
            //3.初始化蓝牙适配器
            this.initBluetoothAdapter()
      },
      onShow(){
            
      },
      onUnload() {
            //停止搜索设备
            this.stopBluetoothDevicesDiscovery()
      },
      onHide(){
            
      },
      filters: {
            //展示蓝牙设备的名称 有名称则展示名称,没名称则展示展示其他
            showDeviceName(device) {
                if (device.name) {
                  return device.name
                } else {
                  return device.deviceId
                }
            }
      },
      methods: {
            //获取所需权限
            getNeedAuthority(){
                //获取系统权限
                uni.getSystemInfo({
                  success: (res)=&gt;{
                        // #ifdefMP-WEIXIN
                        this.bluetoothEnabled = res.bluetoothEnabled //蓝牙系统开关
                        this.locationEnabled = res.locationEnabled //地理位置的系统开关
                        this.locationAuthorized = res.locationAuthorized //允许微信使用定位的开关
                        // #endif
                  }
                })
            },
            //获取已保存的蓝牙设备列表
            getSaveBluetoothDevice(){
                this.saveBluetoothDevice = uni.getStorageSync(this.saveBluetoothDevice_key) || []
                console.log('本地记录 已保存过的蓝牙设备列表',this.saveBluetoothDevice)
            },
            //初始化蓝牙适配器H5不支持
            initBluetoothAdapter(){
                // #ifndefH5
               
                //先获取蓝牙适配器状态 防止重复初始化
                console.log('获取本机蓝牙适配器状态')
                uni.getBluetoothAdapterState({
                  //在这里处理成功
                  success:(res)=&gt;{
                        console.log('success',res)
                        //搜寻附近的蓝牙外围设备 参数10表示扫描10秒
                        this.scanBluetoothDevice(10)
                  },
                  //在这里处理失败
                  fail:(res)=&gt;{
                        console.log('fail',res)
                        
                        //res.errCode == 10000代表未进行过初始化 下一步进行初始化操作
                        if(res.errCode == 10000){
                            //如果是微信小程序 需要进行权限验证
                            // #ifdefMP-WEIXIN
                            // 任何一个未开启,都阻止
                            if (!this.bluetoothEnabled || !this.locationEnabled || !this.locationAuthorized){
                              console.log('请先开启相关权限')
                              uni.showToast({
                                    icon:"error",
                                    title:"请开启相关权限",
                                    duration: 2500
                              })
                              return
                            }
                            // #endif
                           
                            // 开始初始化蓝牙适配器
                            console.log('开始初始化蓝牙适配器')
                            uni.openBluetoothAdapter({
                              //在这里处理成功
                              success:(res)=&gt;{
                                    console.log('success',res)
                                    
                                    //监听蓝牙适配器状态变化事件
                                    this.onBluetoothAdapterStateChange()
                                    
                                    //监听低功耗蓝牙连接状态的改变事件
                                    this.onConnectionStateChange()
                                    
                                    //搜寻附近的蓝牙外围设备
                                    this.scanBluetoothDevice(10)
                              },
                              //在这里处理失败
                              fail: (res) =&gt; {
                                    console.log('fail',res)
                                    uni.showToast({
                                        icon: "none",
                                        title: res.errMsg,
                                        duration: 2500
                                    })
                              },
                              //为了兼容性考虑,请勿使用 complete 回调
                            })
                        }
                        
                  }
                })
                // #endif
            },
            //搜寻附近的蓝牙外围设备
            scanBluetoothDevice(scan_duration = 10) {
                //先关闭之前的已开启的搜索
                this.stopBluetoothDevicesDiscovery()
                console.log(`开始搜寻附近的蓝牙外围设备 ${scan_duration}秒`)
                //开始搜寻附近的蓝牙外围设备
                //此操作比较耗费系统资源,请在 适当时机 调用 uni.stopBluetoothDevicesDiscovery 方法停止搜索
                uni.startBluetoothDevicesDiscovery({
                  //services: ['FEE7'],
                  allowDuplicatesKey: true,//允许重复上报同一设备
                  //成功调用在这里处理
                  success:(res)=&gt;{
                        //监听寻找到新设备的事件
                        uni.onBluetoothDeviceFound((devices) =&gt; {
                            //当前设备
                            var now_devices = devices.devices
                           
                            //判断是否已经存在列表中
                            var isInDeviceFindList = this.bluetoothDeviceFindList.some(item=&gt;{
                              return item.deviceId == now_devices.deviceId
                            })
                           
                            //不存在附近设备的列表中
                            if(!isInDeviceFindList){
                              //挂载新属性
                              now_devices.advertisDataStr = this.ab2hex(now_devices.advertisData)
                              //添加到列表中
                              this.bluetoothDeviceFindList.push(now_devices)
                            }
                           
                            //判断此设备是否存在已保存列表中
                            var isInSaveBluetoothDevice = this.saveBluetoothDevice.some(item=&gt;{
                              return item.deviceId == now_devices.deviceId
                            })
                           
                            //如果已存在列表中,且未进行过任何连接 且不是连接进行中 则自动连接
                            if(isInSaveBluetoothDevice &amp;&amp; this.connectedBluetoothDevices.length==0 &amp;&amp; !this.bluetoothConnecting){
                              //console.log('模拟点击 自动进行设备连接')
                              //模拟点击 进行设备连接
                              this.clickDeviceToConnect(now_devices)
                            }
                        })
                        
                        //延时定时器 自动关闭扫描
                        var timer = setTimeout(()=&gt;{
                            //停止搜索设备
                            this.stopBluetoothDevicesDiscovery()
                        },scan_duration * 1000)
                        
                        //全局记录此延时定时器id
                        this.$store.commit('updateBluetoothCloseScanTimer',timer)
                  },
                  fail: (res) =&gt; {
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  }
                })
            },
            //获取已连接的蓝牙设备列表 只有设备id和名称
            getConnectedBluetoothDevices() {
                //获取已经建立链接的设备
                uni.getConnectedBluetoothDevices({
                  services: [],
                  //调用成功在这里处理
                  success:(res)=&gt;{
                        //保存设备列表
                        this.$store.commit('updateConnectedBluetoothDevices',res.devices)
                  },
                  //调用失败在这里处理
                  fail: (res)=&gt; {
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  }
                })
            },
            //连接蓝牙设备
            createBLEConnection(device){
                //显示加载中
                uni.showLoading({
                  title:"连接中"
                })
                //更新连接状态
                this.bluetoothConnecting = true
                //进行设备连接
                uni.createBLEConnection({
                  deviceId: device.deviceId,
                  timeout: 10 * 1000,
                  success:(res)=&gt;{
                        //隐藏加载状态
                        uni.hideLoading()
                        //更新连接状态
                        this.bluetoothConnecting = false
                        
                        //记录已连接的设备 如果之前为连接的话
                        var index = this.saveBluetoothDevice.findIndex(item=&gt;{
                            return item.deviceId == device.deviceId
                        })
                        //如果存在 则先删除
                        if(index != -1){
                            this.saveBluetoothDevice.splice(index,1)
                        }
                        //添加到已保存列表中
                        this.saveBluetoothDevice.unshift({
                            deviceId: device.deviceId,
                            name: device.name
                        })
                        //保存到本地
                        uni.setStorageSync(this.saveBluetoothDevice_key,this.saveBluetoothDevice)
                                       
                        //停止搜索设备
                        this.stopBluetoothDevicesDiscovery()   
                        
                        //延迟获取设备服务
                        this.delayGetServerTimer = setTimeout(()=&gt;{
                            //获取设备服务
                            this.getDeviceServer(device)
                        },1500)
                  },
                  fail: (res) =&gt; {
                        //隐藏加载状态
                        uni.hideLoading()
                        //更新连接状态
                        this.bluetoothConnecting = false
                        
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  }
                })
            },
            //获取设备服务
            getDeviceServer(device) {
                uni.getBLEDeviceServices({
                  deviceId: device.deviceId,
                  success:(res)=&gt;{
                        console.log('获取设备服务成功',res)
                        
                        //查找目标服务 00001804-0000-1000-8000-00805F9B34FB 目标服务
                        var scan_server = res.services.find(item=&gt;{
                            return item.uuid == "0000FEEA-0000-1000-8000-00805F9B34FB"
                        })
                        
                        //是由存在目标服务
                        if(scan_server){
                            //获取扫码服务服务的特征值
                            this.getDeviceCharacteristics(device, scan_server)
                        }else{
                            console.log('未获取到目标服务')
                            uni.showToast({
                              icon:"none",
                              title:"未获取到目标服务",
                              duration: 2500
                            })
                        }
                  },
                  fail: (res) =&gt; {
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  }
                })
            },
            //获取蓝牙设备某个服务中所有特征值
            getDeviceCharacteristics(device, services_item) {
                //当前蓝牙扫描是第三个服务
                var serviceId = services_item.uuid
                uni.getBLEDeviceCharacteristics({
                  // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
                  deviceId: device.deviceId,
                  // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
                  serviceId: serviceId,
                  success:(res)=&gt;{
                        console.log('特征值列表',res.characteristics)
                        //特征值列表
                        var characteristics = res.characteristics
                                    
                        //启用低功耗蓝牙设备特征值变化时的 notify 功能
                        this.openNotify(device.deviceId, serviceId, characteristics.uuid)
                        
                  },
                  fail: (res) =&gt; {
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  },
                })
            
            },
            //启用低功耗蓝牙设备特征值变化时的 notify 功能
            openNotify(deviceId, serviceId, characteristicId) {
                console.log('characteristicId',characteristicId)
                uni.notifyBLECharacteristicValueChange({
                  state: true, // 启用 notify 功能
                  // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
                  deviceId,
                  // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
                  serviceId,
                  // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
                  characteristicId,
                  success:(res)=&gt;{
                        this.$store.commit('updateBluetoothOpenNotify',true)
                        console.log('notify 功能开启成功 开启监听到特征值变化')
                        //监听特征值变化
                        uni.onBLECharacteristicValueChange((res) =&gt; {
                            //扫描到的二维码为
                            var code = this.arrayBufferUTF8ToStr(res.value)
                           
                            console.log("监听到特征值变化 ",code)
                           
                            //处理扫码结果
                            this.handleBluetoothCode(code)
                        })
                  },
                  fail: (res) =&gt; {
                        console.log('notify 功能开启失败',res)
                        this.$store.commit('updateBluetoothOpenNotify',false)
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  }
                })
            },
            //停止搜索设备
            stopBluetoothDevicesDiscovery() {
                console.log('停止搜索设备,并清除相关定时器')
                uni.stopBluetoothDevicesDiscovery()
                //清除延时定时器
                clearTimeout(this.bluetoothCloseScanTimer)
            },
            //断开与低功耗蓝牙设备的连接
            closeBLEConnection(deviceId){
                //显示操作状态
                uni.showLoading({
                  title:"操作中..."
                })
                uni.closeBLEConnection({
                  deviceId,
                  success:(res)=&gt;{
                        //隐藏操作状态
                        uni.hideLoading()
                  },
                  fail: (res)=&gt; {
                        //隐藏操作状态
                        uni.hideLoading()
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  }
                })
            },
            //关闭蓝牙适配器 关闭之后 所有监听失效
            closeBluetoothAdapter(){
                uni.closeBluetoothAdapter({
                  success:(res)=&gt;{
                        //重置vuex中的数据
                        this.$store.commit('resetBluetoothData')
                        //清空已扫描到的设备列表
                        this.deviceFindList = []
                        //清空定时器
                        clearTimeout(this.bluetoothCloseScanTimer)
                        console.log('关闭屏幕常亮')
                        uni.setKeepScreenOn({
                                keepScreenOn: false
                        })
                  },
                  fail: (res)=&gt; {
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                  }
                })
            },
            
            
            //监听蓝牙适配器状态变化事件
            onBluetoothAdapterStateChange(){
                uni.onBluetoothAdapterStateChange((res)=&gt;{
                  //更新vuex中的数据
                  this.$store.commit('updateBluetoothAdapterAvailable',res.available)
                  //bluetoothAdapterDiscovering
                  this.$store.commit('updateBluetoothAdapterDiscovering',res.discovering)
                  if(!res.available){
                        //清除定时器
                        clearTimeout(this.delayGetServerTimer)
                        //清空周围的设备列表
                        this.bluetoothDeviceFindList = []
                  }
                })
            },
            //监听低功耗蓝牙连接状态的改变事件
            onConnectionStateChange(){
                uni.onBLEConnectionStateChange((res)=&gt; {
                  console.log('蓝牙连接状态发生改变',res)
                  if(!res.connected){
                        uni.showModal({
                            title:"提示",
                            content:"蓝牙已断开",
                            showCancel:false,
                            success:(res)=&gt;{
                              
                            }
                        })
                        
                        console.log('关闭屏幕常亮')
                        uni.setKeepScreenOn({
                                keepScreenOn: false
                        })
                  }else{
                        uni.showToast({
                            icon:"none",
                            title:"蓝牙已连接",
                            duration: 2500
                        })
                        
                        console.log('保持屏幕常亮')
                        uni.setKeepScreenOn({
                                keepScreenOn: true
                        })
                  }
                  //获取已连接的蓝牙设备列表
                  this.getConnectedBluetoothDevices()
                })
            },
            
            
            
            //点击清空已保存的蓝牙设备列表
            clearSaveBluetoothDevice(){
                //清空已保存的蓝牙设备列表 - 页面
                this.saveBluetoothDevice = []
                //清空已保存的蓝牙设备列表 - 本地存储
                uni.setStorageSync(this.saveBluetoothDevice_key,this.saveBluetoothDevice)
            },
            //点击重载扫描到的设备列表
            reloadDeviceFindList(){
                //清空之前扫描到的设备
                this.deviceFindList = []
                //判断本机蓝牙适配器状态。
                if(this.bluetoothAdapterAvailable){
                  //搜寻附近的蓝牙外围设备
                  this.scanBluetoothDevice(10)
                }else{
                  console.log('请先开启蓝牙适配器')
                  uni.showToast({
                        icon: "none",
                        title: "请先开启蓝牙适配器",
                        duration: 2500
                  })
                }
            },
            //点击蓝牙适配器开关切换
            bluetoothAdapterAvailableChange(status){
                if(!status){
                  //关闭蓝牙适配器
                  this.closeBluetoothAdapter()
                }else{
                  //3.初始化蓝牙适配器
                  this.initBluetoothAdapter()
                }
            },
            //点击选中蓝牙设备进行连接
            clickDeviceToConnect(device) {
                console.log('点击选中的设备 ' + device.deviceId)
               
                //一次只能连一个设备
                if(this.connectedBluetoothDevices.length &gt; 0){
                  uni.showToast({
                        icon: "none",
                        title: "只能连一个设备",
                        duration: 2500
                  })
                  return
                }
                //只有这种设备才能连
                var target_advertisServiceUUIDs = "0000FEE7-0000-1000-8000-00805F9B34FB"
                //如果不符合条件 则禁止连接
                if(!device.advertisServiceUUIDs.includes(target_advertisServiceUUIDs)){
                  uni.showToast({
                        icon: "none",
                        title: "不支持该设备",
                        duration: 2500
                  })
                  return
                }
               
                //连接设备
                this.createBLEConnection(device)
            },
            //点击已保存过的设备
            clickSaveDevice(device){
                //判断此设备是否已经连接过
                var isConnected = this.deviceIsConnected(device.deviceId)
                //如果已经连接 则断开
                if(isConnected){
                  uni.showModal({
                        title:"提示",
                        content:"是否断开蓝牙连接",
                        success: (res)=&gt;{
                            if (res.confirm) {
                              console.log('用户点击确定')
                              
                            } else if (res.cancel) {
                              console.log('用户点击取消')
                            }
                        }
                  })
                }else{
                  //尝试连接蓝牙设备
                  this.createBLEConnection(device)
                }
            },
            //点击权限开关
            handleClickEnabled(){
                uni.showToast({
                  icon:"none",
                  title:"请到系统设置界面进行操作",
                  duration: 2500
                })
            },
            
            //判断设备id是否已经在连接列表中
            deviceIsConnected(deviceId){
                return this.connectedBluetoothDevices.some(item=&gt;{
                  return item.deviceId == deviceId
                })
            },
            ab2hex(buffer) {
                const hexArr = Array.prototype.map.call(
                  new Uint8Array(buffer),
                  function(bit) {
                        return ('00' + bit.toString(16)).slice(-2)
                  }
                )
                return hexArr.join('')
            },
            arrayBufferUTF8ToStr(array) {
                var out, i, len, c;
                var char2, char3;
                if (array instanceof ArrayBuffer) {
                  array = new Uint8Array(array);
                }

                out = "";
                len = array.length;
                i = 0;
                while (i &lt; len) {
                  c = array;
                  switch (c &gt;&gt; 4) {
                        case 0:
                        case 1:
                        case 2:
                        case 3:
                        case 4:
                        case 5:
                        case 6:
                        case 7:
                            // 0xxxxxxx
                            out += String.fromCharCode(c);
                            break;
                        case 12:
                        case 13:
                            // 110x xxxx   10xx xxxx
                            char2 = array;
                            out += String.fromCharCode(((c &amp; 0x1F) &lt;&lt; 6) | (char2 &amp; 0x3F));
                            break;
                        case 14:
                            // 1110 xxxx10xx xxxx10xx xxxx
                            char2 = array;
                            char3 = array;
                            out += String.fromCharCode(((c &amp; 0x0F) &lt;&lt; 12) |
                              ((char2 &amp; 0x3F) &lt;&lt; 6) |
                              ((char3 &amp; 0x3F) &lt;&lt; 0));
                            break;
                  }
                }

                return out;
            },
            
            //处理蓝牙适配器的扫码结果
            handleBluetoothCode(_code = ""){
                //去除左右空格
                var code = _code.trim()
                //通用二维码转码
                var code_obj = this.$utils.transformScanResult(code)
               
                //拿到当前页面 (页面栈最后一个元素)
                var now_page_index = getCurrentPages().length - 1
                var now_page = getCurrentPages()
                //执行页面扫码回调
                if(typeof now_page.$vm.handleScanResult == 'function'){
                  //执行回调
                  console.log('执行当前页面 handleScanResult 方法')
                  now_page.$vm.handleScanResult(code_obj)
                }
            },
      },
      computed:{
            //已经建立连接状态的设备
            connectedBluetoothDevices(){
                return this.$store.state.connectedBluetoothDevices
            },
            //蓝牙适配器是否可用
            bluetoothAdapterAvailable(){
                return this.$store.state.bluetoothAdapterAvailable
            },
            //蓝牙适配器是否处于搜索状态
            bluetoothAdapterDiscovering(){
                return this.$store.state.bluetoothAdapterDiscovering
            },
            //蓝牙是否开启了 Notify
            bluetoothOpenNotify(){
                return this.$store.state.bluetoothOpenNotify
            },
            //延时关闭蓝牙扫描的定时器
            bluetoothCloseScanTimer(){
                return this.$store.state.bluetoothCloseScanTimer
            },
            //判断设备的服务状态
            getDeviceServicesStatus(deviceId){
                if(this.bluetoothOpenNotify &amp;&amp; this.connectedBluetoothDevices.length &gt; 0){
                  return '服务运行中'
                }else{
                  return ''
                }
            },
      }
    }
&lt;/script&gt;

&lt;style scoped lang="scss"&gt;
    .page-wrap{
      padding:10px 0;
      font-size: 14px;
      .tip {
            padding: 3px 8px;
            color: $uni-color-primary;
            font-size: 16px;
            
      }
      .flex-box {
            display: flex;
            align-items: center;
            justify-content: space-between;
      }
      .row-box{
            padding:3px 8px;
      }
      .bluetooth-box{
            background-color: $uni-bg-color-grey;
            padding:15px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 10px;
            &amp;:last-child{
                margin-bottom: 0;
            }
      }
      .is-connected{
            background-color: $uni-color-primary;
            color: #fff;
      }
      
      .device-box{
            padding:8px 12px;
            display: flex;
            align-items: center;
            justify-content: space-between;
      }
      .is-connected2{
            color: $uni-color-primary;
      }
    }
    .btn-box {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .btn-box .btn-item {
      width: 40%;
    }
&lt;/style&gt;

</code></pre><br><br>
来源:https://www.cnblogs.com/OrochiZ-/p/16331186.html
頁: [1]
查看完整版本: uni-app 蓝牙扫码适配