|
@@ -0,0 +1,463 @@
|
|
|
+<template>
|
|
|
+ <view>
|
|
|
+ <cu-custom
|
|
|
+ ref="custom"
|
|
|
+ id="custom"
|
|
|
+ bgImage="https://yostest175549.obs.cn-east-2.myhuaweicloud.com:443/202306151686796745663B52544232.png"
|
|
|
+ :isBack="true"
|
|
|
+ >
|
|
|
+ <block slot="backText">返回</block>
|
|
|
+ <block slot="content"> 搜索蓝牙设备 </block>
|
|
|
+ </cu-custom>
|
|
|
+ <view class="head">
|
|
|
+ <view class="title">蓝牙设备列表</view>
|
|
|
+ <u-loading-icon v-if="beSearching" size="21" />
|
|
|
+ <view v-else class="anew" @click="startBluetooth"> 继续搜索 </view>
|
|
|
+ </view>
|
|
|
+ <My_listbox ref="List" :empty="empty" :pullDown="false">
|
|
|
+ <view class="device" v-for="item in devices" :key="item.deviceId">
|
|
|
+ <view class="left">
|
|
|
+ <text class="iconfont icon-shuifa" />
|
|
|
+ </view>
|
|
|
+ <view class="content">
|
|
|
+ <view class="name u-line-2">{{ item.name || item.deviceId }}</view>
|
|
|
+ <view class="rssi">
|
|
|
+ <view class="text">{{ getRSSIText(item.RSSI) }} </view>
|
|
|
+ <view class="signal">
|
|
|
+ <view
|
|
|
+ class="cell"
|
|
|
+ v-for="h in [4, 6, 8, 10]"
|
|
|
+ :key="h"
|
|
|
+ :style="{
|
|
|
+ height: h + 'px',
|
|
|
+ background:
|
|
|
+ h <= getRSSIStyle(item.RSSI).h
|
|
|
+ ? getRSSIStyle(item.RSSI).BC
|
|
|
+ : '#BBBBBB',
|
|
|
+ }"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view style="flex: 1" />
|
|
|
+
|
|
|
+ <view
|
|
|
+ class="but"
|
|
|
+ hover-class="navigator-hover"
|
|
|
+ @click="connected ? '' : createBLEConnection(item)"
|
|
|
+ >
|
|
|
+ <u-loading-icon
|
|
|
+ v-if="connected == item.deviceId"
|
|
|
+ color="#FFF"
|
|
|
+ inactive-color="#999"
|
|
|
+ mode="circle"
|
|
|
+ size="18"
|
|
|
+ />
|
|
|
+ <text v-else>连接</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </My_listbox>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ components: {},
|
|
|
+ name: "Bluetooth",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ beSearching: false, //搜索状态
|
|
|
+ connected: "", //连接状态
|
|
|
+ device: {}, //连接的蓝牙设备
|
|
|
+ devices: [], //蓝牙设备表
|
|
|
+ serviceid: 0,
|
|
|
+ services: [], //蓝牙设备服务列表
|
|
|
+ characteristics: [], //特征列表
|
|
|
+ characteristic: {
|
|
|
+ uuid: null,
|
|
|
+ }, //选中的特征
|
|
|
+
|
|
|
+ empty: true,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ onLoad(options) {
|
|
|
+ this.startBluetooth();
|
|
|
+ this.$refs.List.setHeight();
|
|
|
+ },
|
|
|
+ onUnload() {
|
|
|
+ this.stopBluetooth();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /* 开始搜索 */
|
|
|
+ startBluetooth() {
|
|
|
+ let that = this;
|
|
|
+ this.connected = "";
|
|
|
+ uni.onBluetoothDeviceFound(function ({ devices }) {
|
|
|
+ that.devices = devices;
|
|
|
+ that.empty = devices.length == 0;
|
|
|
+ });
|
|
|
+ uni.startBluetoothDevicesDiscovery({
|
|
|
+ success(res) {
|
|
|
+ that.beSearching = true;
|
|
|
+ that.$refs.List.setHeight();
|
|
|
+ },
|
|
|
+ fail: (fail) => {
|
|
|
+ that.beSearching = false;
|
|
|
+ that.handleFail(fail);
|
|
|
+ if (fail.errno == 10000) uni.openBluetoothAdapter();
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 停止搜索 */
|
|
|
+ stopBluetooth() {
|
|
|
+ let that = this;
|
|
|
+ uni.stopBluetoothDevicesDiscovery({
|
|
|
+ success: (success) => {
|
|
|
+ that.beSearching = false;
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 开始连接 */
|
|
|
+ createBLEConnection(item) {
|
|
|
+ this.stopBluetooth();
|
|
|
+ let that = this,
|
|
|
+ device = {
|
|
|
+ device: item,
|
|
|
+ };
|
|
|
+ that.connected = item.deviceId;
|
|
|
+ uni.createBLEConnection({
|
|
|
+ deviceId: item.deviceId,
|
|
|
+ success(res) {
|
|
|
+ if (res.errCode == 0) {
|
|
|
+ uni.getBLEDeviceServices({
|
|
|
+ deviceId: item.deviceId,
|
|
|
+ success(res) {
|
|
|
+ console.log("获取蓝牙服务列表", res);
|
|
|
+ if (res.services.length == 0) {
|
|
|
+ uni.showModal({
|
|
|
+ content: "未获取到蓝牙设备服务列表",
|
|
|
+ showCancel: false,
|
|
|
+ confirmText: "确认",
|
|
|
+ });
|
|
|
+ that.closeBLEConnection();
|
|
|
+ } else {
|
|
|
+ device.service = res.services[0];
|
|
|
+
|
|
|
+ uni.getBLEDeviceCharacteristics({
|
|
|
+ deviceId: item.deviceId,
|
|
|
+ serviceId: device.service.uuid,
|
|
|
+ success(res) {
|
|
|
+ console.log("获取特征", res);
|
|
|
+ if (res.characteristics.length == 0) {
|
|
|
+ uni.showModal({
|
|
|
+ content: "未获取到蓝牙服务的特征信息",
|
|
|
+ showCancel: false,
|
|
|
+ confirmText: "确认",
|
|
|
+ });
|
|
|
+ that.closeBLEConnection();
|
|
|
+ } else {
|
|
|
+ device.characteristic = res.characteristics[0];
|
|
|
+ const { write, read } =
|
|
|
+ device.characteristic.properties;
|
|
|
+ return uni.showModal({
|
|
|
+ content: `${
|
|
|
+ item.name || item.deviceId
|
|
|
+ }连接成功\n是否进入设备操作页`,
|
|
|
+ confirmText: "确认",
|
|
|
+ cancelText: "重选",
|
|
|
+ success: ({ confirm }) => {
|
|
|
+ if (confirm) {
|
|
|
+ console.log("设备连接完成");
|
|
|
+ that.$Http.setBluetooth(device);
|
|
|
+ } else {
|
|
|
+ that.closeBLEConnection();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ });
|
|
|
+ if (write && read) {
|
|
|
+ uni.showModal({
|
|
|
+ content: `${
|
|
|
+ item.name || item.deviceId
|
|
|
+ }连接成功\n是否进入设备操作页`,
|
|
|
+ confirmText: "确认",
|
|
|
+ cancelText: "重选",
|
|
|
+ success: ({ confirm }) => {
|
|
|
+ if (confirm) {
|
|
|
+ console.log("设备连接完成");
|
|
|
+ that.$Http.setBluetooth(device);
|
|
|
+ } else {
|
|
|
+ that.closeBLEConnection();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ uni.showModal({
|
|
|
+ content: "设备未开启read或write权限,已断开连接",
|
|
|
+ showCancel: false,
|
|
|
+ confirmText: "确认",
|
|
|
+ });
|
|
|
+ that.closeBLEConnection();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail(err) {
|
|
|
+ console.log("获取特征失败", err);
|
|
|
+ that.handleFail(fail);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail(err) {
|
|
|
+ console.error("获取蓝牙服务失败", err);
|
|
|
+ that.handleFail(fail);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ console.log("设备连接失败", that.codes[res.errCode]);
|
|
|
+ that.handleFail(res);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (fail) => {
|
|
|
+ console.log("连接失败", fail);
|
|
|
+ that.handleFail(fail);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 发送 */
|
|
|
+ send() {
|
|
|
+ let msg = JSON.stringify({
|
|
|
+ password: "87sjy9",
|
|
|
+ msgid: "3c1dfed63972c4441b4f0f1a097f884a",
|
|
|
+ ts: 1695354434598,
|
|
|
+ }),
|
|
|
+ that = this;
|
|
|
+ const buffer = new ArrayBuffer(msg.length);
|
|
|
+ const dataView = new DataView(buffer);
|
|
|
+ for (var i = 0; i < msg.length; i++) {
|
|
|
+ dataView.setUint8(i, msg.charAt(i).charCodeAt());
|
|
|
+ }
|
|
|
+ uni.writeBLECharacteristicValue({
|
|
|
+ deviceId: that.device.deviceId,
|
|
|
+ serviceId: that.serviceid,
|
|
|
+ characteristicId: that.characteristic.uuid,
|
|
|
+ value: buffer,
|
|
|
+ success(res) {
|
|
|
+ console.log("发送信息", res);
|
|
|
+ },
|
|
|
+ fail(err) {
|
|
|
+ console.error("发送信息失败", err);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /* 开始监听 */
|
|
|
+ startNotice() {
|
|
|
+ let that = this;
|
|
|
+ uni.notifyBLECharacteristicValueChange({
|
|
|
+ deviceId: that.device.deviceId,
|
|
|
+ serviceId: that.serviceid,
|
|
|
+ characteristicId: that.characteristic.uuid,
|
|
|
+ state: true,
|
|
|
+ success(res) {
|
|
|
+ uni.onBLECharacteristicValueChange((res) => {
|
|
|
+ // 结果
|
|
|
+ console.log("结果", res);
|
|
|
+ // 结果里有个value值,该值为 ArrayBuffer 类型,所以在控制台无法用肉眼观察到,必须将该值转换为16进制
|
|
|
+ let resHex = that.ab2hex(res.value);
|
|
|
+ console.log("16进制", resHex);
|
|
|
+ // 最后将16进制转换为,就能看到对应的结果
|
|
|
+ let result = that.hexCharCodeToStr(resHex);
|
|
|
+ console.log("ascii码", result);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+ ab2hex(buffer) {
|
|
|
+ const hexArr = Array.prototype.map.call(
|
|
|
+ new Uint8Array(buffer),
|
|
|
+ function (bit) {
|
|
|
+ return ("00" + bit.toString(16)).slice(-2);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ return hexArr.join("");
|
|
|
+ },
|
|
|
+ hexCharCodeToStr(hexCharCodeStr) {
|
|
|
+ var trimedStr = hexCharCodeStr.trim();
|
|
|
+ var rawStr =
|
|
|
+ trimedStr.substr(0, 2).toLowerCase() === "0x"
|
|
|
+ ? trimedStr.substr(2)
|
|
|
+ : trimedStr;
|
|
|
+ var len = rawStr.length;
|
|
|
+ if (len % 2 !== 0) {
|
|
|
+ alert("存在非法字符!");
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ 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("");
|
|
|
+ },
|
|
|
+
|
|
|
+ handleFail(fail) {
|
|
|
+ const codes = {
|
|
|
+ 0: "ok",
|
|
|
+ "-1": "已连接 ",
|
|
|
+ 10000: "未初始化蓝牙适配器",
|
|
|
+ 10001: "当前蓝牙适配器不可用",
|
|
|
+ 10002: "没有找到指定设备",
|
|
|
+ 10003: "连接失败",
|
|
|
+ 10004: "没有找到指定服务",
|
|
|
+ 10005: "没有找到指定特征值",
|
|
|
+ 10006: "当前连接已断开",
|
|
|
+ 10007: "当前特征值不支持此操作",
|
|
|
+ 10008: "其余所有系统上报的异常",
|
|
|
+ 10009: "系统版本低于 4.3 不支持 BLE",
|
|
|
+ 10010: "已连接",
|
|
|
+ 10011: "配对设备需要配对码",
|
|
|
+ 10012: "连接超时",
|
|
|
+ 10013: "连接 deviceId 为空或者是格式不正确",
|
|
|
+ };
|
|
|
+ this.connected = false;
|
|
|
+ uni.showModal({
|
|
|
+ content: codes[fail.errCode] || fail.errMsg,
|
|
|
+ showCancel: false,
|
|
|
+ confirmText: "确认",
|
|
|
+ });
|
|
|
+ this.closeBLEConnection();
|
|
|
+ },
|
|
|
+ /* 关闭连接 */
|
|
|
+ closeBLEConnection() {
|
|
|
+ let that = this;
|
|
|
+ if (this.connected)
|
|
|
+ uni.closeBLEConnection({
|
|
|
+ deviceId: that.connected,
|
|
|
+ success(res) {},
|
|
|
+ });
|
|
|
+ this.connected = "";
|
|
|
+ },
|
|
|
+ getRSSIText: function (rssi) {
|
|
|
+ let text = "";
|
|
|
+ if (rssi >= -70) {
|
|
|
+ text = "可连接";
|
|
|
+ } else if (rssi < -90) {
|
|
|
+ text = "不可连接";
|
|
|
+ } else {
|
|
|
+ text = "信号差";
|
|
|
+ }
|
|
|
+ return text + `(${rssi})`;
|
|
|
+ },
|
|
|
+ getRSSIStyle: function (rssi) {
|
|
|
+ let obj = {
|
|
|
+ h: 10,
|
|
|
+ BC: "#5AB73F",
|
|
|
+ };
|
|
|
+ if (rssi < -60 && rssi >= -69) {
|
|
|
+ obj.h = 8;
|
|
|
+ } else if (rssi <= -70 && rssi >= -79) {
|
|
|
+ obj.h = 6;
|
|
|
+ obj.BC = "#F29C37";
|
|
|
+ } else if (rssi <= -80 && rssi >= -89) {
|
|
|
+ obj.h = 4;
|
|
|
+ obj.BC = "#EB4B5C";
|
|
|
+ } else if (rssi <= -90) {
|
|
|
+ obj.h = 0;
|
|
|
+ }
|
|
|
+ return obj;
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.head {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ width: 100vw;
|
|
|
+ padding: 10px;
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ .title {
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 15px;
|
|
|
+ color: #ffffff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .anew {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #ffffff;
|
|
|
+ opacity: 0.8;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.device {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 355px;
|
|
|
+ height: 78px;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 4px;
|
|
|
+ margin: 0 auto 10px;
|
|
|
+
|
|
|
+ .left {
|
|
|
+ width: 61px;
|
|
|
+ text-align: center;
|
|
|
+ color: #333333;
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ width: 168px;
|
|
|
+ .name {
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #333333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .rssi {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 17px;
|
|
|
+ margin-top: 4px;
|
|
|
+
|
|
|
+ .text {
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666666;
|
|
|
+ margin-right: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .signal {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-end;
|
|
|
+ height: 12px;
|
|
|
+
|
|
|
+ .cell {
|
|
|
+ width: 3px;
|
|
|
+ border-radius: 1px;
|
|
|
+ margin-right: 1px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .but {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 56px;
|
|
|
+ height: 24px;
|
|
|
+ background: #0b3f7e;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #ffffff;
|
|
|
+ margin-right: 20px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|