feat(web): add peripheral card quick controls
This commit is contained in:
@@ -1024,11 +1024,11 @@ operations:
|
||||
) as response:
|
||||
speech_worklet = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.js?v=nfc-session-58", timeout=3
|
||||
f"{self.base_url}/self-extension.js?v=peripheral-cards-59", timeout=3
|
||||
) as response:
|
||||
extension_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.css?v=pet-settings-54-dialog-size", timeout=3
|
||||
f"{self.base_url}/self-extension.css?v=peripheral-cards-55", timeout=3
|
||||
) as response:
|
||||
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
|
||||
with self.opener.open(
|
||||
@@ -1105,7 +1105,7 @@ operations:
|
||||
self.assertIn('id="v2BootScreen"', template)
|
||||
self.assertIn('正在加载车机控制与实时状态', template)
|
||||
self.assertIn(
|
||||
f'src="{runtime_url("self-extension.js", "/self-extension.js?v=nfc-session-58")}"',
|
||||
f'src="{runtime_url("self-extension.js", "/self-extension.js?v=peripheral-cards-59")}"',
|
||||
html,
|
||||
)
|
||||
self.assertIn('import "../self-extension.css";', vite_entry)
|
||||
|
||||
@@ -131,6 +131,9 @@ def test_each_detection_publishes_three_frames_per_device_and_then_stops() -> No
|
||||
for _at, publication in publications
|
||||
) == {"toy-fan-001": 3, "toy-light-001": 3}
|
||||
status = service.snapshot()
|
||||
session_id = status["session_id"]
|
||||
assert isinstance(session_id, str) and session_id
|
||||
assert status["detection_count"] == 1
|
||||
assert status["state"] == "burst_complete"
|
||||
assert status["burst_frame_attempt_count"] == 3
|
||||
assert status["burst_publish_attempt_count"] == 6
|
||||
@@ -147,6 +150,8 @@ def test_each_detection_publishes_three_frames_per_device_and_then_stops() -> No
|
||||
nfc["card"] = {"action": "present", "uid": "E113B203"}
|
||||
assert wait_until(lambda: len(publications) == 12)
|
||||
assert service.snapshot()["state"] == "burst_complete"
|
||||
assert service.snapshot()["session_id"] == session_id
|
||||
assert service.snapshot()["detection_count"] == 2
|
||||
finally:
|
||||
service.close()
|
||||
|
||||
@@ -172,6 +177,8 @@ def test_detected_devices_remain_latched_until_publisher_stops() -> None:
|
||||
service.close()
|
||||
|
||||
assert service.snapshot()["detected_nfc_ids"] == []
|
||||
assert service.snapshot()["session_id"] is None
|
||||
assert service.snapshot()["detection_count"] == 0
|
||||
|
||||
|
||||
def test_no_matching_ability_publishes_nothing() -> None:
|
||||
|
||||
@@ -94,7 +94,7 @@ def test_performance_assets_have_a_new_cache_generation() -> None:
|
||||
for asset in (
|
||||
"/app.js?v=dual-seat-147-video-media",
|
||||
"/console-v2.js?v=local-first-79-cinema-status",
|
||||
"/self-extension.js?v=nfc-session-58",
|
||||
"/self-extension.js?v=peripheral-cards-59",
|
||||
"/energy-monitor.js?v=energy-monitor-5-performance",
|
||||
):
|
||||
assert asset in html
|
||||
|
||||
@@ -47,7 +47,7 @@ def test_theme_asset_versions_are_consistent_with_the_pwa_shell() -> None:
|
||||
for source_asset, cached_asset in (
|
||||
('import "../styles.css"', "/styles.css?v=dual-seat-24-dialog-size"),
|
||||
('import "../console-v2.css"', "/console-v2.css?v=delivery-state-69"),
|
||||
('import "../self-extension.css"', "/self-extension.css?v=pet-settings-54-dialog-size"),
|
||||
('import "../self-extension.css"', "/self-extension.css?v=peripheral-cards-55"),
|
||||
('import "../speech-input.css"', "/speech-input.css?v=sensevoice-7"),
|
||||
('import "../codepen-glass-theme.css"', "/codepen-glass-theme.css?v=creative-cloud-6-performance"),
|
||||
):
|
||||
|
||||
@@ -67,13 +67,23 @@ def test_unified_console_exposes_device_id_based_nfc_authorization() -> None:
|
||||
assert 'signalSource: "backend_catalog"' in script
|
||||
assert '等待 NFC 外设靠近' in script
|
||||
assert '检测到已绑定的 NFC 后,外设将在本次运行期间持续显示' in script
|
||||
assert 'data-hide-peripheral-card' in script
|
||||
assert 'data-toggle-peripheral-card' in script
|
||||
assert 'data-run-peripheral-quick-action' in script
|
||||
assert 'function hidePeripheralCard(deviceId, cardUid)' in script
|
||||
assert 'function togglePeripheralCard(deviceId)' in script
|
||||
assert 'api("peripherals/execute"' in script
|
||||
assert 'const hiddenPeripheralDevices = new Map()' in script
|
||||
assert 'detection_count' in script
|
||||
|
||||
assert '.v2-nfc-inline' in styles
|
||||
assert '.v2-nfc-dialog' in styles
|
||||
assert '.v2-nfc-command-list' in styles
|
||||
assert '.v2-nfc-command-option' in styles
|
||||
assert '.v2-nfc-select-all' in styles
|
||||
assert 'self-extension.js?v=nfc-session-58' in html
|
||||
assert '.v2-extension-device-hide' in styles
|
||||
assert '.v2-extension-quick-controls' in styles
|
||||
assert 'self-extension.js?v=peripheral-cards-59' in html
|
||||
assert 'import "../self-extension.css";' in vite_entry
|
||||
|
||||
|
||||
@@ -144,6 +154,8 @@ def test_server_exposes_nfc_snapshot_through_same_origin_extension_api() -> None
|
||||
assert 'SeatDemoHandler.pets.start()' in server
|
||||
assert 'SeatDemoHandler.pets.close()' in server
|
||||
assert 'SeatDemoHandler.nfc.close()' in server
|
||||
assert 'path == "/api/extension/peripherals/execute"' in server
|
||||
assert "execute_quick_peripheral_capability" in server
|
||||
assert "frame-src 'self' http://*:3001" in server
|
||||
|
||||
|
||||
|
||||
@@ -1403,6 +1403,40 @@ class SelfExtensionTests(unittest.TestCase):
|
||||
self.assertEqual(operation["domain"], "self_extension")
|
||||
self.assertTrue(operation["telemetry"]["gateway_acknowledged"])
|
||||
|
||||
def test_quick_peripheral_control_reauthorizes_durable_capability(self) -> None:
|
||||
with TemporaryDirectory() as directory:
|
||||
service = SelfExtensionService(
|
||||
SimulationGateway(),
|
||||
catalog_db_path=Path(directory, "infrared.sqlite3"),
|
||||
)
|
||||
service.sync_peripheral_catalog(
|
||||
[
|
||||
{
|
||||
"id": "quick-light-on",
|
||||
"status": "learned",
|
||||
"enabled": True,
|
||||
"device_id": "quick-light",
|
||||
"device_name": "快捷灯",
|
||||
"object_names": ["快捷灯"],
|
||||
"card_uid": "2CC8B103",
|
||||
"action": "打开",
|
||||
"action_names": ["打开", "开启"],
|
||||
"slot": 8,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
result = service.execute_quick_peripheral_capability(
|
||||
"quick-light-on",
|
||||
conversation_id="conv-quick-light",
|
||||
)
|
||||
|
||||
self.assertTrue(result["success"])
|
||||
self.assertEqual(result["execution_mode"], "hardware")
|
||||
self.assertEqual(result["provider_requested"], "web_console_quick_control")
|
||||
self.assertEqual(result["peripheral"]["slot"], 8)
|
||||
service.close()
|
||||
|
||||
def test_main_peripheral_sequence_executes_validated_actions_and_delays(self) -> None:
|
||||
service = SelfExtensionService(SimulationGateway())
|
||||
catalog = [
|
||||
|
||||
@@ -116,3 +116,18 @@ def test_homepage_only_reveals_nfc_devices_detected_in_this_run() -> None:
|
||||
assert "const detectedNfcIds = new Set(" in unified
|
||||
assert "detectedNfcIds.has(normalizeCardUid(device.cardUid))" in unified
|
||||
assert "检测到已绑定的 NFC 后,外设将在本次运行期间持续显示" in unified
|
||||
|
||||
|
||||
def test_homepage_peripheral_cards_are_single_expand_and_quick_control_surfaces() -> None:
|
||||
source = SOURCE_PATH.read_text(encoding="utf-8")
|
||||
unified = _function_source(
|
||||
source,
|
||||
" function buildUnifiedExtensionSync() {",
|
||||
"\n function init()",
|
||||
)
|
||||
|
||||
assert "expandedPeripheralDeviceId === device.id" in unified
|
||||
assert 'data-hide-peripheral-card="${escapeHtml(device.id)}"' in unified
|
||||
assert 'data-toggle-peripheral-card="${escapeHtml(device.id)}"' in unified
|
||||
assert 'data-run-peripheral-quick-action="${escapeHtml(action.id)}"' in unified
|
||||
assert "hiddenPeripheralDevices.has(device.id)" in unified
|
||||
|
||||
Reference in New Issue
Block a user