let blueApi = {
config: {
deviceNo: "",
device_info: "",
server_info: "6E40",
write_info: "0002",
read_info: "0003",
connectCmd: [0xAA, 0x7E, 0x54, 0x59, 0x5A, 0xAA],
checkCmd: [0x55, 0xE7, 0x54, 0x59, 0xA5, 0x55],
that: null
},
blue_data: {
isFoundDevice:false,
device_id: "",
service_id: "",
write_id: "",
read_id: "",
notify_id: "",
package: null,
isConnect: false,
isAuthorization: false
},
deviceDb: {
isOnline: false,
isUsing: false,
isClose: false,
isPause: false,
surplusTime: 0,
usedTime: 0,
totalTime: 0,
mode: 3
},
showToast(text) {
wx.showToast({
title: text,
icon: 'none',
duration: 2500
})
},
openBluetoothAdapterError(errCode) {
errCode = Number(errCode)
let errMsg = "未初始化蓝牙适配器"
switch (errCode) {
case 10001:
errMsg = "请检查手机蓝牙是否打开";
break;
case 10002:
errMsg = "没有找到指定蓝牙设备";
break;
case 10003:
errMsg = "蓝牙连接失败,请检查是否开启[蓝牙]和[位置定位]";
break;
case 10004:
errMsg = "没有找到蓝牙设备指定服务";
break;
case 10005:
errMsg = "没有找到蓝牙设备服务指定特征值";
break;
case 10007:
errMsg = "当前蓝牙设备服务特征值不支持此操作";
break;
case 10008:
errMsg = "当前蓝牙适配器不可用";
break;
case 10006:
errMsg = "当前蓝牙连接已断开";
break;
case 10009:
errMsg = "当前设备不支持蓝牙";
break;
default:
break;
}
return errMsg
},
connect(deviceNo, obj, isConnect) {
if (this.blue_data.device_id) {
this.disconnect();
this.closeBluetoothAdapter()
this.blue_data.isFoundDevice = false
this.sleep(500)
}
if (obj) {
this.config.that = obj
}
this.blue_data.isConnect = isConnect || false
if (deviceNo && this.config.device_info !== deviceNo) {
this.blue_data.isFoundDevice = false
this.config.deviceNo = this.config.device_info;
this.config.device_info = deviceNo;
this.blue_data.device_id = "";
}
if (!wx.openBluetoothAdapter) {
this.showToast("当前微信版本过低,无法使用蓝牙功能,请升级到最新微信版本后重试。");
return;
}
const _this = this;
wx.openBluetoothAdapter({
complete: (res) => {
if (res.errCode && res.errCode != 0) {
_this.showToast(_this.openBluetoothAdapterError(res.errCode))
} else {
setTimeout(()=>{
_this.getBlueState();
},200)
}
}
})
},
directive: {
getDeviceOnlineStatus: [0x7e, 0, 0, 0, 0, 0, 0, 0, 0, 0x5a],
openAuthorization(seconds) {
let instructions = [0x7e, 0, 0, 0, 0, 0, 1, 0, 3, 0x5a]
let arr = seconds.toString(16).split('').map((t) => {
return parseInt(t, 16)
})
let arrLen = arr.length
instructions.splice(5 - arrLen, arrLen, ...arr)
return instructions
},
getDeviceStatus: [0x7e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5a],
deviceControl(type = 6, params = 3) {
let instructions = [0x7e, 0, 6, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5a]
instructions.splice(2, 3, ...[type, 0, params])
return instructions
}
},
dealWith(res) {
const _this = this
if (res.type === 0) {
_this.showToast("蓝牙连接成功")
setTimeout(() => {
_this.sendMsg(_this.directive.getDeviceStatus, true)
}, 250)
} else if (res.type === 1) {
_this.deviceDb.isOnline = true
} else if (res.type === 2) {
_this.config.that.countdownFunc()
setTimeout(() => {
_this.sendMsg(_this.directive.deviceControl(6, _this.deviceDb.mode))
}, 250)
} else {
const arr = res.package
if (_this.blue_data.isConnect) {
_this.blue_data.isAuthorization = true
_this.blue_data.isConnect = false
if (arr[22] === 0) {
if (_this.config.deviceNo === _this.config.device_info) {
let timeText = arr[3].toString(16) + arr[4].toString(16) + arr[5].toString(16) + arr[6].toString(16)
_this.deviceDb.surplusTime = parseInt(timeText, 16)
_this.config.that.setData({
isUsing: _this.deviceDb.isUsing,
seconds: _this.deviceDb.surplusTime
})
setTimeout(() => {
_this.config.that.countdownFunc()
}, 0)
} else {
wx.showModal({
title: '温馨提示',
content: '是否强制结束当前运行中设备,以便开始新的按摩服务?',
showCancel: true,
confirmColor: "#6A97FE",
confirmText: '确定',
success(mres) {
if (mres.confirm) {
_this.sendMsg(_this.directive.deviceControl(8, 1))
}
}
})
}
} else {
wx.navigateTo({
url: '../massage-package/massage-package?deviceNo=' + _this.config.device_info + '&isBlue=true'
})
}
} else {
_this.deviceDb.isUsing = arr[22] === 0 ? true : false
_this.deviceDb.isClose = arr[19] === 0 ? true : false
let timeText = arr[3].toString(16) + arr[4].toString(16) + arr[5].toString(16) + arr[6].toString(16)
_this.deviceDb.surplusTime = parseInt(timeText, 16)
_this.config.that.setData({
isUsing: _this.deviceDb.isUsing,
seconds: _this.deviceDb.surplusTime
})
}
}
wx.hideLoading()
},
writeBLEValue(buf, callback) {
const _this = this;
wx.writeBLECharacteristicValue({
deviceId: _this.blue_data.device_id,
serviceId: _this.blue_data.service_id,
characteristicId: _this.blue_data.write_id,
value: buf,
success: (res) => {
callback(res)
},
fail(res) {
callback(false)
_this.showToast(_this.openBluetoothAdapterError(res.errCode))
}
})
},
sendMsg(msg, isCheckCmd) {
wx.showLoading({
title: '请稍候...'
})
const _this = this;
const arrayLen = msg.length;
for (let i = 0; i < arrayLen; i += 20) {
let arrBufLen = (arrayLen - i - 20) > 0 ? 20 : arrayLen - i;
let arr = msg.slice(i, i + arrBufLen);
let buf = _this.arraytoToArrayBuffer(arr);
setTimeout(() => {
_this.writeBLEValue(buf, (res) => {
if (res) {
if (isCheckCmd && i === 0) {
setTimeout(() => {
if (!_this.blue_data.isAuthorization) {
wx.hideLoading()
wx.navigateTo({
url: '../massage-package/massage-package?deviceNo=' + _this.config.device_info + '&isBlue=true'
})
}
}, 4000)
}
} else {
wx.showModal({
title: '温馨提示',
content: '蓝牙数据发送失败,是否重新发送?',
showCancel: true,
confirmColor: "#6A97FE",
confirmText: '确定',
success(mres) {
if (mres.confirm) {
_this.sendMsg(msg, isCheckCmd)
}
}
})
wx.hideLoading()
}
})
}, i / 20 * 250)
}
},
onNotifyChange() {
const _this = this;
wx.onBLECharacteristicValueChange((res) => {
let msg = _this.arrayBufferToHexString(res.value);
let typedArray = new Uint8Array(msg.match(/[\da-f]{2}/gi).map(function(h) {
return parseInt(h, 16)
}))
_this.stickyPackage(typedArray, (res) => {
if (res) {
_this.dealWith(res)
} else {
}
})
})
},
disconnect() {
const _this = this;
wx.closeBLEConnection({
deviceId: _this.blue_data.device_id,
success(res) {}
})
},
closeBluetoothAdapter() {
wx.closeBluetoothAdapter({
success: (res) => {}
})
},
getBlueState() {
const _this = this;
if (_this.blue_data.device_id != "" && wx.getSystemInfoSync().platform.toUpperCase() ==="ANDROID") {
_this.connectDevice();
return;
}
wx.getBluetoothAdapterState({
success: (res) => {
if (!!res && res.available) {
_this.startSearch();
}
}
})
},
startSearch() {
const _this = this;
wx.startBluetoothDevicesDiscovery({
services: [],
success(res) {
wx.onBluetoothDeviceFound((res) => {
if (_this.blue_data.isFoundDevice) {
_this.stopSearch();
return
}else{
_this.showToast("正在搜索蓝牙设备...")
}
let device = _this.filterDevice(res.devices);
if (device && !_this.blue_data.isFoundDevice) {
_this.blue_data.isFoundDevice=true
_this.showToast("设备搜索成功,正在连接...")
_this.blue_data.device_id = device.deviceId;
_this.stopSearch();
_this.connectDevice();
}
});
setTimeout(()=>{
if (!_this.blue_data.isFoundDevice){
_this.stopSearch();
wx.showModal({
title: '温馨提示',
content: '无法搜索到蓝牙,请检查是否开启[位置定位]权限',
showCancel: true,
confirmColor: "#6A97FE",
confirmText: '重新搜索',
success(mres) {
if (mres.confirm) {
_this.startSearch();
}
}
})
}
},10000)
},
fail(res){
_this.showToast(_this.openBluetoothAdapterError(res.errCode))
}
})
},
connectDevice() {
const _this = this;
wx.createBLEConnection({
deviceId: _this.blue_data.device_id,
success(res) {
_this.getDeviceService();
},
fail(res) {
_this.showToast(_this.openBluetoothAdapterError(res.errCode))
}
})
},
getDeviceService() {
const _this = this;
wx.getBLEDeviceServices({
deviceId: _this.blue_data.device_id,
success: (res) => {
let service_id = _this.filterService(res.services);
if (service_id != "") {
_this.blue_data.service_id = service_id;
_this.getDeviceCharacter();
}
},
fail(res) {
_this.showToast(_this.openBluetoothAdapterError(res.errCode))
}
})
},
getDeviceCharacter() {
const _this = this;
wx.getBLEDeviceCharacteristics({
deviceId: _this.blue_data.device_id,
serviceId: _this.blue_data.service_id,
success: (res) => {
let notify_id, write_id, read_id;
for (let i = 0; i < res.characteristics.length; i++) {
if (res.characteristics[i].uuid.indexOf(_this.config.write_info) != -1) {
write_id = res.characteristics[i].uuid;
}
if (res.characteristics[i].properties.notify) {
notify_id = res.characteristics[i].uuid;
}
if (res.characteristics[i].uuid.indexOf(_this.config.read_info) != -1) {
read_id = res.characteristics[i].uuid;
}
}
if (notify_id != null && write_id != null) {
_this.blue_data.notify_id = notify_id;
_this.blue_data.write_id = write_id;
_this.blue_data.read_id = read_id;
setTimeout(() => {
_this.openNotify();
}, 100);
}
},
fail(res) {
_this.showToast(_this.openBluetoothAdapterError(res.errCode))
}
})
},
openNotify() {
const _this = this;
wx.notifyBLECharacteristicValueChange({
state: true,
deviceId: _this.blue_data.device_id,
serviceId: _this.blue_data.service_id,
characteristicId: _this.blue_data.notify_id,
complete: (res) => {
if (res.errCode && res.errCode != 0) {
_this.showToast(_this.openBluetoothAdapterError(res.errCode))
return
}
setTimeout(() => {
_this.sendMsg(_this.config.connectCmd);
}, 1000);
_this.onNotifyChange();
}
})
},
stopSearch() {
const _this = this;
wx.stopBluetoothDevicesDiscovery({
success: (res) => {}
})
},
stickyPackage(arr, callback) {
const _this = this
const arrLen = arr.length
if (arrLen === 0) {
callback(false)
} else if (arr[0] === 85 && arr[arrLen - 1] === 85 && arrLen > 1) {
callback({
type: 0,
packageLen: arrLen,
package: arr
})
} else if (arr[0] === 126 && arr[arrLen - 1] === 90 && (arrLen === 10 || arrLen === 24)) {
let type = 3
if (arrLen === 10) {
type = (arr[arrLen - 2] === 1 && arr[arrLen - 4] === 1) ? 2 : 1
}
let data = {
type: type,
packageLen: arrLen,
package: arr
}
callback(data)
} else {
let subpackage = _this.blue_data.package || new Uint8Array(0);
if (subpackage.length === 0 && arr[0] !== 126) {
callback(false)
return
}
let tempPackage = new Uint8Array(subpackage.length + arrLen)
if (subpackage.length > 0) {
tempPackage.set(subpackage, 0)
}
tempPackage.set(arr, subpackage.length)
_this.blue_data.package = tempPackage
const packageLen = _this.blue_data.package.length
if (_this.blue_data.package[0] === 126 && arr[arrLen - 1] === 90 && (packageLen === 10 || packageLen === 24)) {
const stickyData = _this.blue_data.package
let packageType = 3
if (packageLen === 10) {
packageType = (stickyData[packageLen - 2] === 1 && stickyData[packageLen - 4] === 1) ? 2 : 1
}
let packageData = {
type: packageType,
packageLen: packageLen,
package: stickyData
}
_this.blue_data.package = null
callback(packageData)
} else {
callback(false)
}
}
},
arraytoToArrayBuffer(array) {
let typedArray = new Uint8Array(array.map((h) => {
return parseInt(h.toString(16), 16)
}))
return typedArray.buffer
},
arrayBufferToHexString(buffer) {
let bufferType = Object.prototype.toString.call(buffer)
if (buffer != '[object ArrayBuffer]') {
return
}
let dataView = new DataView(buffer)
var hexStr = '';
for (let i = 0; i < dataView.byteLength; i++) {
let str = dataView.getUint8(i);
let hex = (str & 0xff).toString(16);
hex = (hex.length === 1) ? '0' + hex : hex;
hexStr += hex;
}
return hexStr.toUpperCase();
},
hexStringToArrayBuffer(str, ind = 0) {
if (!str) {
return new ArrayBuffer(0);
}
let buffer = new ArrayBuffer(str.length / 2);
let dataView = new DataView(buffer)
for (let i = 0, len = str.length; i < len; i += 2) {
let code = parseInt(str.substr(i, 2), 16)
dataView.setUint8(ind, code)
ind++
}
return buffer;
},
filterDevice(device) {
let obj = null
for (let i = 0; i < device.length; i++) {
if (device[i].name && device[i].name === this.config.device_info) {
obj = {
name: device[i].name,
deviceId: device[i].deviceId
}
break;
}
}
return obj
},
filterService(services) {
let service_id = "";
for (let i = 0; i < services.length; i++) {
if (~services[i].uuid.toUpperCase().indexOf(this.config.server_info)) {
service_id = services[i].uuid;
break;
}
}
return service_id;
},
sleep(delay){
let start = (new Date()).getTime();
while ((new Date()).getTime() - start < delay) {
continue;
}
}
}
module.exports = {
blueApi: blueApi
}