8.7 KiB
8.7 KiB
报警解除状态 - 基于ESP32的isActive字段
🎯 核心逻辑
利用ESP32已有的 isActive 字段来判断报警是否已解除:
isActive: true= 报警正在持续中isActive: false= 报警已自动解除
📊 ESP32数据格式
ESP32的报警历史API (/api/alarm/history) 已经包含 isActive 字段:
{
"alarms": [
{
"id": 1,
"title": "燃烧报警报警",
"desc": "热源持续升温,温度超过阈值",
"temp": 65.5,
"time": "14:26:10",
"date": "01-22",
"location": "厨房区域",
"isActive": false, // ✅ 关键字段:false表示已解除
"timestamp_ms": 1737548770000
},
{
"id": 2,
"title": "无人值守风险",
"desc": "热源持续升温,厨房无人5分钟",
"temp": 45.2,
"time": "21:30:00",
"date": "01-22",
"location": "厨房区域",
"isActive": true, // ⚠️ 仍在持续
"timestamp_ms": 1737548400000
}
],
"anyAlarmActive": true
}
🔧 前端实现逻辑
1. 解除状态判断
在 pages/kitchen/history.vue 的 parseAlarmHistory 方法中:
// 1. 优先使用ESP32的isActive字段判断(isActive=false表示已解除)
if (alarm.hasOwnProperty('isActive') && alarm.isActive === false) {
resolved = true
resolvedType = 'auto' // ESP32自动检测解除
// 如果ESP32提供了解除时间,使用它
if (alarm.resolvedAt || alarm.resolved_at || alarm.resolvedTimestamp) {
const resolvedTimestamp = alarm.resolvedAt || alarm.resolved_at || alarm.resolvedTimestamp
const resolvedDate = new Date(typeof resolvedTimestamp === 'number' ? resolvedTimestamp : Date.parse(resolvedTimestamp))
resolvedTime = resolvedDate.getHours() + ':' + String(resolvedDate.getMinutes()).padStart(2, '0')
} else {
// 如果没有解除时间,显示为"已解除"(不显示具体时间)
resolvedTime = ''
}
}
2. UI显示
在报警卡片中:
<view class="alarm-resolved" v-if="alarm.resolved">
<text class="resolved-icon">✅</text>
<!-- 有解除时间时 -->
<text class="resolved-text" v-if="alarm.resolvedTime">
已于 {{alarm.resolvedTime}} {{alarm.resolvedType === 'auto' ? '自动解除' : '用户处理'}}
</text>
<!-- 没有解除时间时 -->
<text class="resolved-text" v-else>
{{alarm.resolvedType === 'auto' ? '已自动解除' : '已处理'}}
</text>
</view>
3. 显示效果
有解除时间:
无人值守风险
热源持续升温,厨房无人5分钟
热点区域: 厨房区域
✅ 已于 21:38 自动解除
没有解除时间:
燃烧报警报警
热源持续升温,温度超过阈值
热点区域: 厨房区域
✅ 已自动解除
🚀 ESP32端建议优化
当前实现
ESP32已经在报警数据中提供了 isActive 字段,这很好!
建议增强
为了提供更完整的解除信息,建议ESP32在报警解除时记录解除时间:
// AlarmManager.cpp
struct AlarmRecord {
int id;
String title;
String desc;
float temp;
unsigned long timestamp;
String location;
bool isActive; // ✅ 已有
// 建议新增
unsigned long resolvedAt; // 解除时间戳(毫秒)
};
// 当检测到报警解除时
void checkAndResolveAlarms() {
for (auto& alarm : alarmHistory) {
if (alarm.isActive && currentTemp < SAFE_TEMP) {
alarm.isActive = false;
alarm.resolvedAt = millis(); // 记录解除时间
ESP_LOGI(TAG, "报警已解除: %s, 解除时间: %lu",
alarm.title.c_str(), alarm.resolvedAt);
}
}
}
增强后的API响应
{
"alarms": [
{
"id": 1,
"title": "燃烧报警报警",
"desc": "热源持续升温,温度超过阈值",
"temp": 65.5,
"time": "14:26:10",
"date": "01-22",
"location": "厨房区域",
"isActive": false,
"timestamp_ms": 1737548770000,
"resolvedAt": 1737548880000 // ✨ 新增:解除时间戳
}
]
}
📋 数据流程
1. 报警触发
ESP32检测到高温
→ 创建报警记录 (isActive=true)
→ 推送到云端
→ APP显示报警(无解除状态)
2. 报警解除
ESP32检测到温度恢复
→ 更新报警记录 (isActive=false, resolvedAt=当前时间)
→ 同步到云端
→ APP显示解除状态 ✅
3. APP查询
APP请求报警历史
→ ESP32返回所有报警(包含isActive字段)
→ APP根据isActive判断是否显示解除状态
→ 如果有resolvedAt,显示具体解除时间
→ 如果没有resolvedAt,只显示"已自动解除"
🎨 UI效果对比
未解除的报警
┌─────────────────────────────┐
│ 🔥 高温危险 65.5°C │
│ 14:26 │
│ │
│ 热源持续升温,温度超过阈值 │
│ 热点区域: 厨房区域 │
│ │
│ [标记已读] [删除] │
└─────────────────────────────┘
已解除的报警(有时间)
┌─────────────────────────────┐
│ 👤 无人值守风险 45.2°C │
│ 21:30 │
│ │
│ 热源持续升温,厨房无人5分钟 │
│ 热点区域: 厨房区域 │
│ ┌─────────────────────────┐ │
│ │ ✅ 已于 21:38 自动解除 │ │
│ └─────────────────────────┘ │
│ │
│ [标记已读] [删除] │
└─────────────────────────────┘
已解除的报警(无时间)
┌─────────────────────────────┐
│ 🔥 燃烧报警报警 65.5°C │
│ 14:26 │
│ │
│ 热源持续升温,温度超过阈值 │
│ 热点区域: 厨房区域 │
│ ┌─────────────────────────┐ │
│ │ ✅ 已自动解除 │ │
│ └─────────────────────────┘ │
│ │
│ [标记已读] [删除] │
└─────────────────────────────┘
🔍 调试信息
在首页 (pages/index/index.vue) 中已经有详细的调试日志:
console.log('📊 最新报警状态:', {
id: this.recentAlarms[0].id,
title: this.recentAlarms[0].title,
isActive: this.recentAlarms[0].isActive,
anyAlarmActive: response.data.anyAlarmActive,
shouldShowResetBtn: this.isAlarmActive(this.recentAlarms[0])
})
console.log('🔍 Alarm Reset按钮显示检查:', {
totalAlarms: this.recentAlarms.length,
firstAlarmActive: this.recentAlarms.length > 0 ? this.isAlarmActive(this.recentAlarms[0]) : false,
firstAlarmData: this.recentAlarms.length > 0 ? {
hasIsActive: this.recentAlarms[0].hasOwnProperty('isActive'),
isActiveValue: this.recentAlarms[0].isActive,
timestamp_ms: this.recentAlarms[0].timestamp_ms,
timeDiff: this.recentAlarms[0].timestamp_ms ? (Date.now() - this.recentAlarms[0].timestamp_ms) : null
} : null
})
这些日志可以帮助验证 isActive 字段是否正确传递。
✅ 优势
使用 isActive 字段的优势:
- 无需额外开发 - ESP32已经提供了这个字段
- 实时准确 - ESP32实时检测温度,能准确判断报警是否解除
- 向后兼容 - 即使ESP32不提供
resolvedAt,也能显示解除状态 - 逻辑清晰 -
isActive=false直观表示报警已解除
📝 总结
现在报警历史页面已经支持:
- ✅ 读取ESP32的
isActive字段 - ✅
isActive=false时显示"已自动解除" - ✅ 如果有
resolvedAt时间,显示具体解除时间 - ✅ 没有时间时,显示简化文案
- ✅ 绿色背景样式,清晰易识别
无需修改ESP32代码,即可立即使用! 如果ESP32后续添加 resolvedAt 字段,APP会自动显示具体解除时间。