feat(ota): support JSON progress updates

This commit is contained in:
stu2not
2026-10-13 18:12:33 +08:00
parent b6906bc8b6
commit 7d05e78a64
7 changed files with 125 additions and 22 deletions
+15
View File
@@ -127,6 +127,21 @@ Headers:
}
```
设备通过 MQTT `msg_id=003` 上报实时进度时,使用 JSON `data.target_version` 和 `data.progress`,例如:
```json
{
"msg_id": "003",
"status": "updating",
"data": {
"target_version": "2.0.4",
"progress": 30
}
}
```
服务器保存该进度,小程序继续通过本状态接口轮询显示。`progress=100` 只表示设备报告写入进度完成,设备重启后上报的实际版本仍是最终完成依据。
### 4. 管理固件配置信息(管理员接口)
**获取固件配置**
@@ -0,0 +1,31 @@
# OTA `003` 实时进度协议
设备通过 `device/{device_id}/response` 上报 OTA 状态。服务器下发 OTA 命令仍使用 `msg_id=003`,设备回执使用相同的 `msg_id`。
## 进行中回执
```json
{
"msg_id": "003",
"status": "updating",
"data": {
"target_version": "2.0.4",
"progress": 30
}
}
```
`progress` 表示设备当前已完成的百分比,范围为 `0` 到 `100`。设备应在状态变化时上报,并避免高频重复发送。`target_version` 应与服务器下发任务的目标版本一致。
## 状态约定
| 状态 | 必要字段 | 服务器行为 |
| --- | --- | --- |
| `accepted` | `current_version`、`target_version` | 记录设备已接受任务,进度默认为 `0` |
| `updating` | `target_version`、`progress` | 保存最新进度并供小程序查询 |
| `success` | `new_version` 或 `target_version` | 记录完成,进度置为 `100`,随后以设备重启后的版本上报作为最终确认 |
| `failed` | `target_version`、可选 `progress` | 保存失败状态和最后有效进度 |
服务器兼容旧设备上报的 `version`、`new_version` 字段,并优先使用 `target_version` 作为本次任务目标。非法进度值不会覆盖已保存的有效进度。
小程序通过设备固件状态接口轮询 `update_status`、`progress` 和 `target_version`,显示最近一次服务器收到的设备进度。进度达到 `100` 仍显示为等待设备确认,直到设备重启后上报目标版本。
+25 -8
View File
@@ -1,5 +1,6 @@
import asyncio
import json
import math
import time
from datetime import datetime
from typing import Awaitable, Callable, Dict, Optional
@@ -311,26 +312,39 @@ class TalkingQMQTTService:
async def _handle_ota_response(self, device_id: str, payload: dict):
status = payload.get("status")
data = payload.get("data", {})
data = payload.get("data") or {}
if not isinstance(data, dict):
logger.warning(device_id, "", f"[OTA] invalid response data: {payload}")
return
target_version = data.get("target_version") or data.get("version") or data.get("new_version")
progress = data.get("progress")
try:
progress_value = float(progress) if progress is not None else None
parsed_progress = float(progress) if progress is not None else None
progress_value = (
parsed_progress
if parsed_progress is not None and math.isfinite(parsed_progress) and 0 <= parsed_progress <= 100
else None
)
except (TypeError, ValueError):
progress_value = None
if progress is not None and progress_value is None:
logger.warning(device_id, "", f"[OTA] invalid progress value: {progress!r}")
if status == "accepted":
current = data.get("current_version")
target = data.get("target_version")
logger.info(device_id, "", f"[OTA] 设备 {device_id} 已接受升级: {current} -> {target}")
logger.info(device_id, "", f"[OTA] 设备 {device_id} 已接受升级: {current} -> {target_version}")
await self._schedule_persistence(
device_id,
"ota",
device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=data.get("target_version") or data.get("version") or data.get("new_version") or "unknown",
firmware_version=target_version or "unknown",
update_status="accepted",
progress=progress_value if progress_value is not None else 0.0,
target_version=target_version,
),
)
elif status == "updating":
@@ -339,13 +353,14 @@ class TalkingQMQTTService:
"ota",
device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=data.get("version") or data.get("new_version") or "updating",
firmware_version=target_version or "updating",
update_status="updating",
progress=progress_value,
target_version=target_version,
),
)
elif status == "success":
new_version = data.get("new_version") or data.get("version")
new_version = data.get("new_version") or data.get("version") or data.get("target_version")
logger.info(device_id, "", f"[OTA] 设备 {device_id} 升级完成: {new_version}")
await self._schedule_persistence(
@@ -367,6 +382,7 @@ class TalkingQMQTTService:
firmware_version=new_version or "unknown",
update_status="success",
progress=100.0,
target_version=target_version,
),
)
elif status == "failed":
@@ -375,9 +391,10 @@ class TalkingQMQTTService:
"ota",
device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=data.get("version") or data.get("new_version") or "unknown",
firmware_version=target_version or "unknown",
update_status="failed",
progress=progress_value,
target_version=target_version,
),
)
logger.warning(device_id, "", f"[OTA] command failed: {payload}")
+7 -1
View File
@@ -146,9 +146,15 @@ async def test_simulated_device_force_ota_dispatches_once_and_completes(monkeypa
await mqtt_service._handle_ota_response(
device_id,
{"status": "updating", "data": {"version": "1.8.5", "progress": 65}},
{
"msg_id": "003",
"status": "updating",
"data": {"target_version": "1.8.5", "progress": 65},
},
)
assert record["update_status"] == "updating"
assert record["firmware_version"] == "1.8.5"
assert record["target_version"] == "1.8.5"
assert record["progress"] == 65.0
await mqtt_service._handle_ota_response(