uni-app 蓝牙扫码适配

1.前言

  • 蓝牙设备扫码的效率要高于手机摄像头
  • App需要进行对蓝牙扫码枪进行适配才能正常使用蓝牙设备枪,并兼容之前的摄像头扫码
  • 适配的关键在于:扫码枪进行扫码时,需要对其进行事件监听,并拿到条码的值

2.注意事项

  • 蓝牙模块是可选项,默认应该是关闭状态,需要时再手动开启
  • 蓝牙模块的运行状态需要存储至全局变量中(vuex),以便所有的页面都能获知
  • 蓝牙扫描附近的设备时会增加耗电,应在适当时机予以关闭
  • 蓝牙设备连接后,需要保持屏幕常亮,蓝牙断开后,关闭屏幕常亮

3.逻辑梳理

  • 初始化蓝牙相关的全局数据(vuex)
  • 在设置页面中展示蓝牙连接的状态并监听点击
  • 获取开启此功能所依赖的权限,并展示到页面中(仅小程序)
  • 获取已保存过的设备列表,用于自动连接已匹配过的设备
  • 尝试执行初始化,如果已经执行过,则不再执行
  • 初始化成功后,进行事件监听(监听蓝牙适配器状态变化事件,监听低功耗蓝牙连接状态的改变事件)
  • 初始化成功后,开启搜寻附近的蓝牙外围设备(一段时间后进行关闭,节省电量)
  • 则监听设备发现事件,一旦监听到新设备,则将其保存并渲染到页面(进行去重处理),对比之前已经保存过的设备列表,符合条件自动进行连接
  • 将扫码到的附近的蓝牙设备渲染到页面中,监听点击事件进行手动连接
  • 设备连接成功后,更新获取已保存过的设备列表,停止搜索设备,并根据deviceId获取设备服务(延时获取)
  • 选中目标设备服务,获取其特征码,并选中目标特征码,开启notify 功能
  • 监听特征值变化事件(扫码会触发此事件)
  • 扫码数据触发时,需要对数据进行转换,特征值的数据格式是arrayBuffer,需要将其转换成字符串
  • 处理二维码对应的字符串

4.关于设备服务和特征码

  • !!!获取设备服务时,需要延时获取,不然App端获取为空
  • 监听扫码事件的关键在于获取正确的服务id和特征码id
  • 如果不知道需要的是哪个服务和特征码,可以咨询设备厂家的技术客服
  • 服务id和特征码id我是自己盲测出来的

5.数据格式转换 arrayBufferUTF8ToStr

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 < len) {
                    c = array[i++];
                    switch (c >> 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[i++];
                            out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
                            break;
                        case 14:
                            // 1110 xxxx  10xx xxxx  10xx xxxx
                            char2 = array[i++];
                            char3 = array[i++];
                            out += String.fromCharCode(((c & 0x0F) << 12) |
                                ((char2 & 0x3F) << 6) |
                                ((char3 & 0x3F) << 0));
                            break;
                    }
                }

                return out;
            },

6.处理扫码结果

  • 每个页面处理扫码结果的逻辑是不一样的,为了减少对页面代码的改动,我的处理方式是这样的
  • 每个页面新建一个处理扫码结果的方法
  • 进行蓝牙扫描时,获取到当前页面,再拿到这个处理方法,有则执行
//转码
var _code = this.arrayBufferUTF8ToStr(res.value)
//去除左右空格 
var code = _code.trim()
//拿到当前页面 (页面栈最后一个元素)
var now_page_index = getCurrentPages().length - 1
var now_page = getCurrentPages()[now_page_index]
//执行页面扫码回调
if(typeof now_page.$vm.handleScanResult == 'function'){
      //执行回调
      console.log('执行当前页面 handleScanResult 方法')
      now_page.$vm.handleScanResult(code)
}

7.蓝牙相关数据初始化

  • 在vuex中初始化相关数据
const state = {
    connectedBluetoothDevices:[],//已经建立连接状态的设备 长度大于0表示设备连接成功
    bluetoothAdapterAvailable: false,//蓝牙适配器是否可用
    bluetoothAdapterDiscovering:false,//蓝牙适配器是否处于搜索状态
    
    bluetoothOpenNotify:false,//蓝牙扫码监听是否已经开启
    bluetoothCloseScanTimer:null,//关闭蓝牙扫描的延时定时器
}
  • 更新相关数据的方法
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
    }
}

8.设备页面

<!-- #ifndef H5 -->
<view >
     <uni-list>
           <uni-list-item title="蓝牙连接" clickable :note="bluetoothAdapterStatus" @click="gotoBluetoothPage"></uni-list-item>
     </uni-list>
</view>
<!-- #endif -->
  • 读取vuex中的数据,输入当前的蓝牙运行状态
computed:{
    //已经建立连接状态的设备
    connectedBluetoothDevices(){
         return this.$store.state.connectedBluetoothDevices
    },
    //蓝牙适配器是否可用
    bluetoothAdapterAvailable(){
         return this.$store.state.bluetoothAdapterAvailable
    },
    //蓝牙运行状态
    bluetoothAdapterStatus(){
          //如果不可用 显示 
          if(!this.bluetoothAdapterAvailable){
               return '未开启'
         }else if(this.connectedBluetoothDevices.length > 0){
               //如果已经开启 且已经连接了设备 则展示其名称
               return  this.connectedBluetoothDevices[0].name
         }else{
              return '未连接设备'
         }
    },
}

9.蓝牙页面

  • 展示开关
  • 渲染设备列表
  • 事件初始化
<template>
    <view class="page-wrap">
        <!-- 蓝牙适配器状态 -->
        <view class="flex-box" style="padding:0 8px;margin-bottom: 10px;">
            <view style="font-size: 16px;">蓝牙适配器状态</view>
            <view>
                <u-switch :value="bluetoothAdapterAvailable" @change="bluetoothAdapterAvailableChange"></u-switch>
            </view>
        </view>
        
        <!--  #ifdef  MP-WEIXIN -->
        <!-- 权限状态 开始 -->
        <view>
            <view class="tip">确保下列权限都已开启</view>
            <view class="flex-box row-box">
                <text>蓝牙系统开关:</text>
                <view @click="handleClickEnabled">
                    <u-switch disabled :value="bluetoothEnabled"></u-switch>
                </view>
            </view>
            <u-line></u-line>
            
            <view class="flex-box row-box">
                <text>地理位置的系统开关:</text>
                <view @click="handleClickEnabled">
                    <u-switch disabled :value="locationEnabled"></u-switch>
                </view>
            </view>
            <u-line></u-line>
            <view class="flex-box row-box">
                <text>允许微信使用定位:</text>
                <view @click="handleClickEnabled">
                    <u-switch disabled :value="locationAuthorized"></u-switch>
                </view>
            </view>
            <u-line></u-line>
        </view>
        <!-- 权限状态 结束 -->
        <!--  #endif -->
        
        <!-- 已保存设备列表 -->
        <view>
            <!-- 标题栏 -->
            <view class="flex-box" style="margin-top:10px;padding:5px 0px;">
                <view class="tip">已保存设备列表</view>
                <view style="padding:0 8px 0 4px;" @click="clearSaveBluetoothDevice">
                    <u-icon name="trash-fill" size="20" color="#999"></u-icon>
                </view>
            </view>
            <!-- 列表 -->
            <view style="padding:2px 8px;">
                <!-- v-for="(device, index) in save_list" :key="index" -->
                <view  class="bluetooth-box" :class="{'is-connected':deviceIsConnected(save.deviceId)}"
                v-for="(save, index) in saveBluetoothDevice" :key="index" @click="clickSaveDevice(save)">
                    <view>
                        <view style="font-size: 18px;">{{save.name}}</view>
                        <view style="font-size: 14px;">
                            <text>{{deviceIsConnected(save.deviceId)? '已连接':'已保存'}}</text>
                            <text style="margin-left:10px;">{{getDeviceServicesStatus}}</text>
                        </view>
                    </view>
                    <view>
                        <u-icon name="arrow-right" size="22" :color="deviceIsConnected(save.deviceId)? '#fff':'#999'"></u-icon>
                    </view>
                </view>
                
                <view v-if="saveBluetoothDevice.length==0" style="text-align: center;padding:5px;color:#999;">空</view>
            </view>
        </view>


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

<script>
    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)=>{
                        // #ifdef  MP-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(){
                // #ifndef  H5
                
                //先获取蓝牙适配器状态 防止重复初始化
                console.log('获取本机蓝牙适配器状态')
                uni.getBluetoothAdapterState({
                    //在这里处理成功
                    success:(res)=>{
                        console.log('success',res)
                        //搜寻附近的蓝牙外围设备 参数10表示扫描10秒
                        this.scanBluetoothDevice(10)
                    },
                    //在这里处理失败
                    fail:(res)=>{
                        console.log('fail',res)
                        
                        //res.errCode == 10000代表未进行过初始化 下一步进行初始化操作
                        if(res.errCode == 10000){
                            //如果是微信小程序 需要进行权限验证
                            // #ifdef  MP-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)=>{
                                    console.log('success',res)
                                    
                                    //监听蓝牙适配器状态变化事件
                                    this.onBluetoothAdapterStateChange()
                                    
                                    //监听低功耗蓝牙连接状态的改变事件
                                    this.onConnectionStateChange()
                                    
                                    //搜寻附近的蓝牙外围设备
                                    this.scanBluetoothDevice(10)
                                },
                                //在这里处理失败
                                fail: (res) => {
                                    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)=>{
                        //监听寻找到新设备的事件
                        uni.onBluetoothDeviceFound((devices) => {
                            //当前设备
                            var now_devices = devices.devices[0]
                            
                            //判断是否已经存在列表中
                            var isInDeviceFindList = this.bluetoothDeviceFindList.some(item=>{
                                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=>{
                                return item.deviceId == now_devices.deviceId
                            })
                            
                            //如果已存在列表中,且未进行过任何连接 且不是连接进行中 则自动连接
                            if(isInSaveBluetoothDevice && this.connectedBluetoothDevices.length==0 && !this.bluetoothConnecting){
                                //console.log('模拟点击 自动进行设备连接')
                                //模拟点击 进行设备连接
                                this.clickDeviceToConnect(now_devices)
                            }
                        })
                        
                        //延时定时器 自动关闭扫描
                        var timer = setTimeout(()=>{
                            //停止搜索设备
                            this.stopBluetoothDevicesDiscovery()
                        },scan_duration * 1000)
                        
                        //全局记录此延时定时器id
                        this.$store.commit('updateBluetoothCloseScanTimer',timer)
                    },
                    fail: (res) => {
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                    }
                })
            },
            //获取已连接的蓝牙设备列表 只有设备id和名称
            getConnectedBluetoothDevices() {
                //获取已经建立链接的设备
                uni.getConnectedBluetoothDevices({
                    services: [],
                    //调用成功在这里处理
                    success:(res)=>{
                        //保存设备列表
                        this.$store.commit('updateConnectedBluetoothDevices',res.devices)
                    },
                    //调用失败在这里处理
                    fail: (res)=> {
                        //弹出错误提示
                        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)=>{
                        //隐藏加载状态
                        uni.hideLoading()
                        //更新连接状态
                        this.bluetoothConnecting = false
                        
                        //记录已连接的设备 如果之前为连接的话
                        var index = this.saveBluetoothDevice.findIndex(item=>{
                            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(()=>{
                            //获取设备服务
                            this.getDeviceServer(device)
                        },1500)
                    },
                    fail: (res) => {
                        //隐藏加载状态
                        uni.hideLoading()
                        //更新连接状态
                        this.bluetoothConnecting = false
                        
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                    }
                })
            },
            //获取设备服务
            getDeviceServer(device) {
                uni.getBLEDeviceServices({
                    deviceId: device.deviceId,
                    success:(res)=>{
                        console.log('获取设备服务成功',res)
                        
                        //查找目标服务 00001804-0000-1000-8000-00805F9B34FB 目标服务
                        var scan_server = res.services.find(item=>{
                            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) => {
                        //弹出错误提示
                        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)=>{
                        console.log('特征值列表',res.characteristics)
                        //特征值列表
                        var characteristics = res.characteristics
                                    
                        //启用低功耗蓝牙设备特征值变化时的 notify 功能
                        this.openNotify(device.deviceId, serviceId, characteristics[0].uuid)
                        
                    },
                    fail: (res) => {
                        //弹出错误提示
                        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)=>{
                        this.$store.commit('updateBluetoothOpenNotify',true)
                        console.log('notify 功能开启成功 开启监听到特征值变化')
                        //监听特征值变化
                        uni.onBLECharacteristicValueChange((res) => {
                            //扫描到的二维码为
                            var code = this.arrayBufferUTF8ToStr(res.value)
                            
                            console.log("监听到特征值变化 ",code)
                            
                            //处理扫码结果
                            this.handleBluetoothCode(code)
                        })
                    },
                    fail: (res) => {
                        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)=>{
                        //隐藏操作状态
                        uni.hideLoading()
                    },
                    fail: (res)=> {
                        //隐藏操作状态
                        uni.hideLoading()
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                    }
                })
            },
            //关闭蓝牙适配器 关闭之后 所有监听失效
            closeBluetoothAdapter(){
                uni.closeBluetoothAdapter({
                    success:(res)=>{
                        //重置vuex中的数据
                        this.$store.commit('resetBluetoothData')
                        //清空已扫描到的设备列表
                        this.deviceFindList = []
                        //清空定时器
                        clearTimeout(this.bluetoothCloseScanTimer)
                        console.log('关闭屏幕常亮')
                        uni.setKeepScreenOn({
                        	keepScreenOn: false
                        })  
                    },
                    fail: (res)=> {
                        //弹出错误提示
                        uni.showToast({
                            icon: "none",
                            title: res.errMsg,
                            duration: 2500
                        })
                    }
                })
            },
            
            
            //监听蓝牙适配器状态变化事件
            onBluetoothAdapterStateChange(){
                uni.onBluetoothAdapterStateChange((res)=>{
                    //更新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)=> {
                    console.log('蓝牙连接状态发生改变',res)
                    if(!res.connected){
                        uni.showModal({
                            title:"提示",
                            content:"蓝牙已断开",
                            showCancel:false,
                            success:(res)=>{
                                
                            }
                        })
                        
                        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 > 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)=>{
                            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=>{
                    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 < len) {
                    c = array[i++];
                    switch (c >> 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[i++];
                            out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
                            break;
                        case 14:
                            // 1110 xxxx  10xx xxxx  10xx xxxx
                            char2 = array[i++];
                            char3 = array[i++];
                            out += String.fromCharCode(((c & 0x0F) << 12) |
                                ((char2 & 0x3F) << 6) |
                                ((char3 & 0x3F) << 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()[now_page_index]
                //执行页面扫码回调
                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 && this.connectedBluetoothDevices.length > 0){
                    return '服务运行中'
                }else{
                    return ''
                }
            },
        }
    }
</script>

<style scoped lang="scss">
    .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;
            &: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%;
    }
</style>

posted @ 2022-05-31 17:01  ---空白---  阅读(876)  评论(0编辑  收藏  举报