350 lines
11 KiB
JavaScript
350 lines
11 KiB
JavaScript
|
import Taro from "@tarojs/taro";
|
|||
|
import { handshake, asyncDate } from "./sendOrder";
|
|||
|
import useStore from "@/store/index";
|
|||
|
import { aes128Decrypt } from "@/utils/index";
|
|||
|
import TASK from "./taskQueue";
|
|||
|
|
|||
|
const errMsg = {
|
|||
|
0: "正常",
|
|||
|
10000: "未初始化蓝牙适配器",
|
|||
|
10001: "请检查手机蓝牙是否打开或微信是否授权蓝牙",
|
|||
|
10002: "没有找到指定设备",
|
|||
|
10003: "连接失败",
|
|||
|
10004: "没有找到指定服务",
|
|||
|
10005: "没有找到指定特征",
|
|||
|
10006: "当前连接已断开",
|
|||
|
10007: "当前特征不支持此操作",
|
|||
|
10008: "其余所有系统上报的异常",
|
|||
|
10009: "Android系统版本低于4.3不支持蓝牙功能",
|
|||
|
10012: "连接超时",
|
|||
|
10013: "连接超时",
|
|||
|
1509008: "请检查手机定位是否打开或微信是否授权定位",
|
|||
|
};
|
|||
|
|
|||
|
class Bluetooth {
|
|||
|
config = {
|
|||
|
serviceId: "4E31BF4A-8507-3A2B-7344-C7EDACD38104", //设备服务id
|
|||
|
NotifyUUID: "4E31BF4B-8507-3A2B-7344-C7EDACD38104",
|
|||
|
WriteUUID: "4E31BF4C-8507-3A2B-7344-C7EDACD38104",
|
|||
|
};
|
|||
|
|
|||
|
// 连接状态
|
|||
|
linkFlag = false;
|
|||
|
// 蓝牙是否可用
|
|||
|
available = true;
|
|||
|
// 蓝牙初始化
|
|||
|
isInit = false;
|
|||
|
|
|||
|
timeout = null;
|
|||
|
timeout1 = null;
|
|||
|
|
|||
|
// 判断是否搜索到设备
|
|||
|
isFindBt = null;
|
|||
|
|
|||
|
platform = Taro.getSystemInfoSync().platform;
|
|||
|
appAuthorize = Taro.getAppAuthorizeSetting();
|
|||
|
deviceInfo = {};
|
|||
|
callBack = () => { };
|
|||
|
searchBack = () => { };
|
|||
|
|
|||
|
constructor() {
|
|||
|
// if (this.platform == "android") {
|
|||
|
// this.deviceInfo = { deviceId: "50:C0:F0:F2:99:5B", mac: "50:C0:F0:F2:99:5B", name: "W53A", state: false };
|
|||
|
// } else {
|
|||
|
// this.deviceInfo = { deviceId: "", mac: "50:C0:F0:C8:CC:88", name: "W53A", state: false };
|
|||
|
// // this.deviceInfo = { deviceId: "A890AB3C-0BC3-1418-1132-CA27514B147B", mac: "50:C0:F0:F2:99:5B", name: "W53A", state: false };
|
|||
|
// }
|
|||
|
this.onBluetoothAdapterStateChange();
|
|||
|
this.onBLEConnectionStateChange();
|
|||
|
this.onBluetoothDeviceFound();
|
|||
|
}
|
|||
|
// 错误
|
|||
|
fail(res) {
|
|||
|
this.linkFlag = false;
|
|||
|
if (!res.errCode) return;
|
|||
|
let starIde = res.errMsg.indexOf("fail") + 5;
|
|||
|
Taro.showToast({ title: errMsg[res.errCode] || errMsg[res.errno] || res.errMsg.substring(starIde), icon: "none", duration: 3000 });
|
|||
|
}
|
|||
|
|
|||
|
// 监测手机蓝牙状态
|
|||
|
onBluetoothAdapterStateChange() {
|
|||
|
Taro.onBluetoothAdapterStateChange(({ available, discovering }) => {
|
|||
|
this.available = available;
|
|||
|
// 手机蓝牙开启
|
|||
|
if (this.available && !this.isInit) {
|
|||
|
Taro.closeBluetoothAdapter({
|
|||
|
success: (res) => {
|
|||
|
this.openBluetoothAdapter();
|
|||
|
},
|
|||
|
});
|
|||
|
console.log("手机蓝牙开启");
|
|||
|
}
|
|||
|
// 手机蓝牙关闭
|
|||
|
if (!available) {
|
|||
|
this.isInit = false;
|
|||
|
this.deviceInfo.state = false;
|
|||
|
this.linkFlag = false;
|
|||
|
this.callBack(this.deviceInfo);
|
|||
|
console.log("手机蓝牙关闭");
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
// 初始化蓝牙模块
|
|||
|
openBluetoothAdapter() {
|
|||
|
return Taro.openBluetoothAdapter({
|
|||
|
success: () => {
|
|||
|
console.log("初始化蓝牙模块");
|
|||
|
this.startBluetoothDevicesDiscovery()
|
|||
|
this.isInit = true;
|
|||
|
},
|
|||
|
fail: (err) => {
|
|||
|
this.isInit = false;
|
|||
|
this.fail(err);
|
|||
|
},
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 开始搜寻附近的蓝牙外围设备
|
|||
|
startBluetoothDevicesDiscovery() {
|
|||
|
console.log("搜索蓝牙");
|
|||
|
if (this.appAuthorize.locationAuthorized != "authorized" && this.platform == "android")
|
|||
|
return Taro.showToast({ title: "请打开微信定位权限", icon: "none", duration: 3000 });
|
|||
|
Taro.startBluetoothDevicesDiscovery({
|
|||
|
allowDuplicatesKey: true,
|
|||
|
powerLevel: "high",
|
|||
|
success: (res) => {
|
|||
|
this.isFindBt = setTimeout(() => {
|
|||
|
if (this.platform == "android") {
|
|||
|
this.stopBluetoothDevicesDiscovery();
|
|||
|
Taro.showModal({
|
|||
|
title: "未搜索到设备",
|
|||
|
content: "1、请检查设备是否在附近,或者设备是否被其他应用连接。2、请检查手机微信应用权限中的‘附近的设备权限’是否打开。",
|
|||
|
showCancel: false,
|
|||
|
success: () => {
|
|||
|
const pages = Taro.getCurrentPages();
|
|||
|
if (pages?.length > 1) {
|
|||
|
Taro.navigateBack();
|
|||
|
}
|
|||
|
},
|
|||
|
});
|
|||
|
}
|
|||
|
this.isFindBt && this.clearTimeoutFn("isFindBt");
|
|||
|
}, 6000);
|
|||
|
},
|
|||
|
fail: (err) => {
|
|||
|
this.fail(err);
|
|||
|
this.stopBluetoothDevicesDiscovery();
|
|||
|
},
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 监听搜索到新设备的事件
|
|||
|
// 定义一个方法用于处理蓝牙设备发现事件
|
|||
|
onBluetoothDeviceFound() {
|
|||
|
Taro.onBluetoothDeviceFound((res) => {
|
|||
|
const devices = res.devices[0];
|
|||
|
const advertisData = this.ab2hex(devices.advertisData);
|
|||
|
const mac = this.strInsert(String(advertisData).substring(4, 16)).toLocaleUpperCase();
|
|||
|
// 判断是否有搜索到设备
|
|||
|
this.isFindBt && this.clearTimeoutFn("isFindBt");
|
|||
|
|
|||
|
if (devices.name && devices.name == "LE-AB2020") {
|
|||
|
const item = {
|
|||
|
deviceId: devices.deviceId,
|
|||
|
mac: mac,
|
|||
|
name: devices.name,
|
|||
|
};
|
|||
|
|
|||
|
console.log(JSON.stringify(item), "搜索到设备");
|
|||
|
this.deviceInfo = { ...this.deviceInfo, ...item }
|
|||
|
this.stopBluetoothDevicesDiscovery();
|
|||
|
this.createBleConnection();
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 蓝牙连接
|
|||
|
createBleConnection() {
|
|||
|
if (!this.available || this.linkFlag) return;
|
|||
|
this.linkFlag = true;
|
|||
|
Taro.setStorageSync("deviceInfo", this.deviceInfo);
|
|||
|
console.log("正在连接设备");
|
|||
|
Taro.createBLEConnection({
|
|||
|
timeout: 8000,
|
|||
|
deviceId: this.deviceInfo.deviceId,
|
|||
|
success: () => {
|
|||
|
this.deviceInfo.state = true;
|
|||
|
this.getBLEDeviceServices();
|
|||
|
// this.getBLEDeviceCharacteristics();
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
this.linkFlag = false;
|
|||
|
console.log(res, this.deviceInfo, "连接错误");
|
|||
|
this.callBack({ ...this.deviceInfo, errCode: 10003 });
|
|||
|
if (!this.deviceInfo.deviceId) return this.timeout && this.clearTimeoutFn("timeout");
|
|||
|
if (this.timeout) return;
|
|||
|
if (res.errno == "1509007") {
|
|||
|
this.closeBluetoothAdapter();
|
|||
|
} else {
|
|||
|
this.timeout = setTimeout(() => {
|
|||
|
this.timeout && this.clearTimeoutFn("timeout");
|
|||
|
this.createBleConnection();
|
|||
|
}, 10000);
|
|||
|
}
|
|||
|
},
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 获取蓝牙多个service
|
|||
|
getBLEDeviceServices() {
|
|||
|
Taro.getBLEDeviceServices({
|
|||
|
deviceId: this.deviceInfo.deviceId,
|
|||
|
success: (res) => {
|
|||
|
console.log(JSON.stringify(res), 'JSON.stringify(res)');
|
|||
|
|
|||
|
this.getBLEDeviceCharacteristics();
|
|||
|
},
|
|||
|
fail: (err) => this.fail(err),
|
|||
|
});
|
|||
|
}
|
|||
|
// 获取蓝牙低功耗设备某个服务中所有特征
|
|||
|
getBLEDeviceCharacteristics() {
|
|||
|
Taro.getBLEDeviceCharacteristics({
|
|||
|
deviceId: this.deviceInfo.deviceId,
|
|||
|
serviceId: this.config.serviceId,
|
|||
|
success: (res) => {
|
|||
|
this.notifyBLECharacteristicValueChange();
|
|||
|
},
|
|||
|
fail: (err) => this.fail(err),
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 启用蓝牙低功耗设备特征值变化时的 notify 功能
|
|||
|
notifyBLECharacteristicValueChange() {
|
|||
|
Taro.notifyBLECharacteristicValueChange({
|
|||
|
deviceId: this.deviceInfo.deviceId,
|
|||
|
serviceId: this.config.serviceId,
|
|||
|
characteristicId: this.config.NotifyUUID,
|
|||
|
state: true,
|
|||
|
success: (res) => {
|
|||
|
console.log("连接成功");
|
|||
|
|
|||
|
this.callBack(this.deviceInfo);
|
|||
|
},
|
|||
|
fail: (err) => this.fail(err),
|
|||
|
});
|
|||
|
}
|
|||
|
//监听设备返回的数据
|
|||
|
onBLECharacteristicValueChange(fn) {
|
|||
|
Taro.onBLECharacteristicValueChange((data) => {
|
|||
|
let bytes = new Uint8Array(data.value);
|
|||
|
let aesBytes = null;
|
|||
|
let value = String(this.ab2hex(bytes));
|
|||
|
// 长度在16内的需要解密
|
|||
|
if (bytes.byteLength == 17 && bytes[0] == 0xfc) {
|
|||
|
let aesText = this.strInsert(aes128Decrypt(value.substring(2))).split(":");
|
|||
|
let bf = new ArrayBuffer(aesText.length);
|
|||
|
let dv = new DataView(bf);
|
|||
|
aesText.forEach((item, index) => {
|
|||
|
dv.setUint8(index, `0x${item}`);
|
|||
|
});
|
|||
|
aesBytes = new Uint8Array(bf);
|
|||
|
}
|
|||
|
// 获取电量
|
|||
|
if (aesBytes && aesBytes[1] == 0xa1 && aesBytes[2] == 0x07 && aesBytes[3] == 0xb3) {
|
|||
|
useStore.setState((state) => {
|
|||
|
state.battery = aesBytes[4];
|
|||
|
});
|
|||
|
}
|
|||
|
console.log(`设备回复====>${bytes.byteLength == 17 ? String(this.ab2hex(aesBytes)) : value}`);
|
|||
|
TASK.executeTask();
|
|||
|
fn && fn(bytes, aesBytes, value);
|
|||
|
});
|
|||
|
}
|
|||
|
// 向蓝牙写入数据
|
|||
|
writeBleValue(value) {
|
|||
|
console.log("发送指令====>" + String(this.ab2hex(value)).toLocaleUpperCase());
|
|||
|
return Taro.writeBLECharacteristicValue({
|
|||
|
deviceId: this.deviceInfo.deviceId,
|
|||
|
serviceId: this.config.serviceId,
|
|||
|
characteristicId: this.config.WriteUUID,
|
|||
|
writeType: "writeNoResponse",
|
|||
|
value: value,
|
|||
|
success(res) {
|
|||
|
console.log('writeBLECharacteristicValue success', JSON.stringify(res));
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
//蓝牙断开
|
|||
|
onBLEConnectionStateChange() {
|
|||
|
Taro.onBLEConnectionStateChange((res) => {
|
|||
|
if (!res.connected) {
|
|||
|
console.log(res, "蓝牙断开");
|
|||
|
this.deviceInfo.state = res.connected;
|
|||
|
this.callBack(this.deviceInfo);
|
|||
|
this.linkFlag = false;
|
|||
|
if (!this.available || this.timeout || !this.isInit || !this.deviceInfo.deviceId) return;
|
|||
|
this.timeout = setTimeout(() => {
|
|||
|
this.timeout && this.clearTimeoutFn("timeout");
|
|||
|
this.createBleConnection();
|
|||
|
}, 1000);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
//停止搜寻附近的蓝牙外围设备
|
|||
|
stopBluetoothDevicesDiscovery() {
|
|||
|
this.isFindBt && this.clearTimeoutFn("isFindBt");
|
|||
|
Taro.stopBluetoothDevicesDiscovery();
|
|||
|
console.log("停止搜寻");
|
|||
|
}
|
|||
|
// 断开与蓝牙低功耗设备的连接
|
|||
|
closeBluetoothAdapter() {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
this.linkFlag = false;
|
|||
|
this.deviceInfo = { state: false };
|
|||
|
wx.removeStorageSync("deviceInfo");
|
|||
|
this.callBack(this.deviceInfo);
|
|||
|
Taro.closeBluetoothAdapter({
|
|||
|
success: (res) => {
|
|||
|
this.openBluetoothAdapter().then(resolve).catch(reject);
|
|||
|
},
|
|||
|
});
|
|||
|
})
|
|||
|
|
|||
|
}
|
|||
|
// 设备响应回调
|
|||
|
deviceCallBack(fn) {
|
|||
|
this.callBack = fn;
|
|||
|
}
|
|||
|
// 转换二进制数据
|
|||
|
ab2hex(buffer) {
|
|||
|
if (!buffer) return;
|
|||
|
const hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
|
|||
|
return ("00" + bit.toString(16)).slice(-2);
|
|||
|
});
|
|||
|
return hexArr.join("").toLocaleLowerCase();
|
|||
|
}
|
|||
|
/**
|
|||
|
* 字符串插入字符
|
|||
|
* @param {*} str 字符串
|
|||
|
* @param {*} length 间隔多少个插入
|
|||
|
* @param {*} fmt 插入符号
|
|||
|
* @returns
|
|||
|
*/
|
|||
|
strInsert(str, length = 2, fmt = ":") {
|
|||
|
if (!str || !str.length) return "";
|
|||
|
var reg = new RegExp("\\w{1," + length + "}", "g");
|
|||
|
let ma = str.match(reg);
|
|||
|
return ma.join(fmt).toLocaleLowerCase();
|
|||
|
}
|
|||
|
|
|||
|
// 清除计时器
|
|||
|
clearTimeoutFn(name) {
|
|||
|
clearTimeout(this[name]);
|
|||
|
this[name] = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
export default new Bluetooth();
|