diff --git a/README-萤石云对接.md b/README-萤石云对接.md
index 31bce4d..9754181 100644
--- a/README-萤石云对接.md
+++ b/README-萤石云对接.md
@@ -248,3 +248,5 @@ console.log('组件ref:', this.$refs.playerVideoRef)
需要详细说明?查看 → [萤石云APP对接完整指南.md](./萤石云APP对接完整指南.md)
+
+
diff --git a/src/components/EzvizVideoPlayerSimple.vue b/src/components/EzvizVideoPlayerSimple.vue
index 7f2257f..d44dba2 100644
--- a/src/components/EzvizVideoPlayerSimple.vue
+++ b/src/components/EzvizVideoPlayerSimple.vue
@@ -17,13 +17,33 @@
>
-
+
- H5平台暂不支持
+
+
+
-
-
+
+
+
+
+ {{ isPlaying ? '⏸ 暂停' : '▶ 播放' }}
+
+
+ 🔄 刷新
+
+
+
+
+
+
+
@@ -31,6 +51,7 @@
🔄 刷新
+
{{ loadingText }}
@@ -39,6 +60,16 @@
{{ errorText }}
+
+
+
+
+
+
@@ -56,7 +87,8 @@ export default {
return {
platform: '',
status: '未初始化',
- webviewUrl: '',
+ webviewUrl: '', // APP平台使用
+ iframeUrl: '', // H5平台使用
loading: false,
loadingText: '',
error: false,
@@ -96,12 +128,26 @@ export default {
this.status = '加载中'
try {
+ // #ifdef APP-PLUS
+ // APP平台:使用本地HTML文件
const token = encodeURIComponent(config.accessToken)
const url = encodeURIComponent(config.play_url)
- // 使用iframe版本(内存占用更小)
this.webviewUrl = `/static/html/ezviz-iframe.html?accessToken=${token}&playUrl=${url}`
+ console.log('[简单播放器] APP平台 - 使用本地HTML文件')
+ // #endif
- console.log('[简单播放器] 使用iframe版本,URL已设置')
+ // #ifdef H5
+ // H5平台:直接构建萤石云iframe URL
+ this.iframeUrl = 'https://open.ys7.com/ezopen/h5/iframe?' +
+ 'url=' + encodeURIComponent(config.play_url) +
+ '&accessToken=' + encodeURIComponent(config.accessToken) +
+ '&width=100%' +
+ '&height=100%' +
+ '&autoplay=1' +
+ '&audio=1' +
+ '&controls=1'
+ console.log('[简单播放器] H5平台 - 直接使用萤石云iframe')
+ // #endif
setTimeout(() => {
if (this.loading) {
@@ -154,7 +200,13 @@ export default {
// 因为iframe播放器不支持直接控制,所以采用重新加载的方式
if (this.isPlaying) {
// 暂停:清空URL
+ // #ifdef APP-PLUS
this.webviewUrl = ''
+ // #endif
+ // #ifdef H5
+ this.iframeUrl = ''
+ // #endif
+
this.isPlaying = false
this.status = '已暂停'
@@ -163,9 +215,23 @@ export default {
} else {
// 播放:重新设置URL
if (this.config) {
+ // #ifdef APP-PLUS
const token = encodeURIComponent(this.config.accessToken)
const url = encodeURIComponent(this.config.play_url)
this.webviewUrl = `/static/html/ezviz-iframe.html?accessToken=${token}&playUrl=${url}`
+ // #endif
+
+ // #ifdef H5
+ this.iframeUrl = 'https://open.ys7.com/ezopen/h5/iframe?' +
+ 'url=' + encodeURIComponent(this.config.play_url) +
+ '&accessToken=' + encodeURIComponent(this.config.accessToken) +
+ '&width=100%' +
+ '&height=100%' +
+ '&autoplay=1' +
+ '&audio=1' +
+ '&controls=1'
+ // #endif
+
this.isPlaying = true
this.status = '播放中'
@@ -195,12 +261,31 @@ export default {
})
// 先清空再重新加载
+ // #ifdef APP-PLUS
this.webviewUrl = ''
+ // #endif
+ // #ifdef H5
+ this.iframeUrl = ''
+ // #endif
setTimeout(() => {
+ // #ifdef APP-PLUS
const token = encodeURIComponent(this.config.accessToken)
const url = encodeURIComponent(this.config.play_url)
this.webviewUrl = `/static/html/ezviz-iframe.html?accessToken=${token}&playUrl=${url}`
+ // #endif
+
+ // #ifdef H5
+ this.iframeUrl = 'https://open.ys7.com/ezopen/h5/iframe?' +
+ 'url=' + encodeURIComponent(this.config.play_url) +
+ '&accessToken=' + encodeURIComponent(this.config.accessToken) +
+ '&width=100%' +
+ '&height=100%' +
+ '&autoplay=1' +
+ '&audio=1' +
+ '&controls=1'
+ // #endif
+
this.isPlaying = true
this.status = '播放中'
@@ -244,16 +329,27 @@ export default {
.video-webview {
width: 100%;
- height: 50%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
}
-.h5-tip {
- display: flex;
- align-items: center;
- justify-content: center;
+/* H5平台iframe容器 */
+.h5-iframe-container {
+ width: 100%;
height: 100%;
- color: white;
- font-size: 28rpx;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background: #000;
+}
+
+.h5-iframe {
+ width: 100%;
+ height: 100%;
+ border: none;
+ display: block;
}
.loading {
@@ -300,6 +396,36 @@ export default {
border-radius: 10rpx;
}
+/* 错误状态下的控制按钮 */
+.error-controls {
+ display: flex;
+ gap: 20rpx;
+ margin-top: 30rpx;
+ justify-content: center;
+}
+
+.error-controls .control-btn {
+ flex: 1;
+ max-width: 200rpx;
+ height: 60rpx;
+ border-radius: 8rpx;
+ font-size: 24rpx;
+ font-weight: bold;
+ color: white;
+ border: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.error-controls .play-btn {
+ background: linear-gradient(135deg, #46a049 0%, #4caf50 100%);
+}
+
+.error-controls .refresh-btn {
+ background: linear-gradient(135deg, #2196f3 0%, #21cbf3 100%);
+}
+
/* 控制按钮 */
.control-buttons {
position: absolute;
diff --git a/src/pages/environment/index.vue b/src/pages/environment/index.vue
index 5d34062..cb19f3d 100644
--- a/src/pages/environment/index.vue
+++ b/src/pages/environment/index.vue
@@ -24,9 +24,9 @@
-
+
@@ -44,9 +44,9 @@
-
+
@@ -64,22 +64,39 @@
-
+
-
+
+
+
+
+
+
+
+
@@ -259,27 +276,27 @@ export default {
cleanlinessProgress: 0,
lastUpdate: '暂无数据',
temperatureRange: {
- min: 25,
- max: 35
+ min: 0,
+ max: 0
},
humidityRange: {
- min: 40,
- max: 70
+ min: 0,
+ max: 0
},
tempSettings: {
- min: 25,
- max: 35
+ min: 0,
+ max: 0
},
humiditySettings: {
- min: 40,
- max: 70
+ min: 0,
+ max: 0
},
connectionStatus: {
isConnected: false,
lastUpdate: null
},
- targetTemperature: 30,
- targetHumidity: 50, // 空调设定湿度
+ targetTemperature: 0,
+ targetHumidity: 0, // 空调设定湿度
// 温度输入相关
tempInputValue: '',
tempValidationMessage: '',
@@ -295,36 +312,32 @@ export default {
alertHistory: [], // 报警历史记录
// 系统启动事件相关
hasCreatedStartupEvent: false, // 是否已创建启动事件
+ acStatus: 4,
+ // 0待机1启动中2运行中3关机中, 没有就默认连接中
+ acStatusList: ['待机', '启动中', '运行中', '关机中', '连接中'],
+ // 空调控制相关
+ acControlLoading: false, // 空调控制按钮加载状态
}
},
onLoad() {
console.log('环境参数页面加载')
- // 从本地存储读取是否已创建启动事件的状态
- const hasCreatedStartupEvent = uni.getStorageSync('hasCreatedStartupEvent')
- if (hasCreatedStartupEvent) {
- this.hasCreatedStartupEvent = true
- console.log('📱 从本地存储读取到启动事件状态: 已创建')
- }
+ // // 从本地存储读取是否已创建启动事件的状态
+ // const hasCreatedStartupEvent = uni.getStorageSync('hasCreatedStartupEvent')
+ // if (hasCreatedStartupEvent) {
+ // this.hasCreatedStartupEvent = true
+ // console.log('📱 从本地存储读取到启动事件状态: 已创建')
+ // }
this.initMqttListener();
- this.init();
- // // 获取最新空调温度
- // this.getLatestAirConditionerTemperature()
- // // 获取最新温湿度数据
- // this.getLatestWsdData()
- // // 首次进入系统时创建启动事件
- // this.createStartupEventIfNeeded()
-
- // // 获取温湿度区间设置
- // this.loadWsdSettings()
},
onShow() {
- console.log('📱 环境参数页面显示,触发页面更新')
- // 只有在非首次显示时才重新获取最新空调温度
- if (this.hasCreatedStartupEvent) {
- this.init();
- }
+ console.log('📱 环境参数页面显示,触发页面更新');
+ this.init();
+ // // 只有在非首次显示时才重新获取最新空调温度
+ // if (this.hasCreatedStartupEvent) {
+ // this.init();
+ // }
},
onUnload() {
// 页面卸载时移除监听器
@@ -413,12 +426,12 @@ export default {
if (response && response.id) {
// 更新温度和湿度控制范围
this.temperatureRange = {
- min: response.minTemperature || 25,
- max: response.maxTemperature || 35
+ min: response.minTemperature || 0,
+ max: response.maxTemperature || 0
}
this.humidityRange = {
- min: response.minHumidity || 40,
- max: response.maxHumidity || 70
+ min: response.minHumidity || 0,
+ max: response.maxHumidity || 0
}
// 同时更新设置弹窗中的临时变量
@@ -433,8 +446,8 @@ export default {
} catch (error) {
console.error('❌ 温湿度区间设置加载失败:', error)
// 使用默认值
- this.temperatureRange = { min: 25, max: 35 }
- this.humidityRange = { min: 40, max: 70 }
+ this.temperatureRange = { min: 0, max: 0 }
+ this.humidityRange = { min: 0, max: 0 }
this.tempSettings = { ...this.temperatureRange }
this.humiditySettings = { ...this.humidityRange }
console.log('🔄 使用默认温湿度区间设置')
@@ -444,20 +457,18 @@ export default {
// 获取最新空调温湿度参数
async getLatestAirConditionerTemperature() {
try {
- console.log('🌡️ 开始获取最新空调温湿度参数...')
const res = await thDataApi.getLatest();
if (res.status === 'success') {
// 从接口获取温度和湿度设定值
- this.targetTemperature = res.temperature || 30;
- this.targetHumidity = res.humidity || 50;
+ this.targetTemperature = res.temperature || 0;
+ this.targetHumidity = res.humidity || 0;
}
- console.log('✅ 最新空调温湿度参数:', res)
} catch (error) {
console.error('❌ 获取最新空调温湿度参数失败:', error)
// 接口失败时使用默认值
- this.targetTemperature = 30;
- this.targetHumidity = 50;
+ this.targetTemperature = 0;
+ this.targetHumidity = 0;
}
},
// 获取最新温湿度数据
@@ -465,13 +476,10 @@ export default {
try {
const res = await wsdApi.getLatest();
if (res.status === 'success') {
- // this.temperature = res.wd || 30;
- // this.humidity = res.sd || 50
- // this.cleanliness = res.pm || 0;
this.updateEnvironmentData({
deviceType: 'WSD',
- temperature: res.wd || 30,
- humidity: res.sd || 50,
+ temperature: res.wd || 0,
+ humidity: res.sd || 0,
cleanliness: res.pm || 0,
})
}
@@ -515,12 +523,8 @@ export default {
// 更新环境数据
updateEnvironmentData(data) {
- // 处理空调故障状态
- if (data.deviceType === 'AC' && data.faultStatus !== undefined) {
- this.acFaultStatus = data.faultStatus
- }
-
- // 只处理WSD设备的数据
+ console.log('============data', data)
+ // 处理WSD设备的数据
if (data.deviceType === 'WSD') {
if (data.temperature !== undefined) {
this.temperature = parseFloat(data.temperature.toFixed(1))
@@ -531,18 +535,31 @@ export default {
this.humidity = parseFloat(data.humidity.toFixed(1))
this.humidityProgress = Math.min(Math.max(this.humidity, 0), 100)
}
-
- if (data.cleanliness !== undefined) {
- this.cleanliness = parseFloat(data.cleanliness.toFixed(1))
- this.cleanlinessProgress = Math.min(Math.max(this.cleanliness, 0), 100)
- }
this.lastUpdate = data.time || new Date().toLocaleString('zh-CN')
// 检查报警条件,传入MQTT原始数据
this.checkAlerts(data)
+ } else if (data.deviceType === 'AC') {
+ // 处理空调AC数据
+ if (data.faultStatus !== undefined) {
+ this.acFaultStatus = data.faultStatus // 空调故障
+ }
+ if (data.rawData.status !== undefined) {
+ this.acStatus = data.rawData.status
+ }
+ if (data.rawData.ctrlword !== undefined) {
+ this.acCtrlword = data.rawData.ctrlword
+ }
+ } else if (data.deviceType === 'PM25') {
+ // 处理PM25数据
+ if (data.rawData.PM25 !== undefined) {
+ this.cleanliness = data.rawData.PM25;
+ this.cleanlinessProgress = Math.min(Math.max(this.cleanliness, 0), 100)
+ }
} else {
- console.log('⚠️ 非WSD设备数据,跳过更新:', data.deviceType)
+ console.log('⚠️ 非WSD、AC、PM25设备数据,跳过更新:', data.deviceType)
+ // 处理其他设备数据
}
},
@@ -566,13 +583,20 @@ export default {
if (mqttData.deviceType !== 'WSD') {
return
}
+ console.log('====mqttData', mqttData)
+ // {
+ // "deviceType": "WSD",
+ // "temperature": 0,
+ // "humidity": 0,
+ // "cleanliness": 0
+ // }
// 获取MQTT原始数据
const mqttTemperature = mqttData.temperature
const mqttHumidity = mqttData.humidity
// 1. 温度报警:使用环境控制设置的区间(使用MQTT原始数据)
- if (mqttTemperature !== undefined && (mqttTemperature < this.temperatureRange.min || mqttTemperature > this.temperatureRange.max)) {
+ if (mqttTemperature !== undefined && mqttTemperature !== 0 && (mqttTemperature < this.temperatureRange.min || mqttTemperature > this.temperatureRange.max)) {
const alert = {
// content: `温度传感器异常,读数${mqttTemperature}°C超出控制范围${this.temperatureRange.min}°C-${this.temperatureRange.max}°C`,
content: `温度超出控制范围`,
@@ -660,21 +684,21 @@ export default {
// 记录报警到控制台并调用创建告警接口
async logAlert(alert) {
console.log('🚨 报警触发:', JSON.stringify(alert, null, 2))
- this.alertHistory.push(alert)
+ // this.alertHistory.push(alert)
- // 调用创建告警接口
- try {
- console.log('📤 正在创建告警记录...')
- const response = await alertApi.create(alert)
- console.log('✅ 告警记录创建成功:', response)
- } catch (error) {
- console.error('❌ 告警记录创建失败:', error)
- }
+ // // 调用创建告警接口
+ // try {
+ // console.log('📤 正在创建告警记录...')
+ // const response = await alertApi.create(alert)
+ // console.log('✅ 告警记录创建成功:', response)
+ // } catch (error) {
+ // console.error('❌ 告警记录创建失败:', error)
+ // }
- // 限制报警历史记录数量,避免内存溢出
- if (this.alertHistory.length > 100) {
- this.alertHistory = this.alertHistory.slice(-50)
- }
+ // // 限制报警历史记录数量,避免内存溢出
+ // if (this.alertHistory.length > 100) {
+ // this.alertHistory = this.alertHistory.slice(-50)
+ // }
},
// 降低目标温度
@@ -875,7 +899,7 @@ export default {
this.humidityValidationClass = ''
},
- // 显示湿度变化提示
+ // 空调湿度更新
showHumidityChangeToast() {
// 发送空调参数到MQTT
this.sendAirConditionerParams()
@@ -889,110 +913,156 @@ export default {
this.checkAlerts(mockMqttData)
},
- // 显示温度变化提示
- showTemperatureChangeToast() {
- // uni.showToast({
- // title: `目标温度: ${this.targetTemperature}°C`,
- // icon: 'success',
- // duration: 1500
- // })
-
- // 发送空调参数到MQTT
- this.sendAirConditionerParams()
-
- // 温度变化后检查报警(使用当前页面数据模拟MQTT数据)
- const mockMqttData = {
- deviceType: 'WSD',
- temperature: this.temperature,
- humidity: this.humidity
+ // 空调温度更新
+ showTemperatureChangeToast() {
+ // 发送空调参数到MQTT
+ this.sendAirConditionerParams()
+
+ // 温度变化后检查报警(使用当前页面数据模拟MQTT数据)
+ const mockMqttData = {
+ deviceType: 'WSD',
+ temperature: this.temperature,
+ humidity: this.humidity
+ }
+ this.checkAlerts(mockMqttData)
+ },
+
+ // 发送空调参数
+ async sendAirConditionerParams() {
+ try {
+ // 根据MQTT文档,空调温度使用BSQWD,但控制指令可能使用不同的TagName
+ // 发送温度参数 - 使用BSQWD作为TagName(与接收数据保持一致)
+ const temperatureData = {
+ "TagValue": this.targetTemperature,
+ "TagName": "JS_COD", // 使用与接收数据一致的TagName
+ "method": "setValue"
}
- this.checkAlerts(mockMqttData)
- },
- // 发送空调参数
- async sendAirConditionerParams() {
+ // 发送湿度参数 - 根据文档WSD设备湿度使用SD
+ const humidityData = {
+ "TagValue": this.targetHumidity,
+ "TagName": "JS_SD", // 使用与WSD设备湿度一致的TagName
+ "method": "setValue"
+ }
+
+ console.log('🌡️ 发送空调温度参数:', temperatureData)
+ console.log('💧 发送空调湿度参数:', humidityData)
+
+ // 调用发送MQTT数据的方法
+ const tempSuccess = sendMqttData(temperatureData)
+ const humiditySuccess = sendMqttData(humidityData)
+
+ if (tempSuccess && humiditySuccess) {
+ console.log('✅ 空调温湿度参数MQTT发送请求已提交')
+
+ // 发送成功后调用提交温湿度数据API
try {
- // 根据MQTT文档,空调温度使用BSQWD,但控制指令可能使用不同的TagName
- // 发送温度参数 - 使用BSQWD作为TagName(与接收数据保持一致)
- const temperatureData = {
- "TagValue": this.targetTemperature,
- "TagName": "JS_COD", // 使用与接收数据一致的TagName
- "method": "setValue"
- }
-
- // 发送湿度参数 - 根据文档WSD设备湿度使用SD
- const humidityData = {
- "TagValue": this.targetHumidity,
- "TagName": "JS_SD", // 使用与WSD设备湿度一致的TagName
- "method": "setValue"
- }
-
- console.log('🌡️ 发送空调温度参数:', temperatureData)
- console.log('💧 发送空调湿度参数:', humidityData)
-
- // 调用发送MQTT数据的方法
- const tempSuccess = sendMqttData(temperatureData)
- const humiditySuccess = sendMqttData(humidityData)
-
- if (tempSuccess && humiditySuccess) {
- console.log('✅ 空调温湿度参数MQTT发送请求已提交')
-
- // 发送成功后调用提交温湿度数据API
- try {
- await this.submitTemperatureData()
- // 显示成功提示
- uni.showToast({
- title: '参数设置成功',
- icon: 'success',
- duration: 1500
- })
- } catch (apiError) {
- // MQTT发送成功,但接口保存失败
- console.warn('⚠️ MQTT发送成功,但接口保存失败:', apiError)
- uni.showToast({
- title: 'MQTT已发送,接口保存失败',
- icon: 'none',
- duration: 2500
- })
- }
-
- } else {
- console.error('❌ 空调参数MQTT发送失败')
- uni.showToast({
- title: 'MQTT发送失败',
- icon: 'error',
- duration: 2000
- })
- }
- } catch (error) {
- console.error('❌ 发送空调参数异常:', error)
+ await this.submitTemperatureData()
+ // 显示成功提示
uni.showToast({
- title: '参数设置失败',
- icon: 'error',
- duration: 2000
+ title: '参数设置成功',
+ icon: 'success',
+ duration: 1500
+ })
+ } catch (apiError) {
+ // MQTT发送成功,但接口保存失败
+ console.warn('⚠️ MQTT发送成功,但接口保存失败:', apiError)
+ uni.showToast({
+ title: 'MQTT已发送,接口保存失败',
+ icon: 'none',
+ duration: 2500
})
}
- },
+
+ } else {
+ console.error('❌ 空调参数MQTT发送失败')
+ uni.showToast({
+ title: 'MQTT发送失败',
+ icon: 'error',
+ duration: 2000
+ })
+ }
+ } catch (error) {
+ console.error('❌ 发送空调参数异常:', error)
+ uni.showToast({
+ title: '参数设置失败',
+ icon: 'error',
+ duration: 2000
+ })
+ }
+ },
+
+ // 空调开机控制
+ async turnOnAirConditioner() {
+ await this.sendAirConditionerControl(1, '开机')
+ },
+
+ // 空调关机控制
+ async turnOffAirConditioner() {
+ await this.sendAirConditionerControl(2, '关机')
+ },
+
+ // 发送空调控制指令
+ async sendAirConditionerControl(ctrlValue, actionName) {
+ this.acControlLoading = true;
+ try {
+ // 构建控制指令数据
+ const controlData = {
+ "TagValue": ctrlValue,
+ "TagName": "ctrlword",
+ "method": "setValue"
+ }
- // 提交温湿度数据
- async submitTemperatureData() {
- try {
- const temperatureHumidityData = {
- temperature: this.targetTemperature,
- humidity: this.targetHumidity,
- deviceId: "TH_SENSOR_001",
- timestamp: new Date().toISOString(),
- source: "manual_setting" // 标识为手动设置
- }
- console.log('📤 提交温湿度数据到接口:', temperatureHumidityData)
- const response = await thDataApi.submit(temperatureHumidityData)
- console.log('✅ 温湿度数据接口提交成功:', response)
- } catch (error) {
- console.error('❌ 温湿度数据接口提交失败:', error)
- // 接口失败不影响MQTT发送,只记录日志
- throw error // 重新抛出错误,让调用方知道接口失败了
- }
- },
+ console.log(`🔧 发送空调${actionName}指令:`, controlData)
+
+ // 调用发送MQTT数据的方法
+ const success = sendMqttData(controlData)
+ this.acControlLoading = false;
+ if (success) {
+ // 显示成功提示
+ uni.showToast({
+ title: `${actionName}指令已发送`,
+ icon: 'success',
+ duration: 1500
+ })
+ } else {
+ uni.showToast({
+ title: `${actionName}失败`,
+ icon: 'error',
+ duration: 2000
+ })
+ }
+ } catch (error) {
+ uni.showToast({
+ title: `${actionName}异常`,
+ icon: 'error',
+ duration: 2000
+ })
+ } finally {
+ // 清除加载状态
+ this.acControlLoading = false
+ }
+ },
+
+ // 提交温湿度数据
+ async submitTemperatureData() {
+ try {
+ const temperatureHumidityData = {
+ temperature: this.targetTemperature,
+ humidity: this.targetHumidity,
+ deviceId: "TH_SENSOR_001",
+ timestamp: new Date().toISOString(),
+ source: "manual_setting" // 标识为手动设置
+ }
+ console.log('📤 提交温湿度数据到接口:', temperatureHumidityData)
+ const response = await thDataApi.submit(temperatureHumidityData)
+ console.log('✅ 温湿度数据接口提交成功:', response)
+ } catch (error) {
+ console.error('❌ 温湿度数据接口提交失败:', error)
+ // 接口失败不影响MQTT发送,只记录日志
+ throw error // 重新抛出错误,让调用方知道接口失败了
+ }
+ },
// 手动重连MQTT
manualReconnect() {
@@ -1449,6 +1519,58 @@ export default {
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
}
+/* 空调开关控制按钮样式 */
+.ac-power-controls {
+ display: flex;
+ justify-content: center;
+ gap: 20rpx;
+ background: #ffffff;
+ border-radius: 8rpx;
+ padding: 20rpx;
+ border: 1rpx solid #e1e5e9;
+ margin-bottom: 12rpx;
+}
+
+.ac-power-btn {
+ flex: 1;
+ max-width: 120rpx;
+ // height: 60rpx;
+ border-radius: 6rpx;
+ border: 1rpx solid;
+ font-size: 28rpx;
+ font-weight: 500;
+ transition: all 0.2s ease;
+}
+
+.power-on {
+ background: #27ae60;
+ color: white;
+ border-color: #27ae60;
+}
+
+.power-on:active {
+ background: #229954;
+ border-color: #229954;
+}
+
+.power-off {
+ background: #e74c3c;
+ color: white;
+ border-color: #e74c3c;
+}
+
+.power-off:active {
+ background: #c0392b;
+ border-color: #c0392b;
+}
+
+.ac-power-btn:disabled {
+ background: #bdc3c7;
+ color: #7f8c8d;
+ border-color: #bdc3c7;
+ opacity: 0.6;
+}
+
.ac-temp-display {
display: flex;
align-items: baseline;
diff --git a/src/pages/visual/index.vue b/src/pages/visual/index.vue
index 1a5a4a6..f3ed819 100644
--- a/src/pages/visual/index.vue
+++ b/src/pages/visual/index.vue
@@ -25,31 +25,6 @@
-
-
-
- 📡 设备状态
- 在线
-
-
- 🎥 分辨率
- 高清
-
-
- 🔊 音频
- 开启
-
-
-
-
-
-
-
-
@@ -136,55 +111,6 @@ export default {
})
},
- // 初始化播放器
- async handleInitPlayer() {
- console.log('🔄 重新初始化播放器...')
-
- uni.showLoading({
- title: '正在初始化...'
- })
-
- try {
- // 重新获取视频数据并初始化
- await this.getVideoData()
-
- uni.hideLoading()
- uni.showToast({
- title: '初始化成功',
- icon: 'success',
- duration: 2000
- })
-
- // 重置播放状态
- this.isPlaying = true
-
- } catch (error) {
- uni.hideLoading()
- console.error('初始化失败:', error)
- uni.showToast({
- title: '初始化失败',
- icon: 'error',
- duration: 2000
- })
- }
- },
-
- // 切换播放/暂停
- handleTogglePlay() {
- console.log('🎬 切换播放状态:', this.isPlaying ? '暂停' : '播放')
-
- if (this.$refs.playerVideoRef) {
- // 调用播放器组件的切换播放方法(状态会通过事件同步)
- this.$refs.playerVideoRef.togglePlay()
- } else {
- console.error('❌ 播放器组件未找到')
- uni.showToast({
- title: '播放器未就绪',
- icon: 'error',
- duration: 2000
- })
- }
- },
// 处理播放状态变化(由播放器组件触发)
handlePlayStateChange(isPlaying) {
diff --git a/src/utils/mqttDataManager.js b/src/utils/mqttDataManager.js
index 9ba69fb..d6dbe70 100644
--- a/src/utils/mqttDataManager.js
+++ b/src/utils/mqttDataManager.js
@@ -9,7 +9,14 @@ class MqttDataManager {
temperature: null,
humidity: null,
pm25: null,
- timestamp: null
+ timestamp: null,
+ // 扩展数据结构支持更多设备类型
+ deviceType: null,
+ rawData: null,
+ // 空调设备数据
+ acTemperature: null,
+ // 其他设备数据可在此扩展
+ otherDeviceData: {}
}
this.init()
}
@@ -95,41 +102,65 @@ class MqttDataManager {
// console.log('设备数据:', deviceDataContent)
// console.log('时间戳:', timestamp)
- // 根据设备类型处理数据
- if (deviceType === 'WSD') {
- // console.log('✅ 处理WSD设备数据 - 更新环境参数')
- this.processWSDData(deviceDataContent, timestamp)
- } else {
- // console.log(`⚠️ 设备类型 ${deviceType} 暂不处理,仅打印到控制台`)
- // console.log('设备详情:', {
- // deviceType,
- // data: deviceDataContent,
- // timestamp: new Date(timestamp * 1000).toLocaleString('zh-CN')
- // })
- }
+ // 处理所有设备类型的数据
+ console.log(`✅ 处理设备类型: ${deviceType}`)
+ this.processAllDeviceData(deviceType, deviceDataContent, timestamp)
} catch (error) {
console.error('❌ 处理设备数据失败:', error)
}
}
- // 处理WSD设备数据
- processWSDData(data, timestamp) {
+ // 处理所有设备类型的数据
+ processAllDeviceData(deviceType, data, timestamp) {
try {
- // 解析WSD数据 - 根据您提供的数据结构,WD是温度,SD是湿度
- const temperature = data.WD && parseFloat(data.WD);
- const humidity = data.SD && parseFloat(data.SD);
+ console.log(`🌡️ ${deviceType}设备数据解析:`)
+ console.log('设备数据:', data)
- // console.log('🌡️ WSD数据解析:')
- // console.log('温度(WD):', temperature)
- // console.log('湿度(SD):', humidity)
-
- // 构建解析后的数据
+ // 构建基础解析数据
const parsedData = {
- deviceType: 'WSD',
+ deviceType,
timestamp,
time: new Date(timestamp * 1000).toLocaleString('zh-CN'),
- temperature,
- humidity
+ rawData: data // 保存原始数据供页面特殊处理
+ }
+
+ // 根据设备类型解析特定数据
+ switch (deviceType) {
+ case 'WSD': // 温湿度传感器
+ if (data.WD !== undefined) {
+ parsedData.temperature = parseFloat(data.WD)
+ }
+ if (data.SD !== undefined) {
+ parsedData.humidity = parseFloat(data.SD)
+ }
+ break
+
+ case 'AC': // 空调设备
+ if (data.BSQWD !== undefined) {
+ parsedData.acTemperature = parseFloat(data.BSQWD)
+ }
+
+ if (data.SD !== undefined) {
+ parsedData.acHumidity = parseFloat(data.SD)
+ }
+
+ if (data.status !== undefined) {
+ parsedData.acStatus = data.status
+ }
+ // 可以添加其他空调参数
+ break
+
+ case 'PM': // PM2.5传感器
+ if (data.PM25 !== undefined) {
+ parsedData.pm25 = parseFloat(data.PM25)
+ }
+ break
+
+ default:
+ // 其他设备类型,保存到otherDeviceData中
+ console.log(`📦 处理未知设备类型 ${deviceType},保存原始数据`)
+ // parsedData.otherDeviceData = data
+ break
}
// 更新最新数据
@@ -138,9 +169,9 @@ class MqttDataManager {
// 通知所有监听器
this.notifyListeners('dataUpdate', parsedData)
- // console.log('✅ WSD数据处理完成:', parsedData)
+ console.log(`✅ ${deviceType}数据处理完成:`, parsedData)
} catch (error) {
- console.error('❌ 处理WSD数据失败:', error)
+ console.error(`❌ 处理${deviceType}数据失败:`, error)
}
}
@@ -198,16 +229,18 @@ class MqttDataManager {
// 更新最新数据
updateLastData(parsedData) {
- if (parsedData.temperature !== undefined) {
- this.lastData.temperature = parsedData.temperature
- }
- if (parsedData.humidity !== undefined) {
- this.lastData.humidity = parsedData.humidity
- }
- if (parsedData.pm25 !== undefined) {
- this.lastData.pm25 = parsedData.pm25
- }
- this.lastData.timestamp = parsedData.timestamp
+ // 直接合并数据,只更新有值的字段
+ Object.keys(parsedData).forEach(key => {
+ if (parsedData[key] !== undefined) {
+ if (key === 'otherDeviceData' && this.lastData.otherDeviceData) {
+ // 对于 otherDeviceData,进行深度合并
+ this.lastData.otherDeviceData = { ...this.lastData.otherDeviceData, ...parsedData.otherDeviceData }
+ } else {
+ // 其他字段直接赋值
+ this.lastData[key] = parsedData[key]
+ }
+ }
+ })
}
// 添加数据监听器
diff --git a/萤石云APP对接完整指南.md b/萤石云APP对接完整指南.md
index dc3a5bb..074af86 100644
--- a/萤石云APP对接完整指南.md
+++ b/萤石云APP对接完整指南.md
@@ -271,591 +271,21 @@ src/
### 1. 播放器组件 (`EzvizVideoPlayerSimple.vue`)
-```vue
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
----
-
### 2. iframe HTML (`ezviz-iframe.html`)
-```html
-
-
-
-
-
- 萤石云播放器
-
-
-
- 正在加载播放器...
-
-
-
-
-
-```
-
----
-
### 3. 监控页面 (`pages/visual/index.vue`)
-```vue
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 📹
- 暂无监控数据
-
-
-
-
-
- 📡 设备状态
- 在线
-
-
- 🎥 分辨率
- 高清
-
-
- 🔊 音频
- 开启
-
-
-
-
-
-
-
-
-
-```
-
----
-
### 4. AccessToken 管理 (`utils/ezvizTokenManager.js`)
-```javascript
-// 萤石云 AccessToken 管理器
-class EzvizTokenManager {
- constructor() {
- this.appKey = 'your-app-key'
- this.appSecret = 'your-app-secret'
- this.baseUrl = 'https://open.ys7.com/api/lapp'
- }
-
- // 获取有效的 AccessToken
- async getValidAccessToken() {
- // 1. 先从缓存读取
- const cached = uni.getStorageSync('ezviz_access_token')
- const expireTime = uni.getStorageSync('ezviz_token_expire')
-
- // 2. 检查是否过期(提前1小时刷新)
- const now = Date.now()
- if (cached && expireTime && expireTime - now > 3600000) {
- console.log('使用缓存的AccessToken')
- return cached
- }
-
- // 3. 缓存失效,重新获取
- console.log('重新获取AccessToken')
- return await this.fetchAccessToken()
- }
-
- // 从萤石云服务器获取 AccessToken
- async fetchAccessToken() {
- return new Promise((resolve, reject) => {
- uni.request({
- url: `${this.baseUrl}/token/get`,
- method: 'POST',
- header: {
- 'Content-Type': 'application/x-www-form-urlencoded'
- },
- data: {
- appKey: this.appKey,
- appSecret: this.appSecret
- },
- success: (res) => {
- if (res.data.code === '200') {
- const accessToken = res.data.data.accessToken
- const expireTime = Date.now() + (res.data.data.expireTime * 1000)
-
- // 缓存 token
- uni.setStorageSync('ezviz_access_token', accessToken)
- uni.setStorageSync('ezviz_token_expire', expireTime)
-
- console.log('✅ AccessToken获取成功')
- resolve(accessToken)
- } else {
- reject(new Error(res.data.msg || '获取AccessToken失败'))
- }
- },
- fail: (error) => {
- reject(error)
- }
- })
- })
- }
-}
-
-export default new EzvizTokenManager()
-```
-
----
-
## ⚙️ 配置说明
### 1. pages.json(横屏配置)
-```json
-{
- "pages": [
- {
- "path": "pages/visual/index",
- "style": {
- "navigationBarTitleText": "移动式检修车间",
- "navigationStyle": "custom",
- "pageOrientation": "landscape" // ← 横屏展示
- }
- }
- ]
-}
-```
-
----
-
### 2. manifest.json(内存配置)
-```json
-{
- "app-plus": {
- "compatible": {
- "largeHeap": true // ← 启用大内存堆(512MB)
- }
- }
-}
-```
-
----
-
### 3. 萤石云参数说明
#### AccessToken 获取
-```javascript
-// API: https://open.ys7.com/api/lapp/token/get
-// 方法: POST
-// 参数:
-{
- appKey: "your-app-key",
- appSecret: "your-app-secret"
-}
-
-// 返回:
-{
- code: "200",
- data: {
- accessToken: "at.xxx...",
- expireTime: 7200 // 秒,默认2小时
- }
-}
-```
-
#### ezopen 播放地址格式
```
ezopen://open.ys7.com/{设备序列号}/{通道号}.{清晰度}.live
@@ -1223,3 +653,5 @@ onHide() {
🎉 **恭喜!萤石云APP对接完成!** 🎉
+
+