123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940 |
- import {
- genReqSecretPkg,
- genOpDoorPkg,
- genQuPowerPkg,
- gnQuDoorLogPkg,
- Delayed,
- decryptAes,
- parseBELDeviceId,
- hexStringToUint8Array
- } from "../utils/bluetoothUtils";
- import {
- getOpenDoorInfo,
- unloadBLEOpenDoorLog,
- getCheckinInfo
- } from "../utils/api";
- import moment from "moment";
- import {
- mapState
- } from "vuex";
- export const bluetooth = {
- data() {
- return {
- // allowedOpenDoor为0时允许点击开门,为1时点击开门替换为loading状态,为2时会弹提示
- allowedOpenDoor: 2,
- isOpeningDoor: false,
- loadingContent: "初始化蓝牙中...",
- openDoorInfo: {
- gatewayDeviceId: "",
- bluetoothDeviceId: "",
- bluetoothName: "",
- bluetoothKey: "",
- androidOptimization: true,
- openDoorParam: null,
- },
- checkinInfo: {}
- };
- },
- computed: {
- ...mapState("m_user", ["userInfo"]),
- ...mapState("m_business", ["currentHotel"])
- },
- methods: {
- async bluetoothOperate() {
- const vueComponent = this;
- /**
- * 此次操作的生命周期数据,会上传给后端
- */
- const bluetoothLifeData = {
- name: this.openDoorInfo.bluetoothName, // 蓝牙门锁名称
- hotelId: this.currentHotel.hotelId,
- operationType: 1,
- isBluetoothOpened: null, // 蓝牙开关是否打开
- bluetoothConnected: false, // 蓝牙是否连接上
- deviceId: null, // 进行连接时,蓝牙的deviceId。安卓可以计算出来,iOS需要扫描
- serviceId: "0000FFF0-0000-1000-8000-00805F9B34FB", // 蓝牙交互服务的service的UUID。苹果端支付宝侧有点特殊,下面进行了处理
- characteristicId: "0000FFF1-0000-1000-8000-00805F9B34FB", // 特征值的UUID。苹果端支付宝侧有点特殊,下面进行了处理
- initBluetoothAdapterTime: null, // 进行蓝牙初始化时的时间
- startBluetoothDiscoveryTime: null, // 蓝牙初始化完成同时开始探索蓝牙 gap1蓝牙初始化
- targetBluetoothFoundTime: null, // 目标蓝牙被找到,同时开始蓝牙的连接 gap2探索目标蓝牙
- targetBluetoothConnectedTime: null, // 蓝牙被连接时的时间 gap3连接蓝牙
- opForIosEndedTime: null, // 连接蓝牙后,完成iOS设备所必须的操作时的时间 gap4 iOS服务耗时
- requestSessionKeyTime: null, // 请求会话秘钥的开始时间 gap5 订阅特征值变化
- getSessionKeyPackageTime: null, // 获取到请求会话秘钥ack的时间
- requestBleOpenDoorTime: null, // 发送开门请求的时间
- getOpenDoorPackageTime: null, // 获取到请求开门返回包的时间
- requestOpenDoorLogTime: null, //发送获取开门记录的时间
- getOpenDoorLogPackageTime: null, // 获取到开门记录返回包的时间
- requestPowerLogTime: null, // 发送获取电量记录的时间
- getPowerLogPackageTime: null, // 获取到电量记录返回报文的时间
- result: 0,
- endTime: null, // 流程中断的时间点
- mobileOs: null,
- totalTime: null,
- isLocationEnabled: null,
- androidOptimizationStatus: this.openDoorInfo.androidOptimization,
- hostName: null, // 小程序宿主名
- hostVersion: null, // 小程序宿主版本
- openDoorParam: JSON.stringify(this.openDoorInfo.openDoorParam), // 开门时的自定义参数
- bluetoothAuthorized: false, // 蓝牙授权开启情况
- errMessage: '',
- // 剩下的为上传相关记录的操作
- floor: Number(this.checkinInfo.floor),
- room: this.checkinInfo.room
- };
- /**
- *
- * 抓取小程序宿主环境的相关信息
- * 由于小程序可能存在bug,当开启蓝牙和位置之后仍旧报错的,退出小程序再重新进入
- * https://developers.weixin.qq.com/community/develop/doc/000a6e376308d0c306a88a20f56800
- */
- try {
- const res = await uni.getSystemInfoSync();
- // console.log("uni.getSystemInfoSync({ includeBluetooth: true })", res);
- bluetoothLifeData.hostName = res.hostName; // 小程序的宿主名称,微信、支付宝等
- bluetoothLifeData.hostVersion = res.hostVersion; // 宿主环境的版本信息
- bluetoothLifeData.mobileOs = res.osName; // 收集操作系统
- // 后期考虑能否获取到开发环境基础库的版本,并添加进数据库
- } catch (e) {
- console.log("抓取小程序宿主环境的相关信息失败", e);
- }
- /**
- * 处理宿主系统为ios且宿主环境为alipay时的服务id和特征值id
- */
- if (bluetoothLifeData.mobileOs === 'ios' && bluetoothLifeData.hostName === 'alipay') {
- bluetoothLifeData.serviceId = 'FFF0'
- bluetoothLifeData.characteristicId = 'FFF1'
- }
- /**
- * 蓝牙的开关以及在未开启安卓优化时位置的开关
- */
- const systemBleAndLocationEnabled = uni.getSystemSetting()
- bluetoothLifeData.isBluetoothOpened = systemBleAndLocationEnabled.bluetoothEnabled
- // 处理手机蓝牙未打开
- if (!bluetoothLifeData.isBluetoothOpened) {
- uni.showModal({
- title: "蓝牙未打开",
- content: "紧急钥匙功能需要蓝牙支持,请打开手机蓝牙后重新开门。",
- });
- vueComponent.allowedOpenDoor = 0;
- return
- }
- // 不开启安卓优化,设备系统是安卓,校验位置是否开启;暂时不校验位置信息授权,测试后再确定是否校验
- bluetoothLifeData.isLocationEnabled = systemBleAndLocationEnabled.locationEnabled
- if (vueComponent.openDoorInfo.androidOptimization === false &&
- bluetoothLifeData.mobileOs === "android" &&
- !bluetoothLifeData.isLocationEnabled) {
- uni.showModal({
- title: "位置未开启",
- content: "紧急钥匙功能需要地理位置支持,请开启位置信息后重试。",
- });
- vueComponent.allowedOpenDoor = 0;
- return
- }
- /**
- * 判断是否获取到当前房间的蓝牙名和蓝牙秘钥,没获取到则报异常并return
- */
- if (!(this.openDoorInfo.bluetoothName && this.openDoorInfo.bluetoothKey)) {
- uni.showModal({
- title: "未获取到设备信息",
- content: "未获取到门锁设备信息,请稍后重试。",
- });
- vueComponent.allowedOpenDoor = 0;
- uni.switchTab({
- url: '/pages/home/home'
- })
- console.log("方法入口告警:蓝牙名或蓝牙秘钥为空");
- return;
- }
- /**
- * 一些超时时间的设置
- */
- const timeoutValue = {
- findDevice: vueComponent.openDoorInfo.openDoorParam.androidScanTimeout ?
- vueComponent.openDoorInfo.openDoorParam.androidScanTimeout : 6000, // 探索发现设备的超时时间
- connectBLE: 5000, // 连接蓝牙的超时时间
- requestSessionKey: 3000, // 请求秘钥的超时时间
- openDoor: 4000, // 请求开门的超时时间
- getOpenDoorLog: 8000,
- getPowerLog: 3000,
- };
- /**
- * 错误码定义
- */
- const ErrCode = {
- bleNotOpened: 10001, // 蓝牙未打开
- findDeviceTimeout: 10002, // 发现蓝牙设备超时
- bleConnectFailed: 10003, // 连接蓝牙失败
- requestSessionKeyTimeout: 10004, // 请求会话秘钥超时
- openDoorTimeout: 10005, // 蓝牙开门超时
- getDoorLogTimeout: 10006, // 获取开门记录超时
- getPowerLogTimeout: 10007, // 获取电量记录超时
- androidNotOpenLocation: 10008, // 安卓设备未打开位置信息,无法扫描蓝牙;仅在未开启蓝牙优化时出现
- };
- /**
- * 蓝牙开门加载时,显示的内容
- */
- const loading = {
- initBLE: '初始化手机蓝牙中...',
- discoveryBLE: '扫描门锁蓝牙中...',
- connectBLE: "连接门锁蓝牙中...",
- isOpeningDoor: '门锁开门中...'
- }
- // loading,开始进行蓝牙开门操作
- vueComponent.isOpeningDoor = true;
- vueComponent.allowedOpenDoor = 1;
- /**
- * 获取用户的当前设置(蓝牙的授权信息)。返回值中只会出现小程序已经向用户请求过的权限。
- */
- await uni.getSetting().then(res => {
- console.log("getSetting", res);
- if (bluetoothLifeData.hostName === 'WeChat') {
- bluetoothLifeData.bluetoothAuthorized =
- res[1].authSetting['scope.bluetooth'] ? res[1].authSetting['scope.bluetooth'] :
- false
- } else if (bluetoothLifeData.hostName === 'alipay') {
- bluetoothLifeData.bluetoothAuthorized =
- res[1].authSetting['bluetooth'] ? res[1].authSetting['bluetooth'] : false
- }
- console.log("bluetoothLifeData.bluetoothAuthorized", bluetoothLifeData
- .bluetoothAuthorized);
- }).catch(err => {
- console.log("getSetting catch err", err);
- })
- /**
- *
- * 开启蓝牙适配器,必须操作,并在Promise中处理授权相关的信息
- */
- bluetoothLifeData.initBluetoothAdapterTime = +new Date();
- await new Promise(async (resolve, reject) => {
- await uni.openBluetoothAdapter({
- mode: "central", //小程序作为中央设备
- }).then((res) => {
- console.log("openBluetoothAdapter success", res);
- if (bluetoothLifeData.hostName === 'WeChat') {
- // 初始化成功且蓝牙已授权
- if (res[1]) {
- if (!res[1]['errno']) {
- bluetoothLifeData.bluetoothAuthorized = true
- resolve('success')
- }
- } else if (res[0]) {
- // 初始化成功,但蓝牙未授权 微信错误码103为用户拒绝授权
- if (res[0].hasOwnProperty('errno') && res[0].errno === 103) {
- uni.showModal({
- title: "温馨提示",
- content: "紧急门锁功能需要使用您的蓝牙,请授权后重新开门。",
- success: async (res) => {
- if (res.confirm) {
- await uni.openSetting().then(
- res => {
- console.log(
- "openSetting",
- res);
- bluetoothLifeData
- .bluetoothAuthorized =
- res[1].authSetting[
- 'scope.bluetooth'
- ]
- resolve()
- })
- }
- },
- });
- }
- }
- } else if (bluetoothLifeData.hostName === 'alipay') {
- // 初始化成功且蓝牙已授权
- if (res[1]) {
- if (res[1]['isSupportBLE']) {
- bluetoothLifeData.bluetoothAuthorized = true
- resolve('success')
- }
- } else if (res[0]) {
- if (res[0].hasOwnProperty('error') && res[0].error === 2001) {
- // 初始化成功,但蓝牙未授权,支付宝错误码为2001
- uni.showModal({
- title: "温馨提示",
- content: "紧急门锁功能需要使用您的蓝牙,请授权后重新开门。",
- success: async (res) => {
- if (res.confirm) {
- await uni.openSetting().then(
- res => {
- console.log(
- "openSetting",
- res);
- bluetoothLifeData
- .bluetoothAuthorized =
- res[1].authSetting[
- 'bluetooth']
- resolve()
- })
- }
- },
- });
- }
- }
- }
- }).catch((err) => {
- console.log("openBluetoothAdapter catch err", err);
- reject(err)
- });
- }).catch(err => {
- console.log(err);
- })
- /**
- *
- * 判断蓝牙是否授权,未授权直接return
- */
- if (!bluetoothLifeData.bluetoothAuthorized) {
- vueComponent.isOpeningDoor = false;
- vueComponent.allowedOpenDoor = 0;
- console.log("手机蓝牙未授权,最外层函数。");
- bluetoothLifeData.endTime = new Date();
- bluetoothLifeData.totalTime =
- bluetoothLifeData.endTime -
- bluetoothLifeData.initBluetoothAdapterTime;
- bluetoothLifeData.result = ErrCode.bleNotOpened; // 蓝牙未打开其实为蓝牙未授权
- unloadBLEOpenDoorLog(bluetoothLifeData).then((res) => {
- console.log(res);
- });
- uni.showModal({
- title: "温馨提示",
- content: "紧急门锁功能需要使用您的蓝牙,请授权后重新开门。",
- success: (res) => {
- if (res.confirm) {
- uni.openSetting().then(res => {
- console.log("openSetting", res);
- })
- }
- },
- });
- closeBluetoothAdapter();
- return;
- }
- /**
- *
- * 在Promise中调用onBluetoothDeviceFound,直到找到目的设备
- * 针对android设备且开启了安卓设备优化,直接跳过此流程
- */
- if (bluetoothLifeData.mobileOs === "ios" ||
- (bluetoothLifeData.mobileOs === "android" && vueComponent.openDoorInfo.androidOptimization ===
- false)) {
- await new Promise(async (resolve, reject) => {
- vueComponent.loadingContent = loading.discoveryBLE
- // console.log("enter onBluetoothDeviceFound Promise");
- // 设置发现设备的超时
- const timeout = setTimeout(() => {
- if (!bluetoothLifeData.targetBluetoothFoundTime) {
- console.log("onBluetoothDeviceFound Promise 发现设备超时");
- bluetoothLifeData.result = ErrCode.findDeviceTimeout;
- uni.offBluetoothDeviceFound();
- reject("发现设备超时");
- }
- }, timeoutValue.findDevice);
- uni.onBluetoothDeviceFound((res) => {
- let devices = res.devices.filter(
- (device) =>
- device.name === vueComponent.openDoorInfo.bluetoothName
- );
- // console.log("onBluetoothDeviceFound 发现设备数", devices.length);
- if (devices.length <= 0) return;
- let {
- deviceId,
- name
- } = devices[0];
- // console.log("deviceId", deviceId, "deviceName", name);
- bluetoothLifeData.deviceId = deviceId;
- bluetoothLifeData.targetBluetoothFoundTime = +new Date();
- stopBluetoothDevicesDiscovery();
- clearTimeout(timeout);
- resolve({
- bleFound: true
- });
- });
- // 调用扫描蓝牙API,扫描周边蓝牙
- bluetoothLifeData.startBluetoothDiscoveryTime = +new Date();
- let discovery = await uni.startBluetoothDevicesDiscovery({
- powerLevel: "high"
- });
- console.log("startBluetoothDevicesDiscovery", discovery);
- }).catch((err) => {
- console.log("onBluetoothDeviceFound Promise catch err", err);
- });
- /**
- *
- * 搜索蓝牙设备超时或安卓设备未打开位置信息时的处理逻辑
- */
- if (!bluetoothLifeData.targetBluetoothFoundTime) {
- console.log("发现设备超时,最外层函数");
- vueComponent.isOpeningDoor = false;
- vueComponent.allowedOpenDoor = 0;
- vueComponent.loadingContent = loading.initBLE
- uni.showModal({
- title: "未找到门锁",
- content: "未找到门锁设备,请确认您的位置是否正确。",
- });
- console.log("未找到门锁");
- await closeBluetoothAdapter();
- bluetoothLifeData.endTime = new Date();
- bluetoothLifeData.totalTime =
- bluetoothLifeData.endTime -
- bluetoothLifeData.initBluetoothAdapterTime;
- unloadBLEOpenDoorLog(bluetoothLifeData).then((res) => {
- console.log(res);
- });
- return;
- }
- } else {
- //该分支不进行蓝牙扫描
- bluetoothLifeData.startBluetoothDiscoveryTime = +new Date();
- bluetoothLifeData.targetBluetoothFoundTime = +new Date();
- // 根据转换规则,置android设备的deviceId
- console.log(vueComponent.openDoorInfo.bluetoothDeviceId);
- bluetoothLifeData.deviceId = parseBELDeviceId(
- vueComponent.openDoorInfo.bluetoothDeviceId
- );
- console.log(
- "android设备连接蓝牙时的deviceId",
- bluetoothLifeData.deviceId
- );
- }
- /**
- *
- * 找到目标设备,开始进行蓝牙连接
- * 在Promise中连接蓝牙设备,超时就 关闭蓝牙适配器 ,第一次超时return是否会影响第二次的成功连接
- */
- await new Promise((resolve, reject) => {
- vueComponent.loadingContent = loading.connectBLE
- const timeout = setTimeout(() => {
- if (!bluetoothLifeData.targetBluetoothConnectedTime) {
- bluetoothLifeData.result = ErrCode.bleConnectFailed;
- console.log("蓝牙连接超时");
- reject("蓝牙连接超时");
- }
- }, timeoutValue.connectBLE)
- uni.createBLEConnection({
- deviceId: bluetoothLifeData.deviceId,
- })
- .then((res) => {
- clearTimeout(timeout)
- bluetoothLifeData.bluetoothConnected = true;
- bluetoothLifeData.targetBluetoothConnectedTime = +new Date();
- console.log("createBLEConnection res", res);
- resolve("蓝牙连接成功")
- })
- .catch((err) => {
- bluetoothLifeData.result = ErrCode.bleConnectFailed;
- console.log("createBLEConnection catch err", err);
- });
- }).catch(err => {
- console.log("createBLEConnection Promise catch err", err);
- })
- /**
- *
- * 判断蓝牙是否连接成功,若失败则直接return
- */
- if (!bluetoothLifeData.bluetoothConnected) {
- vueComponent.isOpeningDoor = false;
- vueComponent.allowedOpenDoor = 0;
- vueComponent.loadingContent = loading.initBLE
- uni.showModal({
- title: "蓝牙连接失败",
- content: "蓝牙连接失败,请确认位置正确后重试。",
- });
- await closeBluetoothAdapter();
- console.log("蓝牙连接失败,最外层函数");
- bluetoothLifeData.endTime = new Date();
- bluetoothLifeData.totalTime =
- bluetoothLifeData.endTime -
- bluetoothLifeData.initBluetoothAdapterTime;
- console.log("蓝牙连接失败");
- unloadBLEOpenDoorLog(bluetoothLifeData).then((res) => {
- console.log(res);
- });
- return;
- }
- /**
- *
- * 蓝牙连接成功后,对iOS设备而言,需对服务及特征值进行扫描
- */
- if (bluetoothLifeData.mobileOs === "ios") {
- await uni
- .getBLEDeviceServices({
- deviceId: bluetoothLifeData.deviceId,
- })
- .then((res) => {
- console.log("getBLEDeviceServices", res);
- })
- .catch((err) => {
- console.log("getBLEDeviceServices err", err);
- });
- await uni
- .getBLEDeviceCharacteristics({
- deviceId: bluetoothLifeData.deviceId,
- serviceId: bluetoothLifeData.serviceId,
- })
- .then((res) => {
- console.log("getBLEDeviceCharacteristics", res);
- })
- .catch((err) => {
- console.log("getBLEDeviceCharacteristics err", err);
- });
- bluetoothLifeData.opForIosEndedTime = +new Date();
- } else {
- bluetoothLifeData.opForIosEndedTime = +new Date();
- }
- /**
- *
- * 对主服务特征值进行订阅;
- */
- const subscribe = await notifyBLECharacteristicValueChange();
- console.log("subscribe", subscribe);
- /**
- *
- * 向蓝牙发请求,请求会话秘钥(写特征值),解出会话秘钥
- */
- await new Promise(async (resolve, reject) => {
- vueComponent.loadingContent = loading.isOpeningDoor
- // console.log("onBLECharacteristicValueChange监听函数,请求会话秘钥");
- // 设置请求会话秘钥的超时
- const timeout = setTimeout(() => {
- if (!bluetoothLifeData.getSessionKeyPackageTime) {
- bluetoothLifeData.result = ErrCode.requestSessionKeyTimeout;
- console.log("请求会话秘钥超时");
- reject("请求会话秘钥超时");
- }
- }, timeoutValue.requestSessionKey);
- uni.offBLECharacteristicValueChange();
- const requestSessionKeyRes = onBLECharacteristicValueChange();
- bluetoothLifeData.requestSessionKeyTime = +new Date();
- const requestSecret = await writeBLECharacteristicValue(
- genReqSecretPkg()
- );
- console.log("requestSecret", requestSecret);
- requestSessionKeyRes.then((res) => {
- // console.log(res);
- if (res == 0x01) {
- clearTimeout(timeout);
- resolve({
- requestSessionKey: true
- });
- } else {
- clearTimeout(timeout);
- reject({
- message: "应答包获取错误,应为请求会话秘钥的应答包。",
- });
- }
- });
- }).catch((err) => {
- console.log(
- "onBLECharacteristicValueChange监听函数,请求会话秘钥。catch err",
- err
- );
- });
- /**
- *
- * 获取会话秘钥超时的处理逻辑
- */
- if (!bluetoothLifeData.getSessionKeyPackageTime) {
- vueComponent.isOpeningDoor = false;
- vueComponent.allowedOpenDoor = 0;
- vueComponent.loadingContent = loading.initBLE
- uni.showModal({
- title: "蓝牙开门失败",
- content: "蓝牙开门失败,请稍后重试!", // 带!号的蓝牙开门失败实际为请求会话秘钥超时
- });
- console.log("获取会话秘钥失败");
- bluetoothLifeData.endTime = new Date();
- bluetoothLifeData.totalTime =
- bluetoothLifeData.endTime -
- bluetoothLifeData.initBluetoothAdapterTime;
- console.log("会话秘钥超时");
- unloadBLEOpenDoorLog(bluetoothLifeData).then((res) => {
- console.log(res);
- });
- return;
- }
- /**
- *
- * 解完请求会话秘钥后,发蓝牙开门报文,解蓝牙开门返回包
- */
- await new Promise(async (resolve, reject) => {
- // console.log("onBLECharacteristicValueChange监听函数,蓝牙开门");
- // 设置蓝牙开门的超时
- const timeout = setTimeout(() => {
- if (!bluetoothLifeData.getOpenDoorPackageTime) {
- bluetoothLifeData.result = ErrCode.openDoorTimeout;
- console.log("蓝牙开门超时");
- reject("蓝牙开门超时");
- }
- }, timeoutValue.openDoor);
- uni.offBLECharacteristicValueChange();
- const requestBLEOpenDoorRes = onBLECharacteristicValueChange();
- bluetoothLifeData.requestBleOpenDoorTime = +new Date();
- const openDoor = await writeBLECharacteristicValue(genOpDoorPkg());
- // console.log("openDoor", openDoor);
- requestBLEOpenDoorRes.then((res) => {
- // console.log(res);
- if (res == 0x11) {
- vueComponent.isOpeningDoor = false;
- vueComponent.allowedOpenDoor = 2;
- clearTimeout(timeout);
- uni.showModal({
- title: "开门成功",
- content: "蓝牙开门成功,祝您入住愉快。",
- });
- resolve({
- bluetoothOpenDoor: true
- });
- } else {
- clearTimeout(timeout);
- reject({
- message: "应答包获取错误,应为请求会话秘钥的应答包。",
- });
- }
- });
- }).catch((err) => {
- console.log(
- "onBLECharacteristicValueChange监听函数,蓝牙开门。catch err",
- err
- );
- });
- /**
- *
- * 判断蓝牙开门是否成功,如果失败,则return
- */
- if (!bluetoothLifeData.getOpenDoorPackageTime) {
- vueComponent.isOpeningDoor = false;
- vueComponent.allowedOpenDoor = 0;
- vueComponent.loadingContent = loading.initBLE
- uni.showModal({
- title: "蓝牙开门失败",
- content: "蓝牙开门失败,请稍后重试。",
- });
- console.log("蓝牙开门失败");
- bluetoothLifeData.endTime = new Date();
- bluetoothLifeData.totalTime =
- bluetoothLifeData.endTime -
- bluetoothLifeData.initBluetoothAdapterTime;
- unloadBLEOpenDoorLog(bluetoothLifeData).then((res) => {
- console.log(res);
- });
- return;
- }
- /**
- *
- * 断开蓝牙连接,关闭蓝牙适配器,并上传所有记录
- */
- await closeBLEConnection(bluetoothLifeData.deviceId);
- await closeBluetoothAdapter();
- bluetoothLifeData.endTime = new Date();
- bluetoothLifeData.totalTime =
- bluetoothLifeData.endTime - bluetoothLifeData.initBluetoothAdapterTime;
- unloadBLEOpenDoorLog(bluetoothLifeData)
- .then((res) => {
- console.log(res);
- vueComponent.allowedOpenDoor = 0;
- vueComponent.loadingContent = loading.initBLE
- })
- .catch((err) => {
- console.log("完成所有操作,上传开门记录", err);
- vueComponent.allowedOpenDoor = 0;
- vueComponent.loadingContent = loading.initBLE
- })
- .finally(() => {
- vueComponent.allowedOpenDoor = 0;
- vueComponent.loadingContent = loading.initBLE
- });
- console.log(bluetoothLifeData);
- /**
- *
- * 以下为函数声明
- */
- function onBLECharacteristicValueChange() {
- // console.log("onBLECharacteristicValueChange监听函数");
- return new Promise((resolve, reject) => {
- const BleStruct = {
- firstPacketReceived: false, //是否第一个报
- buffer: null, // 用于缓存数据流的 ArrayBuffer
- cmd: null, //返回数据包命令号
- length: 0, //数据包长度
- received: 0,
- errCode: 0,
- };
- uni.onBLECharacteristicValueChange(async function(res) {
- console.log("onBLECharacteristicValueChange", res); //打印特征值
- let receiveBuffer = bluetoothLifeData.hostName === 'alipay' ?
- hexStringToUint8Array(res.value) : new Uint8Array(res.value);
- // console.log("receiveBuffer", receiveBuffer);
- // 接收到的报文包含开始码等报文头数据
- if (
- receiveBuffer[0] == 0xa5 &&
- BleStruct.firstPacketReceived == false
- ) {
- // console.log("包含开始码等报文头数据的报文");
- BleStruct.firstPacketReceived = true;
- BleStruct.length = (receiveBuffer[5] << 8) + receiveBuffer[4] +
- 6; //算出数据长度
- BleStruct.received = 0;
- BleStruct.cmd = receiveBuffer[1]; //命令号赋值
- BleStruct.errCode = receiveBuffer[6];
- BleStruct.buffer = new Uint8Array(BleStruct.length);
- // 错误码!= 0,丢弃报文
- if (BleStruct.errCode != 0) {
- BleStruct.firstPacketReceived = false;
- bluetoothLifeData.result = BleStruct.errCode;
- reject({
- message: "errCode != 0",
- errCode: BleStruct.errCode
- });
- return;
- }
- }
- // 接收到的报文的长度不对,丢弃报文
- if (receiveBuffer.length > BleStruct.length - BleStruct.received) {
- BleStruct.firstPacketReceived = false;
- console.log(
- "onBLECharacteristicValueChange: length err",
- BleStruct,
- receiveBuffer.length
- );
- reject({
- message: "onBLECharacteristicValueChange: length err",
- bleStruct: BleStruct,
- length: receiveBuffer.length,
- });
- return;
- }
- BleStruct.buffer.set(receiveBuffer, BleStruct
- .received); // 将接收到的报文放入buffer容器
- BleStruct.received += receiveBuffer.length;
- // console.log("BleStruct", BleStruct);
- // 已经获取到全部报文
- if (BleStruct.received >= BleStruct.length) {
- BleStruct.firstPacketReceived = false;
- const returnCmd = analysisData(
- BleStruct.cmd,
- BleStruct.buffer.slice(7)
- );
- resolve(returnCmd);
- }
- });
- });
- }
- function analysisData(cmd, data) {
- // console.log("进入分析数据函数");
- const buffer2Str = (buffer) =>
- Array.from(buffer)
- .map((byte) => byte.toString(16).padStart(2, "0"))
- .join("");
- const buffer2Num = (buffer) => parseInt(buffer2Str(buffer), 16);
- //请求会话密钥的返回包,对其进行处理
- if (cmd == 0x01) {
- bluetoothLifeData.getSessionKeyPackageTime = +new Date();
- var originalKey = vueComponent.openDoorInfo.bluetoothKey;
- // var originalKey = "ffffffffffffffffffffffffffffffff";
- var encryptedData = data;
- var sessionKey = decryptAes(originalKey, encryptedData);
- const hexString = Array.from(sessionKey)
- .map((byte) => byte.toString(16).padStart(2, "0"))
- .join("");
- uni.setStorageSync("sessionKey", hexString);
- return 0x01;
- }
- //获取电量返回包,对其进行处理
- if (cmd == 0x04) {
- bluetoothLifeData.getPowerLogPackageTime = +new Date();
- console.log("power:", data);
- let powerString = Array.from(data)
- .map((byte) => byte.toString(16).padStart(2, "0"))
- .join("");
- powerString = parseInt(powerString, 16);
- uni.setStorageSync("power", powerString);
- return 0x04;
- }
- //蓝牙开门返回包,对其进行处理
- if (cmd == 0x11) {
- bluetoothLifeData.getOpenDoorPackageTime = +new Date();
- // console.log("蓝牙开门成功");
- // console.log(data[0]);
- // console.log(data[1]);
- return 0x11;
- }
- //开门记录返回包,对其进行处理
- if (cmd == 0x12) {
- bluetoothLifeData.getOpenDoorLogPackageTime = +new Date();
- var dataBuffer = data;
- var doorBufferLogs = [];
- while (dataBuffer.length > 0) {
- length = Math.min(17, dataBuffer.length);
- doorBufferLogs.push(dataBuffer.slice(0, length));
- // console.log(
- // "cardNum",
- // dataBuffer.slice(1, 5),
- // "roomNum",
- // dataBuffer.slice(5, 9)
- // );
- dataBuffer = dataBuffer.slice(length);
- }
- let doorLogs = doorBufferLogs.map((buffer) => ({
- type: buffer[0],
- cardNum: buffer2Num(buffer.slice(1, 5)),
- roomNum: buffer2Str(buffer.slice(5, 9)),
- time: new Date(buffer2Num(buffer.slice(9)) * 1000),
- }));
- // console.log("analysisData:", doorBufferLogs, doorLogs);
- uni.setStorageSync("doorLogs", doorLogs);
- return 0x12;
- }
- }
- // 启用蓝牙低功耗设备特征值变化时的 notify 功能,
- function notifyBLECharacteristicValueChange() {
- return new Promise((resolve, reject) => {
- // 连上蓝牙后再创建订阅
- console.log("创建订阅");
- uni.notifyBLECharacteristicValueChange({
- deviceId: bluetoothLifeData.deviceId, // 蓝牙设备 id
- serviceId: bluetoothLifeData.serviceId, // 蓝牙特征对应服务的 UUID
- characteristicId: bluetoothLifeData.characteristicId, // 蓝牙特征的 UUID
- state: true, // 是否启用 notify
- success: function(res) {
- console.log("success notifyBLECharacteristicValueChange:", res);
- resolve(res);
- },
- fail: function(res) {
- console.log("fail notifyBLECharacteristicValueChange:", res);
- resolve(res);
- },
- });
- });
- }
- function onBLEConnectionStateChange() {
- uni.onBLEConnectionStateChange((res) => {
- // console.log("onBLEConnectionStateChange", res);
- });
- }
- function stopBluetoothDevicesDiscovery() {
- uni.stopBluetoothDevicesDiscovery({
- success: (res) => {
- // console.log("关闭蓝牙停止搜索成功");
- },
- fail: (res) => {
- console.log("关闭蓝牙停止搜索失败");
- },
- });
- }
- // 向蓝牙低功耗设备特征值中写入二进制数据
- async function writeBLECharacteristicValue(packetData) {
- // 连上蓝牙后再写数据
- // console.log("进入写特征值函数,开始写入数据");
- // 向蓝牙设备发送一个0x00的16进制数据
- // let _Buffer = string2buffer(packetData)
- let count = 1;
- while (packetData.byteLength > 0) {
- let len = Math.min(16, packetData.byteLength);
- try {
- const res = await uni.writeBLECharacteristicValue({
- deviceId: bluetoothLifeData.deviceId,
- serviceId: bluetoothLifeData.serviceId,
- characteristicId: bluetoothLifeData.characteristicId,
- value: packetData.slice(0, len),
- });
- // console.log(`第${count}次写入`, res);
- count += 1;
- } catch (error) {
- console.log("writeBLECharacteristicValue err", error);
- }
- packetData = packetData.slice(len);
- await Delayed(500);
- }
- return;
- }
- // 关闭连接,也是异步操作
- function closeBLEConnection(deviceId = "") {
- return new Promise((resolve, reject) => {
- // console.log("关闭连接");
- uni.closeBLEConnection({
- deviceId: deviceId,
- success: (res) => {
- // console.log("断开蓝牙连接成功", res);
- resolve(res);
- },
- fail: (res) => {
- console.log("断开蓝牙连接失败", res);
- reject(res);
- },
- });
- });
- }
- // 关闭蓝牙模块
- function closeBluetoothAdapter() {
- return new Promise((resolve, reject) => {
- uni.closeBluetoothAdapter({
- success: function(res) {
- // console.log("success closeBluetoothAdapter:", res);
- resolve(res);
- },
- fail: function(res) {
- console.log("fail closeBluetoothAdapter:", res);
- resolve();
- },
- });
- });
- }
- },
- },
- async onLoad() {
- // console.log("currentHotelId", this.currentHotelId);
- // 先获取入住记录
- let { data: infoRes } = await getCheckinInfo(this.currentHotel.hotelId)
- // console.log("获取入住记录", infoRes);
- if (infoRes.code !== 200 || !infoRes.success || !infoRes.data) {
- console.log("获取入住记录失败", infoRes);
- return
- }
- this.checkinInfo = infoRes.data;
- this.checkinInfo.startTime = moment(this.checkinInfo.startTime).format("YYYY/MM/DD HH:mm:ss");
- this.checkinInfo.endTime = moment(this.checkinInfo.endTime).format("YYYY/MM/DD HH:mm:ss");
- // 获取门锁信息
- const { data:openDoorInfoRes } = await getOpenDoorInfo({
- hotelId: this.currentHotel.hotelId,
- floor: Number(this.checkinInfo.floor),
- room: this.checkinInfo.room
- })
- // console.log("获取入住记录和门锁记录", openDoorInfoRes);
-
- const { data: doorLockInfo } = openDoorInfoRes
- // 更新门锁设备相关信息
- this.openDoorInfo.bluetoothDeviceId = doorLockInfo.doorLockDeviceId;
- this.openDoorInfo.bluetoothName = doorLockInfo.doorLockName;
- this.openDoorInfo.androidOptimization =
- doorLockInfo.androidOptimization === false ? false : true;
- this.openDoorInfo.openDoorParam = JSON.parse(doorLockInfo.openDoorParam)
- // console.log("this.openDoorInfo.openDoorParam", this.openDoorInfo.openDoorParam);
- this.openDoorInfo.bluetoothKey = doorLockInfo.doorLockKey;
- this.allowedOpenDoor = 0;
- },
- };
|