补充设备初始化留言与OTA后端能力

This commit is contained in:
stu2not
2026-05-08 16:56:29 +08:00
parent 1aaaf4747a
commit 9f38965a79
14 changed files with 848 additions and 28 deletions

View File

@@ -9,19 +9,19 @@ import aiomqtt
from banban.service.binding import BindingService
from banban.service.device_alarm import device_alarm_service
from banban.service.device_setting import device_setting_service
from banban.service.im import im_service
from banban.service.location import location_service
from config import settings
from services.card_service import card_service
from services.offline_audio_cache import offline_audio_cache
import aiomqtt
from banban.service.location import location_service
from database.models import ChildLocationCurrent
from datetime import datetime
from services.device_target_cache import device_target_cache
from services.device_identity_initializer import (
DeviceIdentityInitializationError,
device_identity_initializer,
)
from services.device_update_manager import device_firmware_update_manager
from services.offline_audio_cache import offline_audio_cache
from services.task_manager import task_manager
from utils.logger import session_logger as logger
from services.task_manager import task_manager
class TalkingQMQTTService:
_instance = None
@@ -55,6 +55,7 @@ class TalkingQMQTTService:
"009": self._handle_remote_sleep_wake_response,
"010": self._handle_alarm_report,
"011": self._handle_short_press_message,
"012": self._handle_device_identity_init,
}
@classmethod
@@ -80,12 +81,16 @@ class TalkingQMQTTService:
if not parts or parts[0] != "device":
continue
device_id = parts[1] if len(parts) >= 2 else "unknown"
if not device_id.startswith(f"{self.device_prefix}_"):
continue
payload = json.loads(message.payload.decode("utf-8"))
msg_id = payload.get("msg_id")
device_id = parts[1] if len(parts) >= 2 else "unknown"
topic_kind = parts[2] if len(parts) >= 3 else ""
if msg_id == "012" and topic_kind != "event":
continue
if msg_id != "012" and not device_id.startswith(f"{self.device_prefix}_"):
continue
handler = self._msg_handlers.get(msg_id)
if handler:
await handler(device_id, payload)
@@ -185,7 +190,36 @@ class TalkingQMQTTService:
async def _handle_ota_response(self, device_id: str, payload: dict):
status = payload.get("status")
data = payload.get("data", {})
if status == "success":
progress = data.get("progress")
try:
progress_value = float(progress) if progress is not None else None
except (TypeError, ValueError):
progress_value = None
if status == "accepted":
await self._schedule_persistence(
device_id,
"ota",
device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=data.get("version") or data.get("new_version") or "unknown",
update_status="accepted",
progress=progress_value if progress_value is not None else 0.0,
),
)
elif status == "updating":
await self._schedule_persistence(
device_id,
"ota",
device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=data.get("version") or data.get("new_version") or "updating",
update_status="updating",
progress=progress_value,
),
)
elif status == "success":
new_version = data.get("new_version") or data.get("version")
await self._schedule_persistence(
device_id,
"ota",
@@ -194,13 +228,33 @@ class TalkingQMQTTService:
power=None,
volume=None,
signal_strength=None,
version_str=data.get("new_version"),
version_str=new_version,
),
)
await self._schedule_persistence(
device_id,
"ota",
device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=new_version or "unknown",
update_status="success",
progress=100.0,
),
)
elif status == "failed":
await self._schedule_persistence(
device_id,
"ota",
device_firmware_update_manager.update_firmware_update(
device_id=device_id,
firmware_version=data.get("version") or data.get("new_version") or "unknown",
update_status="failed",
progress=progress_value,
),
)
elif status != "accepted":
logger.warning(device_id, "", f"[OTA] command failed: {payload}")
else:
logger.warning(device_id, "", f"[OTA] 设备 {device_id} 升级异常: {payload}")
logger.warning(device_id, "", f"[OTA] unknown status: {payload}")
async def _handle_nfc_notice_response(self, device_id: str, payload: dict):
status = payload.get("status")
@@ -283,8 +337,35 @@ class TalkingQMQTTService:
params = payload.get("params", {})
nfc_uuid = params.get("uuid")
logger.info(device_id, "", f"[短按留言] 设备 {device_id} 短按发送留言, UUID={nfc_uuid}")
media_file_key = str(params.get("media_file_key") or params.get("audio_url") or "").strip()
if media_file_key:
try:
await im_service.create_device_parent_leave_message(
device_id=device_id,
media_file_key=media_file_key,
media_duration_ms=params.get("media_duration_ms"),
media_mime_type=params.get("media_mime_type"),
media_size_bytes=params.get("media_size_bytes"),
media_transcript_text=params.get("media_transcript_text"),
client_msg_id=params.get("client_msg_id"),
ext_json=params.get("ext_json") if isinstance(params.get("ext_json"), dict) else None,
)
logger.info(device_id, "", f"[短按留言] 设备 {device_id} 留言已写入家长会话")
except Exception as exc:
logger.warning(device_id, "", f"[短按留言] 设备 {device_id} 留言写入失败: {exc}")
await self._publish(
f"device/{device_id}/event_resp",
{
"msg_id": "011",
"status": "failed",
"message": str(exc),
},
)
return
payload = {
"msg_id": "011",
"status": "success",
"type": 0,
"params": {
"url": f"http://{settings.server_host}:{settings.server_port}/assets/audio/message_ok.mp3"
@@ -292,6 +373,30 @@ class TalkingQMQTTService:
}
await self._publish(f"device/{device_id}/event_resp", payload)
async def _handle_device_identity_init(self, imei: str, payload: dict):
try:
result = await device_identity_initializer.initialize_by_imei(imei)
except DeviceIdentityInitializationError as exc:
logger.warning(imei, "", f"[设备初始化] IMEI初始化失败: {exc}")
await self._publish(
f"device/{imei}/event_resp",
{
"msg_id": "012",
"status": "failed",
"message": str(exc),
},
)
return
response_payload = {
"msg_id": "012",
"params": {
"device_id": result.device_id,
"device_sn": result.serial_number,
},
}
await self._publish(f"device/{imei}/event_resp", response_payload)
async def _send_nfc_listen_response(self, device_id: str, nfc_uuid: str):
topic = f"device/{device_id}/event_resp"
card = await card_service.get_card_by_uuid(nfc_uuid)