feat(web): add peripheral card quick controls

This commit is contained in:
Miayui
2026-09-21 16:01:42 +08:00
parent ad0c1f4ed1
commit ba5dfcd6da
14 changed files with 530 additions and 19 deletions
@@ -6,6 +6,7 @@ import json
import re
import threading
import time
import uuid
from collections.abc import Callable, Mapping
from copy import deepcopy
from datetime import UTC, datetime
@@ -157,6 +158,7 @@ class NfcExtensionToyPublisher:
self._thread: threading.Thread | None = None
self._revision = 0
self._status: dict[str, Any] = {
"session_id": None,
"state": "stopped",
"topic": EXTENSION_TOY_MQTT_TOPIC,
"qos": 1,
@@ -168,6 +170,7 @@ class NfcExtensionToyPublisher:
"active_extension_names": [],
"active_target_scopes": [],
"detected_nfc_ids": [],
"detection_count": 0,
"message_count": 0,
"burst_frame_attempt_count": 0,
"burst_publish_attempt_count": 0,
@@ -188,12 +191,14 @@ class NfcExtensionToyPublisher:
return
self._stop.clear()
self._set_status(
session_id=uuid.uuid4().hex,
state="waiting_for_nfc",
active_nfc_id=None,
active_catalog_revision=None,
active_extension_names=[],
active_target_scopes=[],
detected_nfc_ids=[],
detection_count=0,
message_count=0,
last_error=None,
)
@@ -211,12 +216,14 @@ class NfcExtensionToyPublisher:
thread.join(timeout=2.0)
self._thread = None
self._set_status(
session_id=None,
state="stopped",
active_nfc_id=None,
active_catalog_revision=None,
active_extension_names=[],
active_target_scopes=[],
detected_nfc_ids=[],
detection_count=0,
message_count=0,
)
@@ -339,6 +346,7 @@ class NfcExtensionToyPublisher:
def _run(self) -> None:
latched_uid = ""
present_uid = ""
latched_catalog_revision = ""
latched_message_signature: str | None = None
while not self._stop.is_set():
@@ -349,6 +357,16 @@ class NfcExtensionToyPublisher:
self._stop.wait(self.poll_interval_s)
continue
if card_uid != present_uid:
present_uid = card_uid
if card_uid:
with self._lock:
detection_count = int(self._status["detection_count"]) + 1
self._set_status(
active_nfc_id=card_uid,
detection_count=detection_count,
)
if card_uid:
with self._lock:
detected_nfc_ids = list(self._status["detected_nfc_ids"])
@@ -2976,6 +2976,48 @@ class SelfExtensionService:
},
}
def execute_quick_peripheral_capability(
self,
capability_id: object,
*,
conversation_id: str | None = None,
) -> dict[str, Any]:
"""Execute one durable NFC-authorized capability selected by the UI."""
normalized_id = str(capability_id or "").strip()
if not normalized_id or len(normalized_id) > 128:
raise ValueError("外设快捷控制缺少有效的能力 ID。")
capability = next(
(
dict(item)
for item in self.list_peripheral_capabilities()
if isinstance(item, Mapping)
and str(item.get("id") or "").strip() == normalized_id
and item.get("status") == "learned"
and item.get("enabled") is True
and bool(_normalized_card_uid(item.get("card_uid")))
),
None,
)
if capability is None:
raise ExtensionIntegrationError(
"外设快捷控制未通过当前持久 NFC 能力目录校验。"
)
device_name = str(
capability.get("device_name")
or capability.get("device_id")
or "外设"
).strip()
action_name = str(capability.get("action") or "动作").strip()
result = self.execute_peripheral_request(
f"{device_name} {action_name}",
capability,
conversation_id=conversation_id,
execution_mode="hardware",
provider_requested="web_console_quick_control",
)
return {"success": True, **result}
def execute_peripheral_plan_request(
self,
utterance: str,
+15
View File
@@ -4554,6 +4554,21 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
)
self._json({"success": True, **sync_result})
return
if path == "/api/extension/peripherals/execute":
result = self.extension.execute_quick_peripheral_capability(
payload.get("capability_id"),
conversation_id=str(payload.get("conversation_id") or "").strip()
or None,
)
self.vehicle_display_sync.update_extension(
action={
"type": "peripheral_quick_control",
"command": result.get("utterance"),
},
result=result,
)
self._json(result)
return
if path == "/api/extension/assistant/plan":
self.vehicle_display_sync.update_extension(
catalog=payload.get("peripherals", payload.get("catalog", [])),