123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864 |
- <template>
- <view>
- <view v-if="showCheckinInfo">
- <view class="info">
- <view class="title">
- <view class="hotel-name">
- <!-- <u-text :text="hotelStore.hotel.name" bold color="#333333" size="30rpx"></u-text> -->
- <text>{{hotelStore.hotel.name}}</text>
- </view>
- <view class="tips">
- <!-- <u-text text="入住信息" color="#666666" size="24rpx"></u-text> -->
- <text>入住信息</text>
- </view>
- </view>
- <view class="checkin-info">
- <view class="checkin-info--item">
- <view class="checkin-info--item--text">
- <!-- <u-text text="所在楼层: " color="#999999" size="26rpx"></u-text> -->
- <text>所在楼层:</text>
- </view>
- <view class="checkin-info--item--text">
- <!-- <u-text :text="checkinInfo.floor + '楼'" color="#999999" size="26rpx"></u-text> -->
- <text>{{`${checkinInfo.floor}楼`}}</text>
- </view>
- </view>
- <view class="checkin-info--item">
- <view class="checkin-info--item--text">
- <!-- <u-text text="房间信息 : " color="#999999" size="26rpx"></u-text> -->
- <text>房间信息:</text>
- </view>
- <view class="checkin-info--item--text">
- <!-- <u-text :text="checkinInfo.room" color="#999999" size="26rpx"></u-text> -->
- <text>{{checkinInfo.room}}</text>
- </view>
- </view>
- <view class="checkin-info--item">
- <view class="checkin-info--item--text">
- <!-- <u-text text="起始时间 : " color="#999999" size="26rpx"></u-text> -->
- <text>起始时间:</text>
- </view>
- <view class="checkin-info--item--text">
- <!-- <u-text :text="formatDateTime(checkinInfo.startTime)" color="#999999" size="26rpx"></u-text> -->
- <text>{{formatDateTime(checkinInfo.startTime)}}</text>
- </view>
- </view>
- <view class="checkin-info--item">
- <view class="checkin-info--item--text">
- <!-- <u-text text="结束时间 : " color="#999999" size="26rpx"></u-text> -->
- <text>结束时间:</text>
- </view>
- <view class="checkin-info--item--text">
- <!-- <u-text :text="formatDateTime(checkinInfo.endTime)" color="#999999" size="26rpx"></u-text> -->
- <text>{{formatDateTime(checkinInfo.endTime)}}</text>
- </view>
- </view>
- </view>
- </view>
- <view class="room__operate">
- <view v-if="doorLockStatus === 'doorLockReady'" class="room__operate--display" @click="openBoYuanLock">
- <uni-icons type="locked" size="30"></uni-icons>
- <view class="display__desc">手机开门</view>
- </view>
- <view v-if="doorLockStatus === 'doorLockUnavailable'" class="room__operate--display">
- <uni-icons type="locked" size="30"></uni-icons>
- <view class="display__desc">手机开门</view>
- </view>
- <view v-if="doorLockStatus === 'doorLockOpening'" class="room__operate--display">
- <view class="display__desc">{{openDoorTips}}</view>
- </view>
- </view>
- </view>
- <view v-else class="not-checkin">
- <view class="not-checkin-text">
- <text>您还未入住!</text>
- </view>
- <view class="not-checkin-text">
- <text>若已退房或房间到期,请至前台办理</text>
- </view>
- </view>
- </view>
- </template>
- <script setup>
- import {
- ref
- } from "vue";
- import {
- getCheckinInfo,
- getOpenDoorInfo,
- unloadBLEOpenDoorLog
- } from "../../utils/api"
- import {
- useHotelStore
- } from '@/store/hotelStore.js'
- import {
- onLoad
- } from '@dcloudio/uni-app'
- import moment from "moment";
- import {
- isIosDevice,
- ErrorCode,
- TimeoutValue,
- needScanDevice,
- parseBELDeviceId,
- genReqSecretPkg,
- genOpDoorPkg,
- hexStringToUint8Array,
- decryptAes,
- Delayed
- } from "../../utils/emergencyKeyUtils";
- const hotelStore = useHotelStore()
- const showCheckinInfo = ref(false)
- const checkinInfo = ref(null)
- const openDoorInfo = ref(null)
- const doorLockReady = "doorLockReady"
- const doorLockOpening = "doorLockOpening"
- const doorLockStatus = ref("doorLockUnavailable")
- const openDoorTips = ref("正在扫描门锁蓝牙...")
- const openBoYuanLock = async () => {
- console.log("openDoorInfo", openDoorInfo.value);
- doorLockStatus.value = doorLockOpening
- openDoorTips.value = "正在扫描门锁蓝牙..."
- const BluetoothLifeData = {
- name: null, // 蓝牙门锁名称
- hotelId: null,
- operationType: 1, // 蓝牙开门
- isBluetoothOpened: false, // 蓝牙开关是否打开
- bluetoothConnected: false, // 蓝牙是否连接上
- deviceId: null, // 进行连接时,蓝牙的deviceId。安卓可以计算出来,iOS需要扫描
- originalSecret: null,
- sessionKey: null,
- 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, // 获取到请求开门返回包的时间
- result: null, // 蓝牙操作结果,与错误码相对应
- endTime: null, // 流程中断的时间点
- mobileOs: null,
- totalTime: null,
- isLocationEnabled: false,
- androidOptimization: false,
- hostName: null, // 小程序宿主名
- hostVersion: null, // 小程序宿主版本
- openDoorParam: null, // 开门时的自定义参数
- bluetoothAuthorized: false, // 蓝牙授权开启情况
- errMessage: '',
- // 剩下的为上传相关记录的操作
- floor: null,
- room: null
- }
- BluetoothLifeData.androidOptimization = openDoorInfo.value.androidOptimization
- BluetoothLifeData.deviceId = openDoorInfo.value.doorLockDeviceId
- BluetoothLifeData.name = openDoorInfo.value.doorLockName
- BluetoothLifeData.openDoorParam = openDoorInfo.value.openDoorParam
- BluetoothLifeData.originalSecret = openDoorInfo.value.doorLockKey
- BluetoothLifeData.hotelId = hotelStore.hotel.hotelId
- BluetoothLifeData.room = checkinInfo.value.room
- BluetoothLifeData.floor = checkinInfo.value.floor
- const systemInfo = uni.getSystemInfoSync();
- //console.log("小程序宿主环境信息", systemInfo);
- BluetoothLifeData.hostName = systemInfo.hostName // 小程序的宿主名称,微信、支付宝等
- BluetoothLifeData.hostVersion = systemInfo.hostVersion; // 宿主环境的版本信息
- BluetoothLifeData.mobileOs = systemInfo.osName; // 收集操作系统
- // 后期考虑能否获取到开发环境基础库的版本,并添加进数据库
- // 处理宿主系统为ios且宿主环境为alipay时的服务id和特征值id
- if (isIosDevice(BluetoothLifeData) && BluetoothLifeData.hostName === 'alipay') {
- BluetoothLifeData.serviceId = 'FFF0'
- BluetoothLifeData.characteristicId = 'FFF1'
- }
- /**
- * 蓝牙的开关以及在未开启安卓优化时位置的开关
- */
- const systemSetting = uni.getSystemSetting()
- //console.log("获取系统设置信息", systemSetting);
- BluetoothLifeData.isBluetoothOpened = systemSetting.bluetoothEnabled
- // 处理手机蓝牙未打开
- if (!BluetoothLifeData.isBluetoothOpened) {
- return ErrorCode.get(10001)
- }
- // 不开启安卓优化,设备系统是安卓,校验位置是否开启
- BluetoothLifeData.isLocationEnabled = systemSetting.locationEnabled
- //console.log("BluetoothLifeData", BluetoothLifeData);
- /**
- * 获取用户的当前设置(蓝牙的授权信息)。返回值中只会出现小程序已经向用户请求过的权限。
- */
- const userSetting = await uni.getSetting()
- //console.log("获取用户系统设置信息", userSetting);
- if (BluetoothLifeData.hostName === "alipay") {
- BluetoothLifeData.bluetoothAuthorized = userSetting.authSetting['bluetooth'] ?
- userSetting.authSetting['bluetooth'] : false
- } else if (BluetoothLifeData.hostName === "WeChat") {
- BluetoothLifeData.bluetoothAuthorized = userSetting.authSetting['scope.bluetooth'] ?
- userSetting.authSetting['scope.bluetooth'] : false
- }
- //console.log("BluetoothLifeData.bluetoothAuthorized", BluetoothLifeData.bluetoothAuthorized);
- /**
- *
- * 开启蓝牙适配器,必须操作,并在Promise中处理授权相关的信息
- */
- BluetoothLifeData.initBluetoothAdapterTime = +new Date();
- console.info("开始打开蓝牙适配器")
- let openBluetoothAdapter;
- try {
- // 请求授权时:同意:支付宝{isSupportBLE: true}
- // 请求授权时:拒绝:new Error()
- openBluetoothAdapter = await uni.openBluetoothAdapter()
- // console.log("openBluetoothAdapter", openBluetoothAdapter);
- if (BluetoothLifeData.hostName === "alipay") {
- BluetoothLifeData.bluetoothAuthorized = openBluetoothAdapter.isSupportBLE
- } else {
- BluetoothLifeData.bluetoothAuthorized = true
- }
- } catch (err) {
- BluetoothLifeData.bluetoothAuthorized = false
- //console.log("打开蓝牙适配器错误", err);
- if ((BluetoothLifeData.hostName === "alipay" && err.error === 2001) ||
- (BluetoothLifeData.hostName === "WeChat" && err.errno === 103)) {
- // 用户拒绝蓝牙授权,引导用户进行蓝牙授权
- await new Promise(async (resolve, reject) => {
- uni.showModal({
- title: "温馨提示",
- content: "蓝牙开门功能需要使用您的蓝牙,授权后才能开门。",
- showCancel: false,
- success: async (res) => {
- if (res.confirm) {
- await uni.openSetting().then(async (res) => {
- //console.log("openSetting", res);
- if (BluetoothLifeData.hostName ===
- "alipay") {
- BluetoothLifeData.bluetoothAuthorized =
- res.authSetting['bluetooth']
- } else if (BluetoothLifeData.hostName ===
- "WeChat") {
- BluetoothLifeData.bluetoothAuthorized =
- res.authSetting['scope.bluetooth']
- }
- try {
- await uni.openBluetoothAdapter()
- } catch (error) {
- //console.log(error);
- }
- resolve()
- })
- }
- },
- });
- })
- }
- }
- //console.log("1------------------>开启蓝牙适配器", openBluetoothAdapter);
- // 判断蓝牙是否授权,未授权直接return
- if (!BluetoothLifeData.bluetoothAuthorized) {
- BluetoothLifeData.endTime = new Date();
- BluetoothLifeData.totalTime =
- BluetoothLifeData.endTime - BluetoothLifeData.initBluetoothAdapterTime;
- // 蓝牙操作未授权
- BluetoothLifeData.result = 10002;
- const uploadRes = await unloadBLEOpenDoorLog(BluetoothLifeData);
- //console.log("上传开门记录结果", uploadRes);
- await closeBluetoothAdapter();
- doorLockStatus.value = doorLockReady
- return ErrorCode.get(10002);
- }
- /**
- *
- * 在Promise中调用onBluetoothDeviceFound,直到找到目的设备
- * 针对android设备且开启了安卓设备优化,直接跳过此流程
- */
- if (needScanDevice(BluetoothLifeData)) {
- console.info("发现目标蓝牙门锁")
- if (BluetoothLifeData.mobileOs === "android" && !BluetoothLifeData.isLocationEnabled) {
- BluetoothLifeData.result = 10008;
- BluetoothLifeData.endTime = new Date();
- BluetoothLifeData.totalTime =
- BluetoothLifeData.endTime - BluetoothLifeData.initBluetoothAdapterTime;
- await closeBluetoothAdapter();
- const uploadRes = await unloadBLEOpenDoorLog(BluetoothLifeData);
- doorLockStatus.value = doorLockReady
- //console.log("上传开门记录结果", uploadRes);
- return ErrorCode.get(10008);
- }
- await new Promise(async (resolve, reject) => {
- //console.log("1.1------------------>发现周边蓝牙设备");
- // 注册超时函数
- const timeout = setTimeout(() => {
- if (!BluetoothLifeData.targetBluetoothFoundTime) {
- //console.log("onBluetoothDeviceFound Promise 发现设备超时");
- BluetoothLifeData.result = 10003;
- uni.stopBluetoothDevicesDiscovery();
- reject("发现设备超时");
- }
- }, TimeoutValue.findDevice);
- // 注册发现蓝牙事件
- uni.onBluetoothDeviceFound((res) => {
- let devices = res.devices.filter((device) =>
- device.name === BluetoothLifeData.name
- );
- //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();
- uni.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("超时时间内未发现门锁");
- BluetoothLifeData.endTime = new Date();
- BluetoothLifeData.totalTime =
- BluetoothLifeData.endTime - BluetoothLifeData.initBluetoothAdapterTime;
- await closeBluetoothAdapter();
- const uploadRes = await unloadBLEOpenDoorLog(BluetoothLifeData);
- doorLockStatus.value = doorLockReady
- //console.log("上传开门记录结果", uploadRes);
- return ErrorCode.get(10003)
- }
- } else {
- //该分支不进行蓝牙扫描
- BluetoothLifeData.startBluetoothDiscoveryTime = +new Date();
- BluetoothLifeData.targetBluetoothFoundTime = +new Date();
- // 根据转换规则,置android设备的deviceId
- BluetoothLifeData.deviceId = parseBELDeviceId(
- BluetoothLifeData.deviceId
- );
- //console.log("android设备连接蓝牙时的deviceId", BluetoothLifeData.deviceId);
- }
- /**
- *
- * 找到目标设备,开始进行蓝牙连接
- * 在Promise中连接蓝牙设备,超时就 关闭蓝牙适配器 ,第一次超时return是否会影响第二次的成功连接
- */
- console.info("开始连接目标蓝牙门锁")
- openDoorTips.value = "正在连接门锁蓝牙..."
- await new Promise(async (resolve, reject) => {
- //console.log("2------------------>进行蓝牙连接");
- // 注册超时函数
- const timeout = setTimeout(() => {
- if (!BluetoothLifeData.targetBluetoothConnectedTime) {
- BluetoothLifeData.result = 10004;
- //console.log("蓝牙连接超时");
- reject("蓝牙连接超时");
- }
- }, TimeoutValue.connectBLE)
- // 微信和支付宝连接蓝牙的函数不一样
- // 发起蓝牙连接
- uni.createBLEConnection({
- deviceId: BluetoothLifeData.deviceId,
- success: (res) => {
- clearTimeout(timeout)
- BluetoothLifeData.bluetoothConnected = true;
- BluetoothLifeData.targetBluetoothConnectedTime = +new Date();
- //console.log("蓝牙连接成功", res);
- resolve("蓝牙连接成功")
- },
- fail: (err) => {
- BluetoothLifeData.result = 10005;
- //console.log("蓝牙连接失败", err);
- }
- })
- }).catch(err => {
- //console.log("createBLEConnection Promise catch err", err);
- })
- // 判断蓝牙是否连接成功,若失败则直接return
- if (!BluetoothLifeData.bluetoothConnected) {
- BluetoothLifeData.endTime = new Date();
- BluetoothLifeData.totalTime =
- BluetoothLifeData.endTime - BluetoothLifeData.initBluetoothAdapterTime;
- await closeBluetoothAdapter();
- const uploadRes = await unloadBLEOpenDoorLog(BluetoothLifeData);
- //console.log("上传开门记录结果", uploadRes);
- doorLockStatus.value = doorLockReady
- if (BluetoothLifeData.result === 10004) {
- return ErrorCode.get(10004)
- } else {
- return ErrorCode.get(10005)
- }
- }
- /**
- *
- * 蓝牙连接成功后,对iOS设备而言,需对服务及特征值进行扫描
- */
- if (isIosDevice(BluetoothLifeData)) {
- await new Promise((reslove, reject) => {
- uni.getBLEDeviceServices({
- deviceId: BluetoothLifeData.deviceId,
- success: res => {
- //console.log("获取蓝牙设备所有服务列表成功", res);
- reslove(res)
- },
- fail: err => {
- //console.log("获取蓝牙设备所有服务列表失败", err);
- reject(err)
- }
- })
- }).catch(err => {
- //console.log("获取蓝牙设备所有服务列表Promise err", err);
- })
- await new Promise((reslove, reject) => {
- uni.getBLEDeviceCharacteristics({
- deviceId: BluetoothLifeData.deviceId,
- serviceId: BluetoothLifeData.serviceId,
- success: res => {
- //console.log("获取低功耗蓝牙设备服务中所有特征成功", res);
- reslove(res)
- },
- fail: err => {
- //console.log("获取低功耗蓝牙设备服务中所有特征失败", err);
- reject(err)
- }
- })
- }).catch(err => {
- //console.log("获取低功耗蓝牙设备服务中所有特征Promise err", err);
- })
- BluetoothLifeData.opForIosEndedTime = +new Date();
- } else {
- BluetoothLifeData.opForIosEndedTime = +new Date();
- }
- // 对主服务特征值进行订阅
- await new Promise((resolve, reject) => {
- uni.notifyBLECharacteristicValueChange({
- deviceId: BluetoothLifeData.deviceId, // 蓝牙设备 id
- serviceId: BluetoothLifeData.serviceId, // 蓝牙特征对应服务的 UUID
- characteristicId: BluetoothLifeData.characteristicId, // 蓝牙特征的 UUID
- state: true, // 是否启用 notify
- success: function(res) {
- //console.log("对主服务特征值订阅成功", res);
- resolve(res);
- },
- fail: function(res) {
- //console.log("对主服务特征值订阅失败", res);
- reject(res);
- },
- });
- }).catch(err => {
- //console.log("对主服务特征值订阅Promise err", err);
- })
- // 向蓝牙发请求,请求会话秘钥(写特征值),解出会话秘钥
- await new Promise(async (resolve, reject) => {
- //console.log("3------------------>请求会话秘钥");
- // 注册请求蓝牙会话秘钥超时函数
- const timeout = setTimeout(() => {
- if (!BluetoothLifeData.getSessionKeyPackageTime) {
- BluetoothLifeData.result = 10006;
- reject("请求会话秘钥超时");
- }
- }, TimeoutValue.requestSessionKey);
- uni.offBLECharacteristicValueChange();
- const submitSessionKeyReq = onBLECharacteristicValueChange();
- BluetoothLifeData.requestSessionKeyTime = +new Date();
- await writeBLECharacteristicValue(genReqSecretPkg());
- //console.log("发完请求会话密钥请求报文");
- submitSessionKeyReq.then((res) => {
- //console.log("请求会话密钥promise", res);
- if (res === 0x01) {
- clearTimeout(timeout);
- resolve({
- requestSessionKey: true
- });
- } else {
- clearTimeout(timeout);
- reject({
- message: "应答包获取错误,应为请求会话秘钥的应答包。",
- });
- }
- }).catch(err => {
- //console.log("处理会话密钥出现问题", err);
- })
- }).catch((err) => {
- //console.log("监听函数,请求会话秘钥。catch err", err);
- });
- // 获取会话秘钥超时的处理逻辑
- if (!BluetoothLifeData.getSessionKeyPackageTime) {
- BluetoothLifeData.endTime = new Date();
- BluetoothLifeData.totalTime =
- BluetoothLifeData.endTime - BluetoothLifeData.initBluetoothAdapterTime;
- const uploadRes = await unloadBLEOpenDoorLog(BluetoothLifeData);
- //console.log("上传开门记录结果", uploadRes);
- await closeBLEConnection(BluetoothLifeData.deviceId);
- await closeBluetoothAdapter();
- doorLockStatus.value = doorLockReady
- return ErrorCode.get(10006)
- }
- /**
- *
- * 解完请求会话秘钥后,发蓝牙开门报文,解蓝牙开门返回包
- */
- openDoorTips.value = "正在开启门锁..."
- console.info("开启目标门锁中...")
- await new Promise(async (resolve, reject) => {
- //console.log("4------------------>发蓝牙开门报文");
- // 设置蓝牙开门的超时
- const timeout = setTimeout(() => {
- if (!BluetoothLifeData.getOpenDoorPackageTime) {
- BluetoothLifeData.result = 10007;
- reject("蓝牙开门超时");
- }
- }, TimeoutValue.openDoor);
- uni.offBLECharacteristicValueChange();
- const requestBLEOpenDoorRes = onBLECharacteristicValueChange();
- BluetoothLifeData.requestBleOpenDoorTime = +new Date();
- await writeBLECharacteristicValue(genOpDoorPkg(BluetoothLifeData.sessionKey));
- //console.log("发完蓝牙开门报文");
- requestBLEOpenDoorRes.then((res) => {
- if (res == 0x11) {
- clearTimeout(timeout);
- resolve({
- bluetoothOpenDoor: true
- });
- } else {
- clearTimeout(timeout);
- reject({
- message: "应答包获取错误,应为请求会话秘钥的应答包。",
- });
- }
- });
- }).catch((err) => {
- //console.log("监听函数,蓝牙开门。catch err", err);
- });
- // 判断蓝牙开门是否成功,如果失败,则return
- if (!BluetoothLifeData.getOpenDoorPackageTime) {
- //console.log("蓝牙开门失败");
- BluetoothLifeData.endTime = new Date();
- BluetoothLifeData.totalTime =
- BluetoothLifeData.endTime - BluetoothLifeData.initBluetoothAdapterTime;
- const uploadRes = await unloadBLEOpenDoorLog(BluetoothLifeData);
- //console.log("上传开门记录结果", uploadRes);
- await closeBLEConnection(BluetoothLifeData.deviceId);
- await closeBluetoothAdapter();
- doorLockStatus.value = doorLockReady
- return ErrorCode.get(10007)
- }
- // 断开蓝牙连接,关闭蓝牙适配器,并上传所有记录
- await closeBLEConnection(BluetoothLifeData.deviceId);
- await closeBluetoothAdapter();
- BluetoothLifeData.endTime = new Date();
- BluetoothLifeData.totalTime =
- BluetoothLifeData.endTime - BluetoothLifeData.initBluetoothAdapterTime;
- BluetoothLifeData.result = 10000
- const uploadRes = await unloadBLEOpenDoorLog(BluetoothLifeData);
- //console.log("上传开门记录结果", uploadRes);
- //console.log(BluetoothLifeData);
- doorLockStatus.value = doorLockReady
- return ErrorCode.get(10000)
- function onBLECharacteristicValueChange() {
- return new Promise((resolve, reject) => {
- const BleStruct = {
- firstPacketReceived: false, // 是否已接收第一个报文
- buffer: null, // 用于缓存数据流的 ArrayBuffer
- cmd: null, //返回数据包命令号
- length: 0, //数据包长度
- received: 0,
- errCode: 0,
- };
- uni.onBLECharacteristicValueChange(async (res) => {
- //console.log("onBLECharacteristicValueChange", res); //打印特征值
- let receiveBuffer = BluetoothLifeData.hostName === 'alipay' ?
- hexStringToUint8Array(res.value) : new Uint8Array(res.value);
- // 接收到的报文包含开始码等报文头数据
- if (receiveBuffer[0] === 0xa5 && BleStruct.firstPacketReceived === false) {
- 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
- });
- }
- }
- // 接收到的报文的长度不对,丢弃报文
- if (receiveBuffer.length > BleStruct.length - BleStruct.received) {
- BleStruct.firstPacketReceived = false;
- reject({
- message: "onBLECharacteristicValueChange: length err",
- bleStruct: BleStruct,
- length: receiveBuffer.length,
- });
- return;
- }
- BleStruct.buffer.set(receiveBuffer, BleStruct
- .received); // 将接收到的报文放入buffer容器
- BleStruct.received += receiveBuffer.length;
- // 已经获取到全部报文
- if (BleStruct.received >= BleStruct.length) {
- BleStruct.firstPacketReceived = false;
- const returnCmd = analysisData(
- BleStruct.cmd,
- BleStruct.buffer.slice(7)
- );
- resolve(returnCmd);
- }
- });
- });
- }
- function analysisData(cmd, data) {
- // 请求会话密钥的返回包,对其进行处理
- if (cmd === 0x01) {
- BluetoothLifeData.getSessionKeyPackageTime = +new Date();
- const originalKey = BluetoothLifeData.originalSecret;
- const encryptedData = data;
- const sessionKey = decryptAes(originalKey, encryptedData);
- const hexString = Array.from(sessionKey)
- .map((byte) => byte.toString(16).padStart(2, "0"))
- .join("");
- BluetoothLifeData.sessionKey = hexString;
- return 0x01;
- }
- //蓝牙开门返回包,对其进行处理
- if (cmd == 0x11) {
- BluetoothLifeData.getOpenDoorPackageTime = +new Date();
- return 0x11;
- }
- }
- // 向蓝牙低功耗设备特征值中写入二进制数据
- async function writeBLECharacteristicValue(packetData) {
- while (packetData.byteLength > 0) {
- let len = Math.min(16, packetData.byteLength);
- try {
- await uni.writeBLECharacteristicValue({
- deviceId: BluetoothLifeData.deviceId,
- serviceId: BluetoothLifeData.serviceId,
- characteristicId: BluetoothLifeData.characteristicId,
- value: packetData.slice(0, len),
- });
- } catch (error) {
- //console.log("writeBLECharacteristicValue err", error);
- }
- packetData = packetData.slice(len);
- // 写完第一个报文后等待半秒
- await Delayed(500);
- }
- }
- // 关闭连接,也是异步操作
- function closeBLEConnection(deviceId = "") {
- return new Promise((resolve, reject) => {
- 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();
- },
- });
- });
- }
- }
- onLoad(async (options) => {
- console.log("onLoad", options);
- try {
- const infoRes = await getCheckinInfo(hotelStore.hotel.hotelId)
- // console.log("获取入住记录结果", infoRes);
- if (!infoRes.data || !infoRes.success) {
- return
- }
- checkinInfo.value = infoRes.data
- showCheckinInfo.value = true
- } catch (err) {
- console.log("获取入住记录异常", err);
- return
- }
- try {
- console.log("checkinInfo", checkinInfo.value);
- const res = await getOpenDoorInfo({
- hotelId: hotelStore.hotel.hotelId,
- floor: checkinInfo.value.floor,
- room: checkinInfo.value.room
- })
- console.log("获取门锁信息结果", res);
- if (!res.data || !res.success) {
- return
- }
- openDoorInfo.value = res.data
- doorLockStatus.value = doorLockReady
- } catch (err) {
- console.log("获取门锁信息异常", err);
- return
- }
- })
- const formatDateTime = (dateTime) => {
- return moment(dateTime).format("YYYY年MM月DD日HH时mm分")
- }
- </script>
- <style lang="scss">
- page {
- background-color: #FFFFFF;
- .info {
- background: #F5F8FE;
- box-shadow: -6px 17px 21px 0px rgba(112, 152, 246, 0.14);
- margin-bottom: 200rpx;
- .title {
- display: flex;
- justify-content: space-between;
- padding: 0 40rpx;
- line-height: 80rpx;
- border-bottom: 2rpx solid #E0E8FB;
- }
- .checkin-info {
- margin: 20rpx auto;
- .checkin-info--item {
- display: flex;
- margin: 0 40rpx;
- .checkin-info--item--text {
- margin: 10rpx;
- margin-left: 0;
- }
- &:last-child {
- padding-bottom: 20rpx;
- }
- }
- }
- }
- .room__operate {
- height: 90rpx;
- display: flex;
- margin: 2*5rpx 2 * 15rpx;
- background-color: #9e97c3;
- align-items: center;
- justify-content: center;
- border-radius: 10rpx;
- .room__operate--display {
- height: 2 * 25rpx;
- margin: 5rpx;
- padding: 2 * 6rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- .display__desc {
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- padding-left: 10rpx;
- font-size: 40rpx;
- color: #FFFFFF;
- font-size: 32rpx;
- }
- }
- }
- }
- .not-checkin {
- margin-top: 40vh;
- .not-checkin-text {
- display: flex;
- justify-content: center;
- }
- }
- </style>
|