index.vue 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. <template>
  2. <view>
  3. <view style="height: 50px;text-align: left;padding-left: 20px;">
  4. <text>
  5. 1.播放唤醒词:<span style="color:#F00; font-weight:bold;font-size: 15px; ">实时心率,播放,停止播报</span>。
  6. 2.心率区间 <span style="color:#F00; font-weight:bold;font-size: 20px; ">{{rateMin}} </span>-<span
  7. style="color:#F00; font-weight:bold;font-size: 20px;">{{rateMax}}</span>,超出区间每隔3秒钟语音播报。
  8. </text>
  9. </view>
  10. <!-- @click="onRateChange('min',-1)" -->
  11. <view style="display: flex;flex-direction: row;width: 260px;margin-left: 20px;">
  12. <button style="margin-top: 2px;" @tap="showModal" data-target="showPickerModal" data-type="min">
  13. 设置最小值
  14. </button>
  15. <button style="margin-top: 2px;" @tap="showModal" data-target="showPickerModal" data-type="max">
  16. 设置最大值
  17. </button>
  18. </view>
  19. <view style="align-items: center;display: flex;margin: 10px 20px;">
  20. <view>3.播报状态,关闭后需要手动开启播报:</view>
  21. <switch :checked="treatmentStatu.checked" @change="switchChange"></switch>
  22. </view>
  23. <!-- <view>
  24. <view class="title">Cocos预览地址:{{LocationGameUrl}}</view>
  25. <input class="uni-input" :value="LocationGameUrl" @input="onKeyInput" placeholder="输入cocos预览地址" />
  26. </view> -->
  27. <scroll-view scroll-y="true" style="height: 200px;background-color: #c6c6c6;">
  28. <view class="card-view padding-top padding-bottom" v-for="(item,index) in devicesList" :key="index"
  29. :id="'task_'+index" :class="item.bRatio?'hardware-border':''" :style="{'z-index': threeZIndex}">
  30. <view class="flex justify-between align-center">
  31. <view class="flex justify-start align-center">
  32. <image style="margin-left: 20rpx; width: 200rpx;height: 120rpx;" :src="item.icon"
  33. mode="aspectFit">
  34. </image>
  35. <view style="width: 350rpx;margin-left: 10rpx;">
  36. <view style="margin: 20rpx 0rpx; font-weight: bold; font-size: 18px; color: #565656;">
  37. {{item.cname}}
  38. </view>
  39. <view style="font-size: 12px; white-space:pre-wrap;" class="make-text-bPurple">
  40. {{item.describe}}{{item.bOldDevice?'(旧手柄)':''}}
  41. </view>
  42. </view>
  43. </view>
  44. <image style="margin-right: 60rpx; width: 60rpx;height: 60rpx;"
  45. :src="item.bRatio?'/static/devicesOther/radio-b.png':'/static/devicesOther/radio-g.png'"
  46. mode="aspectFit" @tap="_onRadio(item,$event)"></image>
  47. </view>
  48. </view>
  49. </scroll-view>
  50. <view v-if="currentItem" style="display: flex;flex-direction: row;">
  51. <!-- <button style="margin: 10rpx;" type="primary" @click="B_OnSetMTU">MTU</button>
  52. <button style="margin: 10rpx;" type="primary" @click="writeBLEValue('V')">版本</button>
  53. <button style="margin: 10rpx;" type="primary" @click="writeBLEValue('M')">mac</button> -->
  54. </view>
  55. <view v-if="currentItem" style="display: flex;flex-direction: row;">
  56. <!-- <button style="margin: 10rpx;" type="primary" @click="writeBLEValue('H')">开启高速</button> -->
  57. <!-- <button style="margin: 10rpx;" type="primary" @click="writeBLEValue('h')">关闭高速</button> -->
  58. <!-- <button style="margin: 10rpx;" type="primary" @click="writeBLEValue('3')">开启</button>
  59. <button style="margin: 10rpx;" type="primary" @click="writeBLEValue('4')">关闭</button> -->
  60. </view>
  61. <!-- <button style="margin-top: 2px;" type="warn" :disabled="!currentItem" @click="onShowGame">
  62. 显示游戏
  63. </button> -->
  64. <!-- <button type="warn" :disabled="extraLine.length<=0" @click="remove">
  65. 清除输入框数据
  66. </button> -->
  67. <!-- <button @click="initTextToSpeech()">initTextToSpeech</button> -->
  68. <!-- <button @click="playSpeak('当前心率123')">playSpeak</button> -->
  69. <!-- <button @click="stopSpeak()">stopSpeak</button> -->
  70. <!-- <button @touchstart="recordSpeakDown()" @touchend="recordSpeakUp()">recordSpeak</button> -->
  71. <!-- <button @click="recordSpeakDown()">recordSpeakDown</button> -->
  72. <!-- <button @click="recordSpeakUp()">recordSpeakUp</button> -->
  73. <!-- <button @click="releaseToSpeech()">releaseToSpeech</button> -->
  74. <!-- <button style="margin-top: 2px;" type="warn" @click="onEmit">
  75. 模拟发送
  76. </button> -->
  77. <!-- <button style="margin-top: 2px;" type="warn" @click="onClise">
  78. close
  79. </button> -->
  80. <view style="text-align: center;">
  81. <!-- :class="?'active':''" :style="[{ width:loading?'60%':''}]" -->
  82. <view>稳定心率进度:</view>
  83. <view class="cu-progress round sm striped active">
  84. <view class="bg-green" :style="[{ width:loading}]"></view>
  85. </view>
  86. <!-- -->
  87. <text v-if="heartRate === 0 || !bTimerPlay" style="font-size: 80px;">--</text>
  88. <block v-else>
  89. <text style="font-size: 80px;">{{heartRate}}</text>
  90. <text style="font-size: 20px;">bpm</text>
  91. </block>
  92. </view>
  93. <view style="text-align: center;margin: 10px 0px;">
  94. <text style="font-size: 40px;color: blue;">{{nums}}</text>
  95. </view>
  96. <view style="display: flex;flex-direction: row;">
  97. <button style="margin-top: 2px;" type="warn" @click="startTimer()">
  98. 开始计时
  99. </button>
  100. <button style="margin-top: 2px;" type="warn" @click="pauseTime()">
  101. 暂停计时
  102. </button>
  103. <button style="margin-top: 2px;" type="warn" @click="closeTimer()">
  104. 清零计时
  105. </button>
  106. <!-- <button @click="onGetRecordAudioPermission()">test</button> -->
  107. </view>
  108. <!-- <scroll-view class="text-box" scroll-y="true" >
  109. <scroll-view scroll-x="true">
  110. <text selectable="true" style="font-size: 20px;">{{text}} \n {{nums}}</text>
  111. </scroll-view>
  112. </scroll-view> -->
  113. <!-- <view>
  114. <button @click="startLuyin" class="recordingStyle">按住开始说话</button>
  115. <view>识别的结果 : {{ searchText }}</view>
  116. </view> -->
  117. <!-- <view class="content">
  118. </view> -->
  119. <view class="cu-modal bottom-modal" :class="modalName == 'showPickerModal' ? 'show' : ''"
  120. @touchmove.stop.prevent="moveHandle">
  121. <view class="cu-dialog" style="border-top-right-radius: 20rpx; border-top-left-radius: 20rpx;">
  122. <myPicker v-if="modalName == 'showPickerModal' ? true:false" :pickerObj="pickerObj"
  123. @confirmEvent="onConfirm" @cancelEvent="onCancel"></myPicker>
  124. </view>
  125. </view>
  126. </view>
  127. </template>
  128. <script>
  129. import config from '@/common/config.js';
  130. import reqUtil from '@/util/util-js/requstUtil.js';
  131. import ble from '@/util/util-js/BLE.js';
  132. import myPicker from '@/components/slambb-picker/slambb-picker.vue';
  133. import pickerData from '@/components/slambb-picker/picker.js';
  134. import {
  135. mapState,
  136. mapMutations
  137. } from 'vuex';
  138. // 获取 module
  139. var testModule = uni.requireNativePlugin("TestModule")
  140. export default {
  141. computed: mapState(['bOpenBluetooth', 'bOpenSuccess', 'bListenAdapterStateChange', 'bListenDeviceFound',
  142. 'BLEConnectDevice', 'BLEGetServices', 'cIndex', 'bConnection', 'bVerifiedConnection', 'BLEInfoList',
  143. 'BLEDeviceShowList', 'finallyUseDevice', 'systemInfo', 'guideUnlockState', 'currentModeIndex',
  144. "bListenerAccArray", 'LocationGameUrl', 'bGamePlaying'
  145. ]),
  146. components: {
  147. myPicker
  148. },
  149. data() {
  150. return {
  151. // 设备列表
  152. devicesList: [],
  153. currentItem: null,
  154. //设置一个旧的连接item
  155. oldItem: null,
  156. searchObj: null,
  157. //设置搜索超时时间
  158. searchTimeOut: null,
  159. option: null,
  160. saveObj: null,
  161. //搜索提示定时器
  162. searchMac: null,
  163. //
  164. getServicesTimeout: null,
  165. writeMacTimeout: null,
  166. //限制关闭连接
  167. bLimitClose: false,
  168. //是否需要检测发起的连接蓝牙是否匹配上
  169. bTestBondConnect: true,
  170. //是否显示
  171. bShow: true,
  172. //切换连接
  173. bSwitch: false,
  174. //提示
  175. circular: false,
  176. currentHeight: 0,
  177. threeTipHeight: 0,
  178. threeZIndex: 0,
  179. currentIndex: 5,
  180. /**
  181. * 模块
  182. */
  183. modalName: null,
  184. pikerType: '',
  185. pickerObj: {
  186. pickerType: 'dateItem',
  187. pickerTitle: '记时间'
  188. },
  189. //测试
  190. text: '',
  191. extraLine: [],
  192. bShowGame: false,
  193. bPlayText: false,
  194. playTime: 0,
  195. /**
  196. * 计时器部分
  197. */
  198. timer: null,
  199. nums: '0时0分0秒',
  200. bTimerPlay: false,
  201. bTimerPause: false,
  202. hour: 0,
  203. minute: 0,
  204. second: 0,
  205. millisecond: 0,
  206. loading: '0%',
  207. loadingCount: 0,
  208. bLoading: false,
  209. /**
  210. * 录音
  211. */
  212. speechEngine: 'baidu',
  213. searchText: '',
  214. /**
  215. * 当前心率值
  216. */
  217. // rateText: "0",
  218. heartRate: 0,
  219. bReach: false,
  220. rateMax: 140,
  221. rateMin: 120,
  222. /**
  223. * switch
  224. */
  225. treatmentStatu: {
  226. checked: false
  227. },
  228. //检测录音权限
  229. getRecordAudioPermissionInterval: null
  230. }
  231. },
  232. onLoad(op) {
  233. this.add("初始化");
  234. uni.$on('retryConnectBLESuccess', this.onRetryConnectBLESuccess);
  235. uni.$on('callbackCloseBLE', this.hardCallbackCloseBLE);
  236. uni.$on('listenerBLE', this.onListenerBLE);
  237. this.BLEInfoList.forEach((item, index, selfarr) => {
  238. if (
  239. item.usageMode == 'CL809' ||
  240. item.usageMode == 'CL806' ||
  241. item.usageMode == 'CL831' ||
  242. item.usageMode == 'CL838') {
  243. let item = Object.assign({}, selfarr[index], {
  244. bOldDevice: false
  245. });
  246. this.devicesList.push(item);
  247. }
  248. })
  249. //subview
  250. /**
  251. * $on 之后要调用 $off,不然会重复绑定
  252. */
  253. this.onGetLocationGameUrl();
  254. uni.$on("log", this.add)
  255. },
  256. onUnload() {
  257. uni.$off('retryConnectBLESuccess', this.onRetryConnectBLESuccess);
  258. uni.$off('callbackCloseBLE', this.hardCallbackCloseBLE);
  259. uni.$off('listenerBLE', this.onListenerBLE);
  260. //清除定时器
  261. this.onClearTimeout();
  262. uni.$off("log", this.add);
  263. //停止语音
  264. this.stopSpeak();
  265. //释放唤醒资源
  266. this.releaseToSpeech();
  267. },
  268. onShow() {
  269. this.bShow = true;
  270. uni.$on('updateBLEDeviceData', this.callbackUpdateBLEData);
  271. },
  272. onReady() {
  273. // #ifdef APP-PLUS
  274. //初始化语音
  275. this.initTextToSpeech();
  276. this.$set(this.treatmentStatu, 'checked', true);
  277. //如果没有开启麦克风。可能导致唤醒失败
  278. //轮询检测麦克风权限
  279. if (this.getRecordAudioPermissionInterval) {
  280. clearInterval(this.getRecordAudioPermissionInterval);
  281. this.getRecordAudioPermissionInterval = null;
  282. }
  283. //开始检测
  284. this.getRecordAudioPermissionInterval = setInterval(() => {
  285. var ret = testModule.getRecordAudioPermission();
  286. // console.log(ret);
  287. if (ret.bPermission) {
  288. if (this.getRecordAudioPermissionInterval) {
  289. clearInterval(this.getRecordAudioPermissionInterval);
  290. this.getRecordAudioPermissionInterval = null;
  291. }
  292. this.initWakeup();
  293. }
  294. }, 1000)
  295. // this.initWakeup();
  296. // #endif
  297. },
  298. onHide() {
  299. //如果蓝牙弹出匹配框,会触发onHide。这时候处理蓝牙连接流程检测应等onShow 时候,再检测
  300. this.bShow = false;
  301. uni.$off('updateBLEDeviceData', this.callbackUpdateBLEData);
  302. },
  303. methods: {
  304. ...mapMutations(['initAdapter', 'onCreateBLESuccess', 'onGetBLEDeviceServices', 'onOnlyCloseBLEConnection',
  305. 'onGetRSSITransDistance',
  306. 'addBLEDevice', 'onWriteBLEConnectionValue', 'deleteBLEDevice', 'B_GetBondedDevices',
  307. 'B_OpenBLESetting', 'setGuideUnlockState', 'switchLevelList', 'onSetLocationGameUrl',
  308. 'onGetLocationGameUrl', 'onTestEmit', 'B_CloseBLEConnection', 'B_OnSetMTU'
  309. ]),
  310. onEmit() {
  311. this.onTestEmit();
  312. },
  313. /**
  314. * 显示游戏
  315. */
  316. onShowGame() {
  317. // uni.$emit("showGame");
  318. // this.bShowGame = true;
  319. uni.navigateTo({
  320. url: "../game/game"
  321. })
  322. },
  323. //设备回调事件
  324. callbackUpdateBLEData(data) {
  325. //如果在监听状态时候隐藏页面,返回
  326. // console.log(data)
  327. function getIntValue(formatType, offset) {
  328. console.log("getIntValue:" + formatType + " = " + offset);
  329. let array = new Uint8Array(data.ab);
  330. console.log(array);
  331. console.log(array[offset]);
  332. switch (formatType) {
  333. case 17:
  334. return ble.unsignedByteToInt(array[offset]);
  335. case 18:
  336. return ble.unsignedBytesToInt(array[offset], array[offset + 1]);
  337. }
  338. }
  339. // console.log(res, res.serviceId.toLocaleLowerCase());
  340. let offset = 0;
  341. let flags = getIntValue(17, offset);
  342. console.log("flags:", flags);
  343. let hearRateType = (flags & 1) == 0 ? 17 : 18;
  344. console.log("hearRateType:" + hearRateType);
  345. offset = offset + 1;
  346. this.heartRate = getIntValue(hearRateType, offset);
  347. console.log("heartRate:" + this.heartRate);
  348. this.text = JSON.stringify(data.hex) + "\n" + "心率:" + this.heartRate + "bpm" + "\n";
  349. // this.rateText = this.heartRate;
  350. if (this.heartRate != 0) {
  351. if (!this.bLoading) {
  352. this.bLoading = true;
  353. this.loadingCount = 0;
  354. }
  355. if (!this.bReach) {
  356. if (this.heartRate >= this.rateMin && this.heartRate <= this.rateMax) {
  357. this.playSpeak("达到运动心率" + this.heartRate);
  358. console.log("达到运动心率");
  359. //达到一次目标后播报
  360. this.bReach = true;
  361. }
  362. } else {
  363. if (this.heartRate < this.rateMin || this.heartRate > this.rateMax) {
  364. this.playSpeak("当前心率" + this.heartRate);
  365. }
  366. }
  367. } else if (this.bTimerPlay) {
  368. this.bLoading = false;
  369. //暂停心率
  370. this.pauseTime();
  371. }
  372. },
  373. onClearTimeout() {
  374. // 退出后,清除计时器
  375. if (this.searchMac) {
  376. clearTimeout(this.searchMac);
  377. this.searchMac = null;
  378. }
  379. //servicesTimeout
  380. if (this.getServicesTimeout) {
  381. clearTimeout(this.getServicesTimeout);
  382. this.getServicesTimeout = null;
  383. }
  384. //写入指令writeMacTimeout
  385. if (this.writeMacTimeout) {
  386. clearTimeout(this.writeMacTimeout);
  387. this.writeMacTimeout = null;
  388. }
  389. if (this.searchTimeOut) {
  390. clearTimeout(this.searchTimeOut);
  391. this.searchTimeOut = null;
  392. }
  393. },
  394. //监听回调
  395. onListenerBLE(res) {
  396. console.log('onListenerBLE:', res, this.saveObj);
  397. this.add(res.value);
  398. },
  399. /**
  400. * 连接设备成功
  401. */
  402. ConnectionSuccess() {
  403. for (let i = 0; i < this.devicesList.length; i++) {
  404. let eq = this.devicesList[i];
  405. //CL806_Bandage
  406. if ((eq.ename.indexOf("CL806") > -1 && this.BLEConnectDevice.id == 0) ||
  407. (eq.ename.indexOf("CL831") > -1 && this.BLEConnectDevice.id == 1) ||
  408. (eq.ename.indexOf("CL809") > -1 && this.BLEConnectDevice.id == 2) ||
  409. (eq.ename.indexOf("CL838") > -1 && this.BLEConnectDevice.id == 3)
  410. ) {
  411. eq.bRatio = true;
  412. this.currentItem = eq;
  413. console.log('连接成功ConnectionSuccess:', eq);
  414. uni.hideToast();
  415. }
  416. }
  417. },
  418. onCheckBondDevice() {
  419. console.log("onCheckBondDevice", this.bTestBondConnect);
  420. if (this.bTestBondConnect && this.bShow) {
  421. /**
  422. * 假如手机没有匹配,断开连接
  423. */
  424. console.log(2, this.finallyUseDevice);
  425. this.B_GetBondedDevices({
  426. deviceId: this.finallyUseDevice.deviceId,
  427. success: (bondedDevice) => {
  428. uni.hideToast();
  429. if (bondedDevice == null) {
  430. // if (plus.os.name == 'Android')
  431. {
  432. //此问题 华为手机容易出现
  433. //android手机已配对的设备 不存在,但是app 又直接连接成功了。提示,并且断开app连接
  434. //1.关闭当前连接
  435. this.onOnlyCloseBLEConnection({
  436. getSuccess: () => {
  437. this.currentItem.bRatio = false;
  438. this.currentItem = null;
  439. this.saveObj = null;
  440. }
  441. });
  442. //2.跳转蓝牙设置
  443. uni.showModal({
  444. title: '蓝牙配对失败',
  445. content: '请跳转后点击配对BGBox_2020,成功后手动跳转回哔蹦重新连接。',
  446. success: (res) => {
  447. if (res.confirm) {
  448. this.B_OpenBLESetting();
  449. }
  450. }
  451. })
  452. }
  453. }
  454. }
  455. });
  456. this.bTestBondConnect = false;
  457. } else {
  458. uni.hideToast();
  459. }
  460. },
  461. onRetryConnectBLESuccess() {
  462. this.ConnectionSuccess();
  463. },
  464. hardCallbackCloseBLE() {
  465. if (this.BLEConnectDevice == null && this.currentItem && !this.bSwitch) {
  466. this.currentItem.bRatio = false;
  467. this.currentItem = null;
  468. this.saveObj = null;
  469. }
  470. //如果限制不走重连
  471. if (this.bLimitClose) return;
  472. console.log(this.currentItem, this.oldItem);
  473. if (this.oldItem && this.currentItem && this.currentItem.id == this.oldItem.id) {
  474. // this.$store.state.bConnection = false;
  475. if (this.currentItem.bRatio) {
  476. uni.showToast({
  477. title: '设备断开连接!',
  478. icon: 'none',
  479. duration: 2000,
  480. mask: true
  481. })
  482. this.currentItem.bRatio = false;
  483. this.oldItem = null;
  484. this.currentItem = null;
  485. //断开连接
  486. this.$store.state.bVerifiedConnection = false;
  487. }
  488. } else if (this.bConnection && this.BLEConnectDevice) {
  489. //假如匹配过程中断开连接
  490. this.$store.state.BLEConnectDevice = null;
  491. this.$store.state.bConnection = false;
  492. // uni.hideToast();
  493. // console.log("//假如匹配过程中断开连接");
  494. uni.showToast({
  495. title: '连接失败,尝试重新连接。',
  496. icon: 'none',
  497. mask: true,
  498. duration: 2000
  499. })
  500. if (this.getServicesTimeout) {
  501. clearTimeout(this.getServicesTimeout);
  502. this.getServicesTimeout = null;
  503. }
  504. }
  505. },
  506. /**
  507. * 开始查找设备
  508. * */
  509. startBluetoothDeviceDiscovery() {
  510. //在页面显示的时候判断是都已经初始化完成蓝牙适配器若成功,则开始查找设备
  511. let _self = this;
  512. if (_self.bOpenBluetooth) {
  513. //1.第一步还是先进行设备搜索
  514. _self.onCanStart();
  515. } else {
  516. _self.initAdapter(() => {
  517. _self.startBluetoothDeviceDiscovery();
  518. });
  519. }
  520. },
  521. /**
  522. * 通过检测手机连接的设备进行连接,null 的话进行搜索操作
  523. */
  524. onBondedDeviceConnect(data) {
  525. let _self = this;
  526. //获取手机本身已连接的硬件,
  527. //示例
  528. // {
  529. // "deviceId": "C5:5C:19:04:00:30",
  530. // "name": "BGBox_202012",
  531. // "RSSI": -74,
  532. // "localName": "BGBox_20201",
  533. // "advertisServiceUUIDs": ["00001812-0000-1000-8000-00805F9B34FB", "0000FFF0-0000-1000-8000-00805F9B34FB"]
  534. // }
  535. console.log("onBoded ***************:", _self.finallyUseDevice);
  536. _self.B_GetBondedDevices({
  537. deviceId: _self.finallyUseDevice == null ? null : _self.finallyUseDevice.deviceId,
  538. success: (bondedDevice) => {
  539. console.log("bondedDevice:", bondedDevice);
  540. //获取已和蓝牙连接的设备
  541. if (bondedDevice != null) {
  542. //如果用户匹配了对应的设备,直接用对应的设备来连接
  543. let setDevice = {
  544. "deviceId": bondedDevice.address,
  545. "name": bondedDevice.name,
  546. "RSSI": -74,
  547. "localName": "",
  548. "advertisServiceUUIDs": ["00001812-0000-1000-8000-00805F9B34FB",
  549. "0000FFF0-0000-1000-8000-00805F9B34FB"
  550. ]
  551. }
  552. let obj = Object.assign({}, setDevice, _self.currentItem);
  553. //finallyUserDevice 就是最后一次使用搜索到的设备信息
  554. _self.saveObj = Object.assign({}, setDevice, {
  555. id: _self.currentItem.id
  556. });
  557. uni.showToast({
  558. title: '设备连接中...',
  559. icon: 'loading',
  560. duration: 10000,
  561. mask: true
  562. })
  563. console.log("GetBondedDevices:::===", setDevice, _self.currentItem, obj);
  564. // 先直连,然后判断版本
  565. _self._onConnectDevice(obj);
  566. //getBond后发起的连接,不需要检测了
  567. _self.bTestBondConnect = false;
  568. } else {
  569. console.log("没有获取到绑定的设备");
  570. uni.showToast({
  571. title: '获取匹配设备失败',
  572. icon: 'none',
  573. mask: true
  574. })
  575. }
  576. }
  577. });
  578. },
  579. //开始搜索
  580. onCanStart() {
  581. let _self = this;
  582. uni.showToast({
  583. title: '设备连接中...',
  584. icon: 'loading',
  585. duration: 15000,
  586. mask: true
  587. })
  588. // 1.已经搜索到的,根据蓝牙回调的mac 地址判断合法性。
  589. uni.startBluetoothDevicesDiscovery({
  590. allowDuplicatesKey: true,
  591. success: res => {
  592. // console.log("startBluetoothDevicesDiscovery:", res);
  593. _self.bSwitch = false;
  594. _self.onBluetoothDeviceFound();
  595. },
  596. fail: res => {
  597. console.log("搜索失败!");
  598. _self.initAdapter(() => {
  599. _self.startBluetoothDeviceDiscovery();
  600. });
  601. }
  602. });
  603. // 2.没有搜索到的,一段时间后处理。比如手机已经连接了设备,但是app 里面搜索不到,也没记录使用过的。
  604. if (_self.searchTimeOut) {
  605. clearTimeout(_self.searchTimeOut);
  606. _self.searchTimeOut = null;
  607. }
  608. //搜索一段时间后,停止搜索
  609. if (plus.os.name == 'iOS') {
  610. _self.searchTimeOut = setTimeout(() => {
  611. _self.stopBluetoothDevicesDiscovery();
  612. //搜索失败后,再检查是否和手机配对的设备连接
  613. _self.onBondedDeviceConnect();
  614. }, 4000)
  615. } else {
  616. _self.searchTimeOut = setTimeout(() => {
  617. _self.stopBluetoothDevicesDiscovery();
  618. }, 8000)
  619. }
  620. },
  621. /**
  622. * 停止搜索蓝牙设备
  623. */
  624. stopBluetoothDevicesDiscovery() {
  625. uni.stopBluetoothDevicesDiscovery({
  626. success: e => {
  627. // console.log('停止搜索蓝牙设备:' + e.errMsg);
  628. },
  629. fail: e => {
  630. console.log('停止搜索蓝牙设备失败,错误码:' + e.errCode);
  631. }
  632. });
  633. },
  634. /**
  635. * 发现外围设备
  636. */
  637. onBluetoothDeviceFound() {
  638. let _self = this;
  639. _self.searchObj = null;
  640. uni.onBluetoothDeviceFound(res => {
  641. /**
  642. * 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。
  643. */
  644. // console.log("onBluetoothDeviceFound:", res); Fitcent_
  645. res.devices.forEach(device => {
  646. if (device.name.indexOf('CL809') > -1 ||
  647. device.name.indexOf('CL806') > -1 ||
  648. device.name.indexOf('CL831') > -1 ||
  649. device.name.indexOf('CL838') > -1
  650. ) {
  651. //如果搜索的设备名 不是对应当前设备类型,过滤
  652. // if (_self.currentItem.deviceName.indexOf('CL806') == -1 || _self.currentItem.deviceName.indexOf('CL831') == -1) return;
  653. //寻找到对应设备时候,其余的返回
  654. if (_self.searchObj) return;
  655. _self.searchObj = device;
  656. if (_self.searchTimeOut) {
  657. clearTimeout(_self.searchTimeOut);
  658. _self.searchTimeOut = null;
  659. }
  660. //currentItem 是mode 页面选中的item
  661. let obj = Object.assign({}, device, _self.currentItem);
  662. _self.saveObj = Object.assign({}, {
  663. id: _self.currentItem.id
  664. }, device);
  665. console.log(device, "****", obj, _self.saveObj, _self.currentItem)
  666. // 先直连,然后判断版本
  667. _self._onConnectDevice(obj);
  668. _self.stopBluetoothDevicesDiscovery();
  669. }
  670. })
  671. })
  672. },
  673. onBack() {
  674. uni.navigateBack({
  675. delta: 1
  676. })
  677. },
  678. // 提示点击连接设备
  679. _onConnectDevice(item) {
  680. //servicesTimeout
  681. if (this.getServicesTimeout) {
  682. clearTimeout(this.getServicesTimeout);
  683. this.getServicesTimeout = null;
  684. }
  685. //写入指令writeMacTimeout
  686. if (this.writeMacTimeout) {
  687. clearTimeout(this.writeMacTimeout);
  688. this.writeMacTimeout = null;
  689. }
  690. //创建一个连接,需要对应close
  691. this.onCreateBLESuccess({
  692. item: item,
  693. getSuccess: () => {
  694. console.log("****创建一个连接*******");
  695. this.getServicesTimeout = setTimeout(() => {
  696. this.onGetBLEDeviceServices({
  697. item: item,
  698. success: (res) => {
  699. console.log("******getBLEDeviceServices************",
  700. res);
  701. //连接成功了,设置旧的item
  702. this.oldItem = this.currentItem;
  703. setTimeout(() => {
  704. this.saveObj = Object.assign({}, this
  705. .saveObj, {
  706. deviceMac: '',
  707. bOldDevice: false
  708. });
  709. this.addBLEDevice(this.saveObj);
  710. this.ConnectionSuccess();
  711. }, 2000)
  712. }
  713. });
  714. }, 2500);
  715. }
  716. })
  717. },
  718. _onRadio(item, event) {
  719. console.log(this.bConnection, this.BLEGetServices);
  720. if (this.bConnection && this.BLEGetServices && this.BLEGetServices.length == 0) {
  721. console.log("***直接获取服务*******");
  722. this.onGetBLEDeviceServices({
  723. item: item,
  724. success: (res) => {
  725. console.log("*****getBLEDeviceServices************");
  726. //连接成功了,设置旧的item
  727. this.oldItem = this.currentItem;
  728. setTimeout(() => {
  729. this.saveObj = Object.assign({}, this
  730. .saveObj, {
  731. deviceMac: '',
  732. bOldDevice: false
  733. });
  734. this.addBLEDevice(this.saveObj);
  735. this.ConnectionSuccess();
  736. }, 2000)
  737. }
  738. });
  739. return;
  740. }
  741. if (!item.bRatio) {
  742. //设置默认值
  743. this.bTestBondConnect = true;
  744. if (this.BLEConnectDevice) {
  745. this.onOnlyCloseBLEConnection({
  746. getSuccess: () => {
  747. if (this.currentItem)
  748. this.currentItem.bRatio = false;
  749. this.currentItem = null;
  750. this.currentItem = item;
  751. this.bSwitch = true;
  752. console.log("this.currentItem1 ==:", this.currentItem, this
  753. .bOpenBluetooth,
  754. this.BLEConnectDevice);
  755. this.startBluetoothDeviceDiscovery();
  756. }
  757. });
  758. return;
  759. }
  760. this.currentItem = null;
  761. this.currentItem = item;
  762. // console.log("this.currentItem ==2:", this.currentItem, this.bOpenBluetooth, this.BLEConnectDevice);
  763. this.startBluetoothDeviceDiscovery();
  764. }
  765. },
  766. onClise() {
  767. this.B_CloseBLEConnection({
  768. deviceId: "C5:5C:19:04:01:22"
  769. });
  770. },
  771. //跳转进入升级页面,
  772. onNavUpdateDevice() {
  773. //需要连接设备后,才能进入升级
  774. if (!this.currentItem || !this.currentItem.bRatio || !this.BLEConnectDevice) {
  775. uni.showToast({
  776. title: '请先连接硬件!',
  777. icon: 'none'
  778. })
  779. return;
  780. }
  781. this.bLimitClose = true;
  782. uni.navigateTo({
  783. // url: "../devices-update/devices-update?deviceType=" + this.option.deviceType
  784. url: "../devices-update/devices-update"
  785. })
  786. },
  787. add: function(data) {
  788. // console.log(data)
  789. if (this.extraLine.length > 500) {
  790. this.extraLine = [];
  791. this.text = this.extraLine.join('\n');
  792. }
  793. this.extraLine.push(data);
  794. this.text = this.extraLine.join('\n');
  795. },
  796. remove: function(e) {
  797. if (this.extraLine.length > 0) {
  798. // this.extraLine.pop();
  799. this.extraLine = [];
  800. this.text = this.extraLine.join('\n');
  801. }
  802. },
  803. writeBLEValue(str) {
  804. this.onWriteBLEConnectionValue({
  805. value: str
  806. })
  807. },
  808. onKeyInput: function(event) {
  809. this.onSetLocationGameUrl(event.target.value);
  810. },
  811. initTextToSpeech() {
  812. // 调用同步方法
  813. var ret = testModule.initTextToSpeech();
  814. console.log("initTextToSpeech:", ret);
  815. // uni.showToast({
  816. // title: "initTextToSpeech"
  817. // })
  818. },
  819. onGetRecordAudioPermission() {
  820. var ret = testModule.getRecordAudioPermission();
  821. console.log(ret);
  822. console.log(ret.bPermission)
  823. },
  824. initWakeup() {
  825. console.log("initWakeup!");
  826. // 调用同步方法
  827. testModule.initWakeup(null, (data) => {
  828. if (data.code === 200) {
  829. console.log("开启唤醒失败");
  830. uni.showToast({
  831. title: "初始化唤醒失败!",
  832. icon: 'none'
  833. })
  834. //停止语音
  835. this.stopSpeak();
  836. this.$set(this.treatmentStatu, 'checked', false);
  837. } else if (data.code === 0) {
  838. uni.showToast({
  839. title: "初始化唤醒成功!",
  840. icon: 'none'
  841. })
  842. this.$set(this.treatmentStatu, 'checked', true);
  843. //记录一下回调
  844. plus.globalEvent.addEventListener("onWakeup", this.onWakeupCallback);
  845. setTimeout(() => {
  846. this.recordSpeakDown();
  847. }, 1000)
  848. }
  849. });
  850. },
  851. onWakeupCallback(data) {
  852. // this.text = JSON.stringify(data);
  853. console.log("onWakeupCallback:" + JSON.stringify(data));
  854. if (data.word == "实时心率" || data.word == "播放") {
  855. uni.showToast({
  856. icon: 'none',
  857. title: data.word
  858. })
  859. this.playSpeak('当前心率' + this.heartRate);
  860. } else if (data.word == "停止播报") {
  861. this.$set(this.treatmentStatu, 'checked', false) // 手动修改switch的状态,视图会同步更新
  862. //停止语音
  863. this.stopSpeak();
  864. //停止唤醒
  865. this.releaseToSpeech();
  866. }
  867. },
  868. releaseToSpeech() {
  869. // 调用同步方法
  870. var ret = testModule.releaseToSpeech();
  871. console.log(ret);
  872. uni.showToast({
  873. title: "releaseToSpeech"
  874. })
  875. },
  876. playSpeak(text) {
  877. let currentTime = new Date().getTime() - this.playTime;
  878. if (currentTime > 3000) {
  879. // 调用同步方法
  880. var ret = testModule.playSpeak(text);
  881. console.log(ret);
  882. this.playTime = new Date().getTime();
  883. }
  884. },
  885. stopSpeak() {
  886. // 调用同步方法
  887. var ret = testModule.stopSpeak();
  888. console.log(ret);
  889. uni.showToast({
  890. title: "stopSpeak"
  891. })
  892. },
  893. startLuyin() {
  894. console.log('语音输入')
  895. let _this = this;
  896. let options = {};
  897. options.engine = _this.speechEngine
  898. options.punctuation = false; // 是否需要标点符号
  899. options.timeout = 10 * 1000; //超时时间
  900. plus.speech.startRecognize(options, function(s) {
  901. console.log(s) //识别的结果
  902. _this.searchText = s
  903. plus.speech.stopRecognize(); // 关闭
  904. });
  905. },
  906. recordSpeakDown() {
  907. // 调用同步方法
  908. var ret = testModule.recordSpeakDown();
  909. console.log("recordSpeakDown" + JSON.stringify(ret));
  910. // uni.showToast({
  911. // title: "recordSpeakDown"
  912. // })
  913. },
  914. recordSpeakUp() {
  915. // 调用同步方法
  916. var ret = testModule.recordSpeakUp();
  917. console.log("recordSpeakUp" + JSON.stringify(ret));
  918. // uni.showToast({
  919. // title: "recordSpeakUp"
  920. // })
  921. },
  922. /**
  923. * 计时器
  924. */
  925. startTimer() {
  926. //用心率判断是否佩戴上
  927. if(this.heartRate === 0){
  928. uni.showToast({
  929. title:"请佩戴好手表后开始",
  930. icon:'none'
  931. })
  932. return;
  933. }
  934. if (this.timer) {
  935. clearInterval(this.timer);
  936. this.timer = null;
  937. }
  938. this.timer = setInterval(() => {
  939. this.millisecond = this.millisecond + 50;
  940. if (this.millisecond >= 1000) {
  941. this.millisecond = 0;
  942. this.second = this.second + 1;
  943. //计算进度条数据
  944. if (this.bLoading && this.loadingCount < 60) {
  945. this.loadingCount++;
  946. this.loading = Math.round((this.loadingCount / 60) *100) + "%";
  947. }
  948. }
  949. if (this.second >= 60) {
  950. this.second = 0;
  951. this.minute = this.minute + 1;
  952. }
  953. if (this.minute >= 60) {
  954. this.minute = 0;
  955. this.hour = this.hour + 1;
  956. }
  957. this.nums = this.hour + '时' + this.minute + '分' + this.second + '秒';
  958. }, 50);
  959. this.bTimerPlay = true;
  960. },
  961. pauseTime() {
  962. if (this.timer) {
  963. clearInterval(this.timer);
  964. this.timer = null;
  965. }
  966. },
  967. closeTimer() {
  968. this.hour = this.minute = this.second = 0; //初始化
  969. this.millisecond = 0; //毫秒
  970. if (this.timer) {
  971. clearInterval(this.timer);
  972. this.timer = null;
  973. }
  974. this.nums = 0 + '时' + 0 + '分' + 0 + '秒';
  975. this.bTimerPlay = false;
  976. },
  977. onRateChange(type, value) {
  978. switch (type) {
  979. case "min":
  980. if ((value < 0 && this.rateMin > 0) || (value > 0 && this.rateMin + 1 < this.rateMax)) {
  981. this.rateMin += value;
  982. } else {
  983. uni.showToast({
  984. icon: 'none',
  985. title: 'rateMin 不能超过最大值或者小于0',
  986. })
  987. }
  988. break;
  989. case "max":
  990. if ((value < 0 && this.rateMin + 1 < this.rateMax) || value > 0) {
  991. this.rateMax += value;
  992. } else {
  993. uni.showToast({
  994. icon: 'none',
  995. title: 'rateMax 不能小于最小值',
  996. })
  997. }
  998. break;
  999. }
  1000. },
  1001. onConfirm(args) {
  1002. this.hasUpdate = true;
  1003. console.log('onConfirm', args);
  1004. let tempValue = parseInt(args.value);
  1005. if (this.pikerType == 'min') {
  1006. if (tempValue < this.rateMax) {
  1007. this.rateMin = parseInt(args.value);
  1008. } else {
  1009. uni.showToast({
  1010. icon: 'none',
  1011. title: '不能大于或等于最大值',
  1012. })
  1013. }
  1014. } else if (this.pikerType == 'max') {
  1015. if (tempValue > this.rateMin) {
  1016. this.rateMax = parseInt(args.value);
  1017. } else {
  1018. uni.showToast({
  1019. icon: 'none',
  1020. title: '不能小于或等于最小值',
  1021. })
  1022. }
  1023. }
  1024. this.hideModal();
  1025. },
  1026. onCancel(value) {
  1027. console.log('onCancel', value);
  1028. this.modalName = null;
  1029. },
  1030. showModal(e) {
  1031. // console.log(e.currentTarget.type);
  1032. this.modalName = e.currentTarget.dataset.target;
  1033. this.pikerType = e.currentTarget.dataset.type;
  1034. switch (this.pikerType) {
  1035. case 'min':
  1036. this.$set(this.pickerObj, 'pickerLeftList', pickerData.getRateList().rateList);
  1037. this.$set(this.pickerObj, 'pickerRightList', []);
  1038. this.$set(this.pickerObj, 'pickerUnit', '');
  1039. this.$set(this.pickerObj, 'pickerType', 'singleItem');
  1040. this.$set(this.pickerObj, 'pickerTitle', '最小心率');
  1041. this.$set(this.pickerObj, 'defaultValue', this.rateMin);
  1042. this.$set(this.pickerObj, 'showInput', true);
  1043. break;
  1044. case 'max':
  1045. this.$set(this.pickerObj, 'pickerLeftList', pickerData.getRateList().rateList);
  1046. this.$set(this.pickerObj, 'pickerRightList', []);
  1047. this.$set(this.pickerObj, 'pickerUnit', '');
  1048. this.$set(this.pickerObj, 'pickerType', 'singleItem');
  1049. this.$set(this.pickerObj, 'pickerTitle', '最大心率');
  1050. this.$set(this.pickerObj, 'defaultValue', this.rateMax);
  1051. this.$set(this.pickerObj, 'showInput', true);
  1052. break;
  1053. }
  1054. },
  1055. hideModal(e) {
  1056. this.modalName = null;
  1057. this.pikerType = '';
  1058. },
  1059. moveHandle() {
  1060. return;
  1061. },
  1062. switchChange(e) {
  1063. let value = e.target.value
  1064. let that = this
  1065. this.$set(this.treatmentStatu, 'checked', value) // 将点击改变的状态赋给treatmentStatu.checked
  1066. if (value) {
  1067. uni.showModal({
  1068. title: '提示',
  1069. content: '是否开启语音播报?',
  1070. success: function(res) {
  1071. if (res.confirm) {
  1072. //todo 打开语音播报
  1073. console.log('用户点击确定')
  1074. that.initTextToSpeech();
  1075. that.initWakeup();
  1076. } else if (res.cancel) {
  1077. that.$set(that.treatmentStatu, 'checked', false) // 手动修改switch的状态,视图会同步更新
  1078. }
  1079. }
  1080. });
  1081. } else {
  1082. uni.showModal({
  1083. title: '提示',
  1084. content: '是否关闭语音播报?',
  1085. success: function(res) {
  1086. if (res.confirm) {
  1087. //todo 关闭语音播报
  1088. console.log('用户点击确定')
  1089. //停止语音
  1090. that.stopSpeak();
  1091. that.releaseToSpeech();
  1092. } else if (res.cancel) {
  1093. that.$set(that.treatmentStatu, 'checked', true) // 手动修改switch的状态,视图会同步更新
  1094. }
  1095. }
  1096. });
  1097. }
  1098. }
  1099. }
  1100. }
  1101. </script>
  1102. <style>
  1103. .hardware-border {
  1104. border: 1rpx solid #9898FF;
  1105. box-sizing: border-box;
  1106. }
  1107. .swiper {
  1108. flex: 1;
  1109. background-color: rgba(0, 0, 0, 0.32);
  1110. }
  1111. .swiper-item {
  1112. flex: 1;
  1113. }
  1114. .bluetooth-guide-number {
  1115. border-radius: 18px;
  1116. border-width: 1rpx;
  1117. width: 30px;
  1118. height: 30px;
  1119. text-align: center;
  1120. line-height: 30px;
  1121. color: #000000;
  1122. /* border-color: #FFFFFF; */
  1123. background-color: #FFFFFF;
  1124. margin-right: 13px;
  1125. }
  1126. .text-box {
  1127. margin: 20rpx;
  1128. display: flex;
  1129. width: 95%;
  1130. min-height: 300rpx;
  1131. max-height: 600rpx;
  1132. background-color: #c6c6c6;
  1133. justify-content: center;
  1134. align-items: center;
  1135. text-align: left;
  1136. font-size: 30upx;
  1137. color: #353535;
  1138. line-height: 1.8;
  1139. border: 1rpx solid #555555;
  1140. overflow-y: hidden;
  1141. }
  1142. .content {
  1143. align-content: center;
  1144. height: 300rpx;
  1145. background-color: #F4F5F6;
  1146. }
  1147. .uni-input {
  1148. border: 1rpx solid #000000;
  1149. }
  1150. .recordingStyle {
  1151. border-radius: 20px;
  1152. text-align: center;
  1153. color: #fff;
  1154. font-size: 15px;
  1155. background-color: #409eff;
  1156. margin-bottom: 15px;
  1157. }
  1158. </style>