494 lines
14 KiB
JavaScript
494 lines
14 KiB
JavaScript
export class Result {
|
|
static codes = {
|
|
Success: 0,
|
|
Fail: -1,
|
|
|
|
NotMoreData: -10,
|
|
|
|
NotAvailableBluetooth: -20,
|
|
NotAvailableBluetoothPermission: -21,
|
|
NotAvailableWeChatNearbyDevicesPermission: -22,
|
|
NotAvailableWeChatLocationPermission: -23,
|
|
NotAvailableWeChatNearbyDevicesEmpty: -24,
|
|
NotAvailableWeChatBluetoothPermission: -25,
|
|
DeviceHasBeenReset: -30,
|
|
|
|
NotRegisteredLock: 4,
|
|
NotTokenLock: 6,
|
|
NotMoreKeyLock: 12,
|
|
ReadyHasKeyLock: 15,
|
|
ReadyHasPassword: 251
|
|
}
|
|
|
|
static resultsMap = new Map([
|
|
[Result.codes.Success, { message: '成功', data: {} }],
|
|
[Result.codes.Fail, { message: '失败', data: {} }],
|
|
|
|
[Result.codes.NotMoreData, { message: '没有更多数据', data: {} }],
|
|
|
|
[Result.codes.NotAvailableBluetooth, { message: '蓝牙尚未打开,请先打开蓝牙', data: {} }],
|
|
[
|
|
Result.codes.NotAvailableBluetoothPermission,
|
|
{ message: '小程序蓝牙功能被禁用,请打开小程序蓝牙权限', data: {} }
|
|
],
|
|
[
|
|
Result.codes.NotAvailableWeChatNearbyDevicesPermission,
|
|
{ message: '蓝牙功能需要附近设备权限,请前往设置开启微信的附近设备权限后再试', data: {} }
|
|
],
|
|
[
|
|
Result.codes.NotAvailableWeChatLocationPermission,
|
|
{ message: '蓝牙功能需要定位权限,请前往设置开启微信的定位权限后再试', data: {} }
|
|
],
|
|
[
|
|
Result.codes.NotAvailableWeChatNearbyDevicesEmpty,
|
|
{
|
|
message: '蓝牙功能需要定位服务,请前往设置开启定位服务后再试',
|
|
data: {}
|
|
}
|
|
],
|
|
[
|
|
Result.codes.NotAvailableWeChatBluetoothPermission,
|
|
{
|
|
message: '微信的蓝牙权限被禁用,请前往设置开启微信的蓝牙权限后再试',
|
|
data: {}
|
|
}
|
|
],
|
|
[Result.codes.DeviceHasBeenReset, { message: '设备已被重置', data: {} }],
|
|
|
|
[Result.codes.NotRegisteredLock, { message: '用户在锁端未注册', data: {} }],
|
|
[Result.codes.NotTokenLock, { message: '用户在锁端token失效', data: {} }],
|
|
[Result.codes.NotMoreKeyLock, { message: '锁端钥匙数量已达上限', data: {} }],
|
|
[Result.codes.ReadyHasKeyLock, { message: '用户已是锁端用户', data: {} }],
|
|
[Result.codes.ReadyHasPassword, { message: '该密码已存在', data: {} }]
|
|
])
|
|
|
|
constructor(code, data, message) {
|
|
const result = Result.resultsMap.get(code)
|
|
if (result) {
|
|
this.code = code
|
|
this.message = message || result.message
|
|
this.data = data || result.data
|
|
} else {
|
|
this.code = code
|
|
this.message = message || ''
|
|
this.data = data || {}
|
|
}
|
|
}
|
|
|
|
// 成功
|
|
static get Success() {
|
|
return new Result(Result.codes.Success)
|
|
}
|
|
|
|
// 失败(默认错误)
|
|
static get Fail() {
|
|
return new Result(Result.codes.Fail)
|
|
}
|
|
|
|
// 没有更多数据
|
|
static get NotMoreData() {
|
|
return new Result(Result.codes.NotMoreData)
|
|
}
|
|
|
|
// 蓝牙未开启
|
|
static get NotAvailableBluetooth() {
|
|
return new Result(Result.codes.NotAvailableBluetooth)
|
|
}
|
|
|
|
// 小程序蓝牙权限被禁用
|
|
static get NotAvailableBluetoothPermission() {
|
|
return new Result(Result.codes.NotAvailableBluetoothPermission)
|
|
}
|
|
|
|
// 微信附近的设备权限被禁用
|
|
static get NotAvailableWeChatNearbyDevicesPermission() {
|
|
return new Result(Result.codes.NotAvailableWeChatNearbyDevicesPermission)
|
|
}
|
|
|
|
// 微信定位权限被禁用
|
|
static get NotAvailableWeChatLocationPermission() {
|
|
return new Result(Result.codes.NotAvailableWeChatLocationPermission)
|
|
}
|
|
|
|
// 手机定位服务被关闭
|
|
static get NotAvailableWeChatNearbyDevicesEmpty() {
|
|
return new Result(Result.codes.NotAvailableWeChatNearbyDevicesEmpty)
|
|
}
|
|
|
|
// 微信的蓝牙权限被禁用
|
|
static get NotAvailableWeChatBluetoothPermission() {
|
|
return new Result(Result.codes.NotAvailableWeChatBluetoothPermission)
|
|
}
|
|
|
|
// 设备已被重置
|
|
static get DeviceHasBeenReset() {
|
|
return new Result(Result.codes.DeviceHasBeenReset)
|
|
}
|
|
|
|
// 用户在锁端未注册
|
|
static get NotRegisteredLock() {
|
|
return new Result(Result.codes.NotRegisteredLock)
|
|
}
|
|
|
|
// 用户在锁端token失效
|
|
static get NotTokenLock() {
|
|
return new Result(Result.codes.NotTokenLock)
|
|
}
|
|
|
|
// 锁端钥匙数量已达上限
|
|
static get NotMoreKeyLock() {
|
|
return new Result(Result.codes.NotMoreKeyLock)
|
|
}
|
|
|
|
// 锁端钥匙数量已达上限
|
|
static get ReadyHasKeyLock() {
|
|
return new Result(Result.codes.ReadyHasKeyLock)
|
|
}
|
|
|
|
// 密码已存在
|
|
static get ReadyHasPassword() {
|
|
return new Result(Result.codes.ReadyHasPassword)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @typedef {Object} err
|
|
* @property {number} errno - 错误代码
|
|
* @property {number} errCode - 错误代码
|
|
* @property {String} errMsg - 错误信息
|
|
*/
|
|
|
|
// 查找设备并连接
|
|
export function searchAndConnectDevice(name, reset = true) {
|
|
// 循环查找设备
|
|
let timer
|
|
// 超时计时器
|
|
let timeoutTimer
|
|
|
|
return new Promise(async resolve => {
|
|
const result = await startBluetoothDevicesDiscovery()
|
|
if (result.code === Result.Success.code) {
|
|
let searchFlag = false
|
|
timeoutTimer = setTimeout(async () => {
|
|
await stopBluetoothDevicesDiscovery()
|
|
clearInterval(timer)
|
|
if (!searchFlag) {
|
|
resolve(Result.NotAvailableWeChatNearbyDevicesEmpty)
|
|
} else {
|
|
resolve(Result.Fail)
|
|
}
|
|
}, 10500)
|
|
timer = setInterval(async () => {
|
|
const queryResult = await getBluetoothDevices()
|
|
if (queryResult.code === Result.Success.code) {
|
|
const deviceList = queryResult.data
|
|
if (searchFlag === false && deviceList.length > 0) {
|
|
searchFlag = true
|
|
}
|
|
for (let i = 0; i < deviceList.length; i++) {
|
|
if (deviceList[i]?.name === name) {
|
|
const uuid = deviceList[i]?.advertisServiceUUIDs[0]
|
|
if (uuid && uuid.slice(2, 8) === '758824') {
|
|
await stopBluetoothDevicesDiscovery()
|
|
clearTimeout(timeoutTimer)
|
|
clearInterval(timer)
|
|
if (uuid.slice(30, 32) === '00' && reset) {
|
|
resolve(Result.DeviceHasBeenReset)
|
|
} else {
|
|
const connectResult = await createBLEConnection(deviceList[i].deviceId)
|
|
resolve(connectResult)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
resolve(queryResult)
|
|
}
|
|
}, 1000)
|
|
} else {
|
|
resolve(result)
|
|
}
|
|
})
|
|
}
|
|
|
|
// 蓝牙操作报错处理
|
|
async function handleError(err, event) {
|
|
if (err.errCode === 10000) {
|
|
const result = await openBluetoothAdapter()
|
|
if (result.code === Result.Success.code) {
|
|
return await event()
|
|
}
|
|
return result
|
|
}
|
|
if (err.errCode === 10001) {
|
|
if (err.state === 3) {
|
|
return Result.NotAvailableWeChatBluetoothPermission
|
|
}
|
|
return Result.NotAvailableBluetooth
|
|
}
|
|
if (err.errno === 3) {
|
|
return Result.NotAvailableWeChatNearbyDevicesPermission
|
|
}
|
|
if (err.errno === 103) {
|
|
return Result.NotAvailableBluetoothPermission
|
|
}
|
|
if (err.errno === 1509008) {
|
|
return Result.NotAvailableWeChatLocationPermission
|
|
}
|
|
if (err.errMsg === 'openBluetoothAdapter:fail already opened') {
|
|
return Result.Success
|
|
}
|
|
return Result.Fail
|
|
}
|
|
|
|
// 初始化蓝牙模块
|
|
function openBluetoothAdapter() {
|
|
return new Promise(resolve => {
|
|
uni.openBluetoothAdapter({
|
|
success() {
|
|
resolve(Result.Success)
|
|
},
|
|
async fail(err) {
|
|
resolve(await handleError(err))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 关闭蓝牙模块
|
|
export function closeBluetoothAdapter() {
|
|
uni.closeBluetoothAdapter()
|
|
}
|
|
|
|
// 移除蓝牙适配器的全部监听
|
|
export function offBluetoothAdapterStateChange() {
|
|
uni.offBluetoothAdapterStateChange()
|
|
}
|
|
|
|
// 监听蓝牙特征值改变
|
|
export function onBLECharacteristicValueChange(callback) {
|
|
uni.onBLECharacteristicValueChange(res => {
|
|
callback(res)
|
|
})
|
|
}
|
|
|
|
// 开始搜索附近的蓝牙设备
|
|
export function startBluetoothDevicesDiscovery() {
|
|
return new Promise(resolve => {
|
|
uni.startBluetoothDevicesDiscovery({
|
|
success() {
|
|
resolve(Result.Success)
|
|
},
|
|
async fail(err) {
|
|
resolve(await handleError(err, startBluetoothDevicesDiscovery))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 获取所有已发现的蓝牙设备
|
|
export function getBluetoothDevices() {
|
|
return new Promise(resolve => {
|
|
uni.getBluetoothDevices({
|
|
success(res) {
|
|
resolve(new Result(Result.Success.code, res.devices))
|
|
},
|
|
async fail(err) {
|
|
resolve(await handleError(err, getBluetoothDevices))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 停止搜索附近的蓝牙设备
|
|
export function stopBluetoothDevicesDiscovery() {
|
|
return new Promise(resolve => {
|
|
uni.stopBluetoothDevicesDiscovery({
|
|
success() {
|
|
resolve(Result.Success)
|
|
},
|
|
async fail(err) {
|
|
resolve(await handleError(err))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 连接低功耗蓝牙设备
|
|
export function createBLEConnection(deviceId, reconnectNumber = 0) {
|
|
return new Promise(resolve => {
|
|
uni.createBLEConnection({
|
|
deviceId,
|
|
timeout: 10000,
|
|
async success() {
|
|
const res = await getBLEDeviceServicesAndCharacteristics(deviceId)
|
|
await notifyBLECharacteristicValueChange(
|
|
deviceId,
|
|
res.data.serviceId,
|
|
res.data.notifyCharacteristicId
|
|
)
|
|
resolve(res)
|
|
},
|
|
async fail(err) {
|
|
if (err.errno === 1509007) {
|
|
const res = await getBLEDeviceServicesAndCharacteristics(deviceId)
|
|
await notifyBLECharacteristicValueChange(
|
|
deviceId,
|
|
res.data.serviceId,
|
|
res.data.notifyCharacteristicId
|
|
)
|
|
resolve(res)
|
|
} else if (err.errno === 1509001 && reconnectNumber < 1) {
|
|
resolve(Result.Fail)
|
|
} else if (reconnectNumber < 1) {
|
|
resolve(await createBLEConnection(deviceId, reconnectNumber + 1))
|
|
} else {
|
|
resolve(Result.Fail)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 获取服务及对应特征值
|
|
async function getBLEDeviceServicesAndCharacteristics(deviceId) {
|
|
const { code, data } = await getBLEDeviceServices(deviceId)
|
|
if (code === Result.Success.code) {
|
|
const { serviceId } = data
|
|
const {
|
|
code,
|
|
data: { notifyCharacteristicId, writeCharacteristicId }
|
|
} = await getBLEDeviceCharacteristics(deviceId, serviceId)
|
|
if (code === Result.Success.code) {
|
|
return new Result(Result.Success.code, {
|
|
deviceId,
|
|
serviceId,
|
|
notifyCharacteristicId,
|
|
writeCharacteristicId
|
|
})
|
|
}
|
|
return Result.Fail
|
|
}
|
|
return Result.Fail
|
|
}
|
|
|
|
// 获取设备的服务
|
|
function getBLEDeviceServices(deviceId) {
|
|
return new Promise(resolve => {
|
|
uni.getBLEDeviceServices({
|
|
deviceId,
|
|
success(res) {
|
|
let serviceId
|
|
for (let i = 0; i < res.services.length; i++) {
|
|
if (res.services[i].uuid.indexOf('FFF0') !== -1) {
|
|
serviceId = res.services[i].uuid
|
|
}
|
|
}
|
|
if (!serviceId) {
|
|
resolve(Result.Fail)
|
|
return
|
|
}
|
|
resolve(new Result(Result.Success.code, { serviceId }))
|
|
},
|
|
fail() {
|
|
resolve(Result.Fail)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 获取服务的特征值
|
|
function getBLEDeviceCharacteristics(deviceId, serviceId) {
|
|
return new Promise(resolve => {
|
|
uni.getBLEDeviceCharacteristics({
|
|
deviceId,
|
|
serviceId,
|
|
success(res) {
|
|
let notifyCharacteristicId
|
|
let writeCharacteristicId
|
|
for (let i = 0; i < res.characteristics.length; i++) {
|
|
const characteristic = res.characteristics[i]
|
|
if (characteristic.properties.notify) {
|
|
notifyCharacteristicId = characteristic.uuid
|
|
}
|
|
if (characteristic.properties.write) {
|
|
writeCharacteristicId = characteristic.uuid
|
|
}
|
|
}
|
|
if (notifyCharacteristicId && writeCharacteristicId) {
|
|
resolve(
|
|
new Result(Result.Success.code, { notifyCharacteristicId, writeCharacteristicId })
|
|
)
|
|
} else {
|
|
resolve(Result.Fail)
|
|
}
|
|
},
|
|
fail() {
|
|
resolve(Result.Fail)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 订阅特征值
|
|
function notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId) {
|
|
return new Promise(resolve => {
|
|
uni.notifyBLECharacteristicValueChange({
|
|
deviceId,
|
|
serviceId,
|
|
characteristicId,
|
|
state: true,
|
|
success() {
|
|
resolve(Result.Success)
|
|
},
|
|
fail() {
|
|
resolve(Result.Fail)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 断开与低功耗蓝牙设备的连接
|
|
export function closeBLEConnection(deviceId) {
|
|
return new Promise(resolve => {
|
|
uni.closeBLEConnection({
|
|
deviceId,
|
|
success() {
|
|
console.log('断开连接成功')
|
|
resolve(Result.Success)
|
|
},
|
|
fail() {
|
|
console.log('断开连接失败')
|
|
resolve(Result.Fail)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 写入特征值
|
|
export function writeBLECharacteristicValue(deviceId, serviceId, characteristicId, binaryData) {
|
|
return new Promise(resolve => {
|
|
const count = Math.ceil(binaryData.length / 20)
|
|
let successCount = 0
|
|
for (let i = 0; i < count; i++) {
|
|
const writeData = binaryData.slice(i * 20, i === count - 1 ? binaryData.length : (i + 1) * 20)
|
|
uni.writeBLECharacteristicValue({
|
|
deviceId,
|
|
serviceId,
|
|
characteristicId,
|
|
value: writeData.buffer,
|
|
success() {
|
|
successCount++
|
|
if (successCount === count) {
|
|
resolve(Result.Success)
|
|
}
|
|
},
|
|
fail() {
|
|
resolve(Result.Fail)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|