emis-app/components/tpx-bluetooth/tpx-bluetooth.vue
2024-05-13 00:49:19 +08:00

370 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<tpx-layout v-if="showConnectPage">
<template v-slot:header>
<view class="blcok-white" style="margin-top: 0;">
<view >
<u-row custom-style="gap: 10rpx;">
<u-button @click="discoveryPrinter">
{{searchLoading?'搜索中...':'搜索周边设备'}}
</u-button>
<u-button @click="stopDiscoveryPrinter">停止搜索</u-button>
<slot name="button"></slot>
</u-row>
</view>
<view class="">
<view class="pt-10 pb-10">连接信息</view>
<u-steps activeColor="#969799" :current="curTab" direction="column" dot="true">
<u-steps-item v-for="(item, index) in conInfos">
<template v-slot:title>
<view @click="changeTab(index)" :class="`${index == curTab ? 'clr-prm' : 'clr-sub'} pl-20`">{{item.title}}</view>
</template>
<template v-slot:desc>
<view @click="changeTab(index)" class="mt-10 pl-20">{{item.desc}}</view>
</template>
</u-steps-item>
</u-steps>
</view>
</view>
</template>
<template v-slot:body>
<view class="blcok-white">
<view v-if="curTab == 0">
<view class="pb-10">蓝牙设备列表</view>
<template v-for="(item, index) in devices">
<view @click="handleSelectDeviceId(item)" v-if="item.name.length>0"
:key="index" :class="`list-item ${item.deviceId == deviceId?'list-item-active':''}`"
>
<u-row justify="between">
<view class="font-weight">{{item.name }}</view>
</u-row>
<view>
<text class="clr-sub">信号强度:</text> {{item.RSSI}}dBm ({{Math.max(100+item.RSSI,0)}}%)
</view>
<view>
<text class="clr-sub">deviceId:</text>{{item.deviceId}}
</view>
<view >
<text class="clr-sub">Service数量:</text> {{item.advertisServiceUUIDs.length || 0}}
</view>
</view>
</template>
</view>
<view v-if="curTab == 1">
<view class="pb-10">服务列表</view>
<template v-for="(item, index) in serverList" :key="index">
<view @click="handleSelectService(item)"
:class="`list-item ${item.uuid == serviceId?'list-item-active':''}`"
>
<view>
<text class="clr-sub">uuid:</text> {{item.uuid}}
</view>
</view>
</template>
</view>
<view v-if="curTab == 2">
<view class="pb-10">特征值列表</view>
<template v-for="(item, index) in characteristics" :key="index">
<view @click="handleSelectChara(item)"
:class="`list-item ${item.uuid == characteristicId?'list-item-active':''}`"
>
<view>
<text class="clr-sub">uuid:</text> {{item.uuid}}
</view>
<template v-if="item.properties">
<view>
<text class="clr-sub">write:</text> {{item.properties.write}}
</view>
<view>
<text class="clr-sub">notify:</text> {{item.properties.notify}}
</view>
<view>
<text class="clr-sub">indicate:</text> {{item.properties.indicate}}
</view>
</template>
</view>
</template>
</view>
<slot name="content"></slot>
</view>
</template>
</tpx-layout>
</template>
<script>
import { showLoading, showToast, hideLoading } from "@/common/untool"
import { getSessionBluetoothInfo, setSessionBluetoothInfo } from "@/session"
export default {
computed: {
conInfos() {
let ary = [
{ title: '蓝牙设备', desc: this.deviceId || '-' },
{ title: '服务', desc: this.serviceId || '-' },
{ title: '特征值', desc: this.characteristicId || '-' }
]
return ary
},
},
watch: {
deviceId(val){
updateBlueSessionInfo()
},
serviceId(val){
updateBlueSessionInfo()
},
characteristicId(val){
updateBlueSessionInfo()
}
},
props:{
},
data () {
let { deviceId, serviceId, characteristicId } = getSessionBluetoothInfo()
return {
showConnectPage: true,
canvasId: 'shareCanvas',
canvas_width: 240,
canvas_height: 240,
curTab: 0,
searchLoading: false,
devices: [],
deviceId,
serverList: [],
serviceId,
characteristics: [],
characteristicId,
}
},
async mounted () {
// 初始化蓝牙模块
await this.openBluetoothAdapter()
this.autoConnect()
},
unmounted() {
this.stopDiscoveryPrinter()
// this.closeBluetoothAdapter()
},
methods: {
updateBlueSessionInfo() {
let { deviceId, serviceId, characteristicId } = this
setSessionBluetoothInfo({ deviceId, serviceId, characteristicId })
},
// 自动连接历史蓝牙配置
async autoConnect() {
let conSta = await this.checkConnectStat()
// 自动连接
if(!conSta) {
this.discoveryPrinter()
}
return true
},
// 检查蓝牙 缓存连接状态 【获取不到characteristics说明连接已经过期】
async checkConnectStat(){
let bl = false
try{
await this.getBLEDeviceCharacteristics()
bl = true
}catch(e){
}
return bl
},
changeTab(type) {
this.curTab = type
},
openBluetoothAdapter () {
var _this = this
return new Promise((resolve, reject)=> {
uni?.openBluetoothAdapter?.({
complete (e) {
console.log(e);
if (!e.errCode) {
resolve()
console.log('初始化完成')
} else if (e.errCode == 10001) {
reject(e)
showToast('请打开手机蓝牙')
} else {
reject(e)
console.log('openBluetoothAdapter:error=========', e.errMsg)
showToast(e.errMsg)
}
}
})
})
},
// 开始搜寻附近的蓝牙外围设备
discoveryPrinter () {
var _this = this
_this.devices = []
_this.searchLoading = true
_this.curTab = 0
return new Promise((resolve, reject)=> {
uni?.startBluetoothDevicesDiscovery?.({
complete (e) {
console.log(e)
if (e.errMsg == "startBluetoothDevicesDiscovery:ok") {
resolve()
// ArrayBuffer转16进度字符串示例
uni.onBluetoothDeviceFound(devices => {
console.log("========devices=====", devices)
let curDvs = devices?.devices?.[0] || {}
// 过滤重复的
if(curDvs.deviceId && _this.devices.map(t=> t.deviceId).indexOf(curDvs.deviceId) == -1) {
_this.devices.push(curDvs)
}
})
} else {
reject()
}
}
})
})
},
// 停止搜寻附近的蓝牙外围设备
async stopDiscoveryPrinter () {
this.searchLoading = false
uni?.stopBluetoothDevicesDiscovery?.()
},
// 断开蓝牙
async closeBluetoothAdapter () {
this.searchLoading = false
uni?.closeBluetoothAdapter?.()
},
async handleSelectDeviceId (item) {
this.deviceId = item.deviceId
this.serviceId = ''
this.serverList = []
this.characteristics = []
this.characteristicId = ''
//连接蓝牙
await this.connect()
if(this.serverList?.length > 0) {
this.curTab = 1
this.stopDiscoveryPrinter()
} else {
showToast('该设备无服务,请检查配置或换一个设备连接')
}
},
handleSelectService (item) {
this.serviceId = item.uuid
this.characteristics = []
this.characteristicId = ''
// 获取蓝牙特征值
setTimeout(async () =>{
await this.getBLEDeviceCharacteristics()
if(this.characteristics?.length > 0) {
this.curTab = 2
} else {
showToast('该服务无特征值,请检查配置或换一个服务')
}
}, 500);
},
handleSelectChara (item) {
this.characteristicId = item.uuid
},
connect () {
var _this = this
console.log("连接蓝牙=====deviceId", _this.deviceId)
showLoading('正在连接蓝牙')
return new Promise((resolve)=> {
uni.createBLEConnection({
deviceId: _this.deviceId,
complete (e) {
hideLoading()
console.log("蓝牙连接回调=====", e)
if (e.errMsg == "createBLEConnection:ok") {
showToast(`蓝牙连接成功`)
//获取蓝牙设备所有服务(service)。
setTimeout(async()=> {
await _this.getBLEDeviceServices()
resolve()
}, 2000)
} else {
resolve()
showToast(`连接设备失败:${e.errMsg}`)
}
}
})
})
},
getBLEDeviceServices(){
var _this = this
return new Promise((resolve)=> {
showLoading('正在搜索服务')
uni.getBLEDeviceServices({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: _this.deviceId,
success:(res)=>{
hideLoading()
resolve()
console.log('device services:', res)
_this.serverList = res.services
// _this.serviceId = res.services[0].uuid;
console.log('serverId:', _this.serviceId)
},
fail:(e) =>{
hideLoading()
resolve()
this.serverList = []
showToast(`设备服务获取失败:${e.errMsg}`)
console.log("getBLEDeviceServices - fail==============", e)
}
})
})
},
getBLEDeviceCharacteristics () {
var _this = this
return new Promise((resolve, reject)=> {
showLoading('正在搜索特征值')
uni.getBLEDeviceCharacteristics({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: _this.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId:_this.serviceId,
success:(res)=>{
hideLoading()
console.log('getBLEDeviceCharacteristics', res)
_this.characteristics = res.characteristics
resolve()
// _this.characteristicId = res.characteristics[0].uuid
},
fail:(res)=>{
hideLoading()
reject()
console.log(res)
}
})
})
},
getInstance() {
return this
}
}
}
</script>
<style scoped lang="scss">
.list-item {
font-size: 26rpx;
margin-bottom: 20rpx;
border-radius: 10rpx;
border: 2rpx solid #ddd;
padding: 20rpx;
background-color: #F6F6F6;
}
.list-item-active{
border-color: #B12D29;
&:last-child{
margin-bottom: 0;
}
}
</style>