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) } }) } }) }