diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2e0c9c9..3128e38 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -8,7 +8,8 @@ module.exports = { globals: { uni: 'writable', getApp: 'writable', - wx: 'writable' + wx: 'writable', + getCurrentPages: 'writable' }, // 指定如何解析语法 parser: 'vue-eslint-parser', @@ -41,17 +42,15 @@ module.exports = { 'no-var': 'error', // 要求使用 let 或 const 而不是 var 'no-multiple-empty-lines': ['error', { max: 1 }], // 不允许多个空行 'prefer-const': 'off', // 使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const - 'no-use-before-define': 'error', // 禁止在 函数/类/变量 定义之前使用它们 + 'no-use-before-define': 'off', // 禁止在 函数/类/变量 定义之前使用它们 'no-irregular-whitespace': 'off', // 禁止不规则的空白 'no-undef': 'error', // 禁止使用未声明的变量 - 'vue/script-setup-uses-vars': 'off', // 关闭此规则,因为它可能会干扰no-undef的检测 + 'no-unused-vars': 'error', // 禁止出现未使用过的变量 + 'vue/script-setup-uses-vars': 'error', // 确保script setup中的变量必须正确定义 'vue/no-undef-components': 'off', // 关闭组件未定义的检查 - 'vue/no-undef-properties': [ - 'error', - { - ignore: ['getDeviceInfo', 'getBluetoothDevices', 'stopGetBluetoothDevices'] - } - ], // 忽略特定方法的未定义检查 + 'vue/no-undef-properties': 'off', // 关闭属性未定义的检查 + 'vue/no-unused-vars': 'error', // 禁止Vue组件中出现未使用的变量 + 'import/no-unused-modules': 'off', // 关闭模块导出检查 'import/no-cycle': 0, 'no-nested-ternary': 0, 'import/prefer-default-export': 0, diff --git a/App.vue b/App.vue index b2795b5..6b24e74 100644 --- a/App.vue +++ b/App.vue @@ -39,6 +39,8 @@ this.updateMiniProgram() // 监听蓝牙开关状态 this.onBluetoothState() + // 设置voip配置 + this.setVoipConfig() // 检查蓝牙权限 const checkResult = await this.checkSetting() console.log(checkResult) @@ -65,6 +67,24 @@ 'initAndListenBluetooth' ]), ...mapActions(useUserStore, ['updateLoginStatus']), + // voip设置 + setVoipConfig() { + const wmpfVoip = requirePlugin('wmpf-voip').default + wmpfVoip.setUIConfig({ + btnText: '去开门', + customBoxHeight: '75vh', + listenerUI: { + cameraRotation: 270, + objectFit: 'fill', + enableToggleCamera: false + } + }) + wmpfVoip.setVoipEndPagePath({ + url: '/pages/main/home', + key: 'Call', + routeType: 'switchTab' + }) + }, // 强制升级 updateMiniProgram() { const updateManager = uni.getUpdateManager() diff --git a/api/p2p.js b/api/p2p.js deleted file mode 100644 index 7f6d4a4..0000000 --- a/api/p2p.js +++ /dev/null @@ -1,12 +0,0 @@ -import request from '../utils/request' - -// p2p 模块 - -// 获取p2pInfo -export function getP2pInfo(data) { - return request({ - url: '/v1/tencentYun/getDeviceDetail', - method: 'POST', - data - }) -} diff --git a/api/sdk.js b/api/sdk.js new file mode 100644 index 0000000..5ec215c --- /dev/null +++ b/api/sdk.js @@ -0,0 +1,12 @@ +import request from '../utils/request' + +// sdk 模块 + +// 透传 +export function passthrough(data) { + return request({ + url: '/passthrough', + method: 'POST', + data + }) +} diff --git a/config/env.js b/config/env.js index d64d6c6..75636f8 100644 --- a/config/env.js +++ b/config/env.js @@ -9,14 +9,17 @@ uni.getSystemInfo({ const DEV = { name: 'dev', - baseUrl: 'https://dev.lock.star-lock.cn/api', - webviewBaseUrl: 'https://dev.lock.star-lock.cn', + appName: '星星锁Lite', + baseUrl: 'https://lock.dev.star-lock.cn/api', + webviewBaseUrl: 'https://lock.dev.star-lock.cn', version, buildNumber } + const PRE = { name: 'pre', - baseUrl: 'https://pre.lock.star-lock.cn/api', + appName: '星星锁Lite', + baseUrl: 'https://lock.pre.star-lock.cn/api', webviewBaseUrl: 'https://lock.xhjcn.ltd', version, buildNumber @@ -24,6 +27,7 @@ const PRE = { const XHJ = { name: 'xhj', + appName: '星星锁Lite', baseUrl: 'https://lock.xhjcn.ltd/api', webviewBaseUrl: 'https://lock.xhjcn.ltd', version, @@ -32,6 +36,7 @@ const XHJ = { const SKY = { name: 'sky', + appName: '星星锁Lite', baseUrl: 'https://lock.skychip.top/api', webviewBaseUrl: 'https://lock.skychip.top', version, @@ -40,6 +45,7 @@ const SKY = { const GE = { name: 'ge', + appName: '星星锁Lite', baseUrl: 'http://lock.ge.star-lock.cn/api', webviewBaseUrl: 'http://lock.ge.star-lock.cn', version, diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..eb0cdc9 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": [ + "./*" + ] + }, + "target": "es2020", + "module": "esnext", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "jsx": "preserve", + "strict": true, + "skipLibCheck": true + }, + "include": [ + "**/*.js", + "**/*.jsx", + "**/*.vue" + ], + "exclude": [ + "node_modules", + "unpackage", + "dist" + ] +} \ No newline at end of file diff --git a/manifest.json b/manifest.json index 715ce32..fdb5525 100644 --- a/manifest.json +++ b/manifest.json @@ -30,14 +30,14 @@ "provider": "wx1319af22356934bf", "export": "exportForXp2pPlugin.js" }, - "wechat-p2p-player": { - "version": "latest", - "provider": "wx9e8fbc98ceac2628", - "export": "exportForPlayerPlugin.js" - }, "wmpf-voip": { "version": "latest", - "provider": "wxf830863afde621eb" + "provider": "wxf830863afde621eb", + "genericsImplementation": { + "call-page-plugin": { + "custombox": "pages/main/customBox" + } + } } } }, diff --git a/pages.json b/pages.json index b85c5ba..fbe944f 100644 --- a/pages.json +++ b/pages.json @@ -16,6 +16,7 @@ "style": { "navigationStyle": "custom", "navigationBarTitleText": "监控", + "disableScroll": true, "mp-weixin": { "usingComponents": { "iot-p2p-player-with-mjpg": "plugin://xp2p/iot-p2p-player-with-mjpg", @@ -23,8 +24,22 @@ } } } + }, + { + "path": "authorizeWechat", + "style": { + "navigationBarTitleText": "微信授权", + "disableScroll": true + } } - ] + ], + "plugins": { + "wechat-p2p-player": { + "version": "latest", + "provider": "wx9e8fbc98ceac2628", + "export": "exportForPlayerPlugin.js" + } + } }, { "root": "pages/addDevice", @@ -560,6 +575,9 @@ { "path": "pages/main/mine" }, + { + "path": "pages/main/customBox" + }, { "path": "pages/main/notificationList", "style": { diff --git a/pages/addDevice/addLockGuid.vue b/pages/addDevice/addLockGuid.vue index 1f0c932..b3accef 100644 --- a/pages/addDevice/addLockGuid.vue +++ b/pages/addDevice/addLockGuid.vue @@ -12,10 +12,10 @@ - - diff --git a/pages/addDevice/searchDevice.vue b/pages/addDevice/searchDevice.vue index c1a7c66..abdb58f 100644 --- a/pages/addDevice/searchDevice.vue +++ b/pages/addDevice/searchDevice.vue @@ -139,7 +139,7 @@ } this.routeJump({ type: 'redirectTo', - name: 'selectAddress' + name: false ? 'selectAddress' : 'distributionNetwork' }) } else { uni.hideLoading() diff --git a/pages/feature/recordList.vue b/pages/feature/recordList.vue index 4ffef70..5bc9a95 100644 --- a/pages/feature/recordList.vue +++ b/pages/feature/recordList.vue @@ -384,36 +384,36 @@ diff --git a/pages/main/lockDetail.vue b/pages/main/lockDetail.vue index b4de880..b2a3cee 100644 --- a/pages/main/lockDetail.vue +++ b/pages/main/lockDetail.vue @@ -221,6 +221,16 @@ > 消息提醒 + + + 微信呼叫 + diff --git a/pages/p2p/authorizeWechat.vue b/pages/p2p/authorizeWechat.vue new file mode 100644 index 0000000..5c957bb --- /dev/null +++ b/pages/p2p/authorizeWechat.vue @@ -0,0 +1,148 @@ + + + diff --git a/pages/p2p/p2pPlayer.vue b/pages/p2p/p2pPlayer.vue index 6afe085..0a07bbc 100644 --- a/pages/p2p/p2pPlayer.vue +++ b/pages/p2p/p2pPlayer.vue @@ -1,20 +1,50 @@ diff --git a/pages/p2p/utils.js b/pages/p2p/utils.js deleted file mode 100644 index 6d7d0c3..0000000 --- a/pages/p2p/utils.js +++ /dev/null @@ -1,29 +0,0 @@ -export const getUserId = () => { - return 1 -} - -export function compareVersion(ver1, ver2) { - const v1 = ver1.split('.') - const v2 = ver2.split('.') - const len = Math.max(v1.length, v2.length) - - while (v1.length < len) { - v1.push('0') - } - while (v2.length < len) { - v2.push('0') - } - - for (let i = 0; i < len; i++) { - const num1 = parseInt(v1[i]) - const num2 = parseInt(v2[i]) - - if (num1 > num2) { - return 1 - } else if (num1 < num2) { - return -1 - } - } - - return 0 -} diff --git a/pages/p2p/xp2pManager.js b/pages/p2p/xp2pManager.js deleted file mode 100644 index 6eefc30..0000000 --- a/pages/p2p/xp2pManager.js +++ /dev/null @@ -1,118 +0,0 @@ -import { getUserId, compareVersion } from './utils' - -let xp2pManager = null -export const getXp2pManager = () => { - if (!xp2pManager) { - let xp2pPlugin = requirePlugin('xp2p') - - console.log(11111, xp2pPlugin) - - const iotExports = xp2pPlugin.iot - const app = getApp() - - // 用户id,微信用户在此小程序中的唯一标识 - iotExports?.setUserId?.(getUserId() || 'demo') - - // 开发版才打插件log - if (app.pluginLogger && uni.getAccountInfoSync().miniProgram.envVersion === 'develop') { - iotExports?.setPluginLogger?.(app.pluginLogger) - } - - // 设置优先使用的打洞协议 - if ( - compareVersion(uni.getSystemInfoSync().SDKVersion, '3.4.1') >= 0 && - xp2pPlugin.p2p.setTcpFirst - ) { - const tcpFirstKey = 'tcpFirst' - const tcpFirstTime = parseInt(uni.getStorageSync(tcpFirstKey), 10) - const tcpFirst = !!(tcpFirstTime && Date.now() - tcpFirstTime < 3600000 * 24) // 24小时内有效 - console.log('tcpFirst', tcpFirst) - xp2pPlugin.p2p.setTcpFirst(tcpFirst) - - // 给index页用,方便测试时调整tcpFirst - app.tcpFirst = tcpFirst - app.toggleTcpFirst = async () => { - const modalRes = await uni.showModal({ - title: '确定切换 tcpFirst 吗?', - content: '切换后需要重新进入小程序' - }) - if (!modalRes || !modalRes.confirm) { - return - } - uni.setStorageSync(tcpFirstKey, tcpFirst ? '' : Date.now()) - app.restart() - } - } - - // 设置是否连接对端stun - if (xp2pPlugin.p2p.setCrossStunTurn) { - const crossStunTurnKey = 'crossStunTurn' - const crossStunTurnTime = parseInt(uni.getStorageSync(crossStunTurnKey), 10) - const crossStunTurn = !!(crossStunTurnTime && Date.now() - crossStunTurnTime < 3600000 * 24) // 24小时内有效 - xp2pPlugin.p2p.setCrossStunTurn(crossStunTurn) - // 给index页用,方便测试时调整crossStunTurn - app.crossStunTurn = crossStunTurn - app.toggleCrossStunTurn = async () => { - const modalRes = await uni.showModal({ - title: '确定切换 crossStunTurn 吗?', - content: '切换后需要重新进入小程序' - }) - if (!modalRes || !modalRes.confirm) { - return - } - uni.setStorageSync(crossStunTurnKey, crossStunTurn ? '' : Date.now()) - app.restart() - } - } - - // 设置切换端口 - if (xp2pPlugin.p2p.updateStunPort) { - const portKey = 'STUN_PORT' - const stunPort = uni.getStorageSync(portKey) || 20002 - xp2pPlugin.p2p.updateStunPort(stunPort) - app.stunPort = stunPort - app.togglePort = async port => { - const modalRes = await uni.showModal({ - title: '确定切换 stunPort 吗?', - content: '切换后需要重新进入小程序' - }) - if (!modalRes || !modalRes.confirm) { - return - } - uni.setStorageSync(portKey, port) - app.restart() - } - } - - // 设置切换配置跟随 - if (xp2pPlugin.p2p.setUseDeliveryConfig) { - const useDeliveryConfigKey = 'useDeliveryConfig' - const useDeliveryConfigTime = parseInt(uni.getStorageSync(useDeliveryConfigKey), 10) - const useDeliveryConfig = !!( - useDeliveryConfigTime && Date.now() - useDeliveryConfigTime < 3600000 * 24 - ) // 24小时内有效 - xp2pPlugin.p2p.setUseDeliveryConfig(useDeliveryConfig) - app.useDeliveryConfig = useDeliveryConfig - app.toggleUseDeliveryConfig = async () => { - const modalRes = await uni.showModal({ - title: '确定切换 使用设备跟随 吗?', - content: '切换后需要重新进入小程序' - }) - if (!modalRes || !modalRes.confirm) { - return - } - uni.setStorageSync(useDeliveryConfigKey, useDeliveryConfig ? '' : Date.now()) - app.restart() - } - } - - xp2pManager = iotExports.getXp2pManager() - app.logger?.log('xp2pManager', { - P2PPlayerVersion: xp2pManager.P2PPlayerVersion, - XP2PVersion: xp2pManager.XP2PVersion, - // uuid,插件随机生成的id,存储在小程序本地,删除小程序后会重新生成 - uuid: xp2pManager.uuid - }) - } - return xp2pManager -} diff --git a/pages/setting/catEye.vue b/pages/setting/catEye.vue index 66d25a5..780ef06 100644 --- a/pages/setting/catEye.vue +++ b/pages/setting/catEye.vue @@ -9,7 +9,7 @@ ? catEyeMode[ $bluetooth.currentLockSetting.lockSettingInfo.catEyeConfig[0]?.catEyeMode ].name - : "" + : '' }} diff --git a/stores/basic.js b/stores/basic.js index f62d83e..f74cac8 100644 --- a/stores/basic.js +++ b/stores/basic.js @@ -391,6 +391,11 @@ const pages = [ name: 'p2pPlayer', path: '/pages/p2p/p2pPlayer', tabBar: false + }, + { + name: 'authorizeWechat', + path: '/pages/p2p/authorizeWechat', + tabBar: false } ] diff --git a/stores/bluetooth.js b/stores/bluetooth.js index 66c2baa..1302e11 100644 --- a/stores/bluetooth.js +++ b/stores/bluetooth.js @@ -56,7 +56,15 @@ const cmdIds = { // 锁遥控列表 lockRemoteList: 0x3026, // 锁设置列表 - lockSettingList: 0x302a + lockSettingList: 0x302a, + // 获取Wi-Fi列表 + getWifiList: 0x30f6, + // Wi-Fi列表 + wifiList: 0x30f7, + // 配网 + distributionNetwork: 0x30f4, + // 配网结果 + distributionNetworkResult: 0x30f5 } // 子命令ID @@ -396,6 +404,34 @@ export const useBluetoothStore = defineStore('ble', { }) } break + case cmdIds.getWifiList: + characteristicValueCallback({ + code: decrypted[2] + }) + break + case cmdIds.wifiList: + const wifiList = [] + for (let i = 0; i < decrypted[3]; i++) { + wifiList.push({ + SSID: that.uint8ArrayToString(decrypted.slice(4 + 33 * i, 4 + 33 * i + 32)), + rssi: decrypted[33 + 33 * i] + }) + } + uni.$emit('wifiList', { + status: decrypted[2], + wifiList + }) + break + case cmdIds.distributionNetwork: + characteristicValueCallback({ + code: decrypted[2] + }) + break + case cmdIds.distributionNetworkResult: + uni.$emit('distributionNetworkResult', { + status: decrypted[2] + }) + break case cmdIds.addUser: that.updateCurrentLockInfo({ ...that.currentLockInfo, @@ -1558,6 +1594,123 @@ export const useBluetoothStore = defineStore('ble', { return this.getWriteResult(this.getLockStatus, data) }, + // 获取Wi-Fi列表 + async getWifiList(data) { + // 确认蓝牙状态正常 + if (this.bluetoothStatus !== 0) { + console.log('写入未执行', this.bluetoothStatus) + this.getBluetoothStatus() + return { + code: -1 + } + } + + // 确认设备连接正常 + if (!this.currentLockInfo.connected) { + const searchResult = await this.searchAndConnectDevice() + if (searchResult.code !== 0) { + return searchResult + } + this.updateCurrentLockInfo({ + ...this.currentLockInfo, + deviceId: searchResult.data.deviceId + }) + console.log('设备ID:', this.currentLockInfo.deviceId) + const result = await this.connectBluetoothDevice() + console.log('连接结果', result) + if (!result) { + return { + code: -1 + } + } + } + + const { uid } = data + const length = 2 + 20 + const headArray = this.createPackageHeader(3, length) + + const contentArray = new Uint8Array(length) + contentArray[0] = cmdIds.getWifiList / 256 + contentArray[1] = cmdIds.getWifiList % 256 + for (let i = 0; i < uid.length; i++) { + contentArray[i + 2] = uid.charCodeAt(i) + } + + const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, { + mode: 'ecb', + output: 'array' + }) + + const packageArray = this.createPackageEnd(headArray, cebArray) + + await this.writeBLECharacteristicValue(packageArray) + + return this.getWriteResult(this.getWifiList, data) + }, + // 配网 + async distributionNetwork(data) { + // 确认蓝牙状态正常 + if (this.bluetoothStatus !== 0) { + console.log('写入未执行', this.bluetoothStatus) + this.getBluetoothStatus() + return { + code: -1 + } + } + + // 确认设备连接正常 + if (!this.currentLockInfo.connected) { + const searchResult = await this.searchAndConnectDevice() + if (searchResult.code !== 0) { + return searchResult + } + this.updateCurrentLockInfo({ + ...this.currentLockInfo, + deviceId: searchResult.data.deviceId + }) + console.log('设备ID:', this.currentLockInfo.deviceId) + const result = await this.connectBluetoothDevice() + console.log('连接结果', result) + if (!result) { + return { + code: -1 + } + } + } + + const { SSID, password, json } = data + const length = 2 + 30 + 20 + json.length + const headArray = this.createPackageHeader(3, length) + + const contentArray = new Uint8Array(length) + contentArray[0] = cmdIds.distributionNetwork / 256 + contentArray[1] = cmdIds.distributionNetwork % 256 + for (let i = 0; i < SSID.length; i++) { + contentArray[i + 2] = SSID.charCodeAt(i) + } + + for (let i = 0; i < password.length; i++) { + contentArray[i + 32] = password.charCodeAt(i) + } + + contentArray[52] = json.length / 256 + contentArray[53] = json.length % 256 + + for (let i = 0; i < json.length; i++) { + contentArray[i + 54] = json.charCodeAt(i) + } + + const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, { + mode: 'ecb', + output: 'array' + }) + + const packageArray = this.createPackageEnd(headArray, cebArray) + + await this.writeBLECharacteristicValue(packageArray) + + return this.getWriteResult(this.distributionNetwork, data) + }, // 时间戳转二进制 timestampToArray(timestamp) { const array = new Uint8Array(4) @@ -2732,12 +2885,12 @@ export const useBluetoothStore = defineStore('ble', { const md5Array = this.md5Encrypte( uid + keyId, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), - this.currentLockInfo.bluetooth.publicKey + this.currentLockInfo.publicKey ) contentArray.set(md5Array, 75) - const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.bluetooth.privateKey, { + const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) diff --git a/utils/request.js b/utils/request.js index a1a0a1d..dabfeba 100644 --- a/utils/request.js +++ b/utils/request.js @@ -29,13 +29,13 @@ const request = config => { const timestamp = new Date().getTime() timer = setTimeout(() => { resolve({ code: -1, message: '网络访问失败,请检查网络是否正常' }) - }, 7200) + }, 30200) uni.request({ url: URL, method, header, data, - timeout: 7000, + timeout: 30000, async success(res) { const { statusCode, data } = res if (timer) {