feat:空调开关、状态

This commit is contained in:
吉浩茹
2025-10-09 17:22:26 +08:00
parent 4b65bea0bb
commit 279cc4a5ea
6 changed files with 518 additions and 877 deletions

View File

@ -17,13 +17,33 @@
></web-view>
<!-- #endif -->
<!-- H5平台提示 -->
<!-- H5平台直接使用iframe -->
<!-- #ifdef H5 -->
<view class="h5-tip">H5平台暂不支持</view>
<view v-if="iframeUrl" class="h5-iframe-container">
<iframe
:src="iframeUrl"
class="h5-iframe"
allow="autoplay; fullscreen"
allowfullscreen
></iframe>
</view>
<!-- #endif -->
<!-- 控制按钮 -->
<view class="control-buttons" v-if="!loading && !error">
<!-- APP平台使用 cover-view -->
<!-- #ifdef APP-PLUS -->
<cover-view class="control-buttons-cover" v-if="!loading && webviewUrl">
<cover-view class="control-btn-cover play-btn-cover" @click="togglePlay">
<cover-view class="btn-text">{{ isPlaying ? '⏸ 暂停' : '▶ 播放' }}</cover-view>
</cover-view>
<cover-view class="control-btn-cover refresh-btn-cover" @click="refresh">
<cover-view class="btn-text">🔄 刷新</cover-view>
</cover-view>
</cover-view>
<!-- #endif -->
<!-- H5平台使用普通按钮 -->
<!-- #ifdef H5 -->
<view class="control-buttons" v-if="!loading && iframeUrl">
<button class="control-btn play-btn" @click="togglePlay">
{{ isPlaying ? ' 暂停' : ' 播放' }}
</button>
@ -31,6 +51,7 @@
🔄 刷新
</button>
</view>
<!-- #endif -->
<view v-if="loading" class="loading">
<text>{{ loadingText }}</text>
@ -39,6 +60,16 @@
<view v-if="error" class="error">
<text>{{ errorText }}</text>
<button @click="retry">重试</button>
<!-- 即使有错误也显示控制按钮 -->
<view class="error-controls">
<button class="control-btn play-btn" @click="togglePlay">
{{ isPlaying ? ' 暂停' : ' 播放' }}
</button>
<button class="control-btn refresh-btn" @click="refresh">
🔄 刷新
</button>
</view>
</view>
</view>
</template>
@ -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;

View File

@ -24,9 +24,9 @@
<view class="detail-progress-bar">
<view class="detail-progress-fill temperature-progress" :style="{ width: temperatureProgress + '%' }"></view>
</view>
<view class="detail-range">
<!-- <view class="detail-range">
<text class="detail-range-text">{{ 0 }}°C - {{ 100 }}°C</text>
</view>
</view> -->
</view>
<!-- 湿度卡片 -->
@ -44,9 +44,9 @@
<view class="detail-progress-bar">
<view class="detail-progress-fill humidity-progress" :style="{ width: humidityProgress + '%' }"></view>
</view>
<view class="detail-range">
<!-- <view class="detail-range">
<text class="detail-range-text">{{ 0 }}% - {{ 100 }}%</text>
</view>
</view> -->
</view>
<!-- 洁净度卡片 -->
@ -64,22 +64,39 @@
<view class="detail-progress-bar">
<view class="detail-progress-fill cleanliness-progress" :style="{ width: cleanlinessProgress + '%' }"></view>
</view>
<view class="detail-range">
<!-- <view class="detail-range">
<text class="detail-range-text">{{ 0 }}% - {{ 100 }}%</text>
</view>
</view> -->
</view>
</view>
<!-- 空调目标参数设置 -->
<!-- 空调设置 -->
<view class="air-conditioner-settings">
<view class="ac-header">
<view class="ac-title-container">
<view class="ac-icon"></view>
<text class="ac-title">空调目标参数设置</text>
<text class="ac-title">空调设置</text>
</view>
<view class="ac-status-indicator">
<view class="status-dot active"></view>
<text class="status-label">运行中</text>
<text class="status-label">{{ acStatusList[acStatus] }}</text>
</view>
</view>
<!-- 空调开关控制卡片 -->
<view class="ac-control-card power-card">
<view class="control-header">
<view class="control-icon-container">
<text class="control-label">设备控制</text>
</view>
</view>
<view class="ac-power-controls">
<button class="ac-power-btn power-on" @click="turnOnAirConditioner" :disabled="acControlLoading">
开机
</button>
<button class="ac-power-btn power-off" @click="turnOffAirConditioner" :disabled="acControlLoading">
关机
</button>
</view>
</view>
@ -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;

View File

@ -25,31 +25,6 @@
</view>
</view>
<!-- 视频信息区域 -->
<view v-if="ezstate" class="video-info">
<view class="info-item">
<text class="info-label">📡 设备状态</text>
<text class="info-value online">在线</text>
</view>
<view class="info-item">
<text class="info-label">🎥 分辨率</text>
<text class="info-value">高清</text>
</view>
<view class="info-item">
<text class="info-label">🔊 音频</text>
<text class="info-value">开启</text>
</view>
</view>
<!-- 视频控制按钮 -->
<view v-if="ezstate" class="control-section">
<button @click="handleInitPlayer" class="control-btn init-btn">
🔄 初始化
</button>
<button @click="handleTogglePlay" class="control-btn pause-btn">
{{ isPlaying ? '⏸ 暂停播放' : '▶ 开始播放' }}
</button>
</view>
<!-- API测试按钮 -->
<view class="test-section" v-if="!ezstate">
@ -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) {

View File

@ -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]
}
}
})
}
// 添加数据监听器