fix(web): reveal NFC peripherals after detection
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
<script type="module" src="/gesture-control.js?v=android-pad-9"></script>
|
||||
<script src="/pwa.js?v=android-pad-3" defer></script>
|
||||
<script src="/console-v2.js?v=local-first-79-cinema-status" defer></script>
|
||||
<script src="/self-extension.js?v=nfc-persistent-57" defer></script>
|
||||
<script src="/self-extension.js?v=nfc-session-58" defer></script>
|
||||
<script src="/assistant-chat.js?v=codex-assistant-39-video-media" defer></script>
|
||||
<script src="/energy-monitor.js?v=energy-monitor-5-performance" defer></script>
|
||||
<script src="/firmware-workbench-entry.js?v=3" defer></script>
|
||||
|
||||
@@ -378,6 +378,7 @@
|
||||
state: "stopped",
|
||||
topic: "extension/toy",
|
||||
burst_published_count: 0,
|
||||
detected_nfc_ids: [],
|
||||
last_error: null,
|
||||
};
|
||||
let peripheralCatalogRevision = null;
|
||||
@@ -4015,6 +4016,9 @@
|
||||
active_extension_names: Array.isArray(value.active_extension_names)
|
||||
? value.active_extension_names.map((item) => String(item || "")).filter(Boolean)
|
||||
: [],
|
||||
detected_nfc_ids: Array.isArray(value.detected_nfc_ids)
|
||||
? value.detected_nfc_ids.map(normalizeCardUid).filter(Boolean)
|
||||
: [],
|
||||
last_delivery_status: value.last_delivery_status || null,
|
||||
last_error: value.last_error || null,
|
||||
};
|
||||
@@ -4035,11 +4039,14 @@
|
||||
function applyNfcSnapshot(value, { announce = true } = {}) {
|
||||
if (!value || typeof value !== "object") return;
|
||||
if (value.pet_cards) applyPetCardSnapshot(value.pet_cards);
|
||||
if (value.extension_toy) {
|
||||
applyExtensionToySnapshot(value.extension_toy, { announce });
|
||||
}
|
||||
const extensionToyChanged = value.extension_toy
|
||||
? applyExtensionToySnapshot(value.extension_toy, { announce })
|
||||
: false;
|
||||
const revision = Number(value.revision ?? -1);
|
||||
if (revision === nfcSnapshot.revision) return;
|
||||
if (revision === nfcSnapshot.revision) {
|
||||
if (extensionToyChanged) renderUnifiedExtensionView?.();
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(value.legacy_profiles) && value.legacy_profiles.length) {
|
||||
importLegacyProfiles(value.legacy_profiles);
|
||||
}
|
||||
@@ -4229,6 +4236,7 @@
|
||||
reader: nfcSnapshot.reader,
|
||||
card: nfcSnapshot.card,
|
||||
},
|
||||
detectedExtensionNfcIds: extensionToySnapshot.detected_nfc_ids,
|
||||
pets: petCardSnapshot,
|
||||
localDevices: state.devices.map((device) => ({
|
||||
id: device.id,
|
||||
@@ -4331,7 +4339,10 @@
|
||||
item.enabled === false ? `${actionName}(已停用)` : actionName,
|
||||
);
|
||||
});
|
||||
const devices = [...grouped.values()];
|
||||
const detectedNfcIds = new Set(extensionToySnapshot.detected_nfc_ids);
|
||||
const devices = [...grouped.values()].filter(
|
||||
(device) => detectedNfcIds.has(normalizeCardUid(device.cardUid)),
|
||||
);
|
||||
const lastAction = snapshot?.extension?.last_action;
|
||||
const lastResult = snapshot?.extension?.result;
|
||||
const nfcCapabilities = nfcDevice
|
||||
@@ -4414,7 +4425,7 @@
|
||||
<div>${device.actions.slice(0, 8).map((action) => `<span>${escapeHtml(action)}</span>`).join("")}</div>
|
||||
</article>`).join("") : nfcPet ? "" : `
|
||||
<div class="v2-extension-empty">
|
||||
<i data-lucide="link-2-off"></i><div><strong>当前没有已绑定外设</strong><span>外设绑定 NFC 后将持续显示,解除绑定后移除</span></div>
|
||||
<i data-lucide="scan-line"></i><div><strong>等待 NFC 外设靠近</strong><span>检测到已绑定的 NFC 后,外设将在本次运行期间持续显示</span></div>
|
||||
</div>`}
|
||||
</div>
|
||||
<footer class="v2-extension-linkage" data-active="${String(Boolean(visibleLastAction))}">
|
||||
|
||||
@@ -12,7 +12,7 @@ const APP_SHELL = [
|
||||
"/console-v2.css?v=delivery-state-69",
|
||||
"/console-v2.js?v=local-first-79-cinema-status",
|
||||
"/self-extension.css?v=pet-settings-54-dialog-size",
|
||||
"/self-extension.js?v=nfc-persistent-57",
|
||||
"/self-extension.js?v=nfc-session-58",
|
||||
"/speech-input.css?v=sensevoice-7",
|
||||
"/speech-input.js?v=sensevoice-13",
|
||||
"/speech-audio-worklet.js?v=sensevoice-5",
|
||||
|
||||
@@ -167,6 +167,7 @@ class NfcExtensionToyPublisher:
|
||||
"active_catalog_revision": None,
|
||||
"active_extension_names": [],
|
||||
"active_target_scopes": [],
|
||||
"detected_nfc_ids": [],
|
||||
"message_count": 0,
|
||||
"burst_frame_attempt_count": 0,
|
||||
"burst_publish_attempt_count": 0,
|
||||
@@ -192,6 +193,7 @@ class NfcExtensionToyPublisher:
|
||||
active_catalog_revision=None,
|
||||
active_extension_names=[],
|
||||
active_target_scopes=[],
|
||||
detected_nfc_ids=[],
|
||||
message_count=0,
|
||||
last_error=None,
|
||||
)
|
||||
@@ -214,6 +216,7 @@ class NfcExtensionToyPublisher:
|
||||
active_catalog_revision=None,
|
||||
active_extension_names=[],
|
||||
active_target_scopes=[],
|
||||
detected_nfc_ids=[],
|
||||
message_count=0,
|
||||
)
|
||||
|
||||
@@ -346,6 +349,14 @@ class NfcExtensionToyPublisher:
|
||||
self._stop.wait(self.poll_interval_s)
|
||||
continue
|
||||
|
||||
if card_uid:
|
||||
with self._lock:
|
||||
detected_nfc_ids = list(self._status["detected_nfc_ids"])
|
||||
if card_uid not in detected_nfc_ids:
|
||||
self._set_status(
|
||||
detected_nfc_ids=[*detected_nfc_ids, card_uid],
|
||||
)
|
||||
|
||||
try:
|
||||
catalog_revision = self._catalog_revision() if card_uid else ""
|
||||
except Exception as exc: # noqa: BLE001 - retry while the card remains
|
||||
|
||||
@@ -1024,7 +1024,7 @@ operations:
|
||||
) as response:
|
||||
speech_worklet = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.js?v=nfc-persistent-57", timeout=3
|
||||
f"{self.base_url}/self-extension.js?v=nfc-session-58", timeout=3
|
||||
) as response:
|
||||
extension_script = response.read().decode("utf-8")
|
||||
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-persistent-57")}"',
|
||||
f'src="{runtime_url("self-extension.js", "/self-extension.js?v=nfc-session-58")}"',
|
||||
html,
|
||||
)
|
||||
self.assertIn('import "../self-extension.css";', vite_entry)
|
||||
|
||||
@@ -143,6 +143,7 @@ def test_each_detection_publishes_three_frames_per_device_and_then_stops() -> No
|
||||
|
||||
nfc["card"] = None
|
||||
assert wait_until(lambda: service.snapshot()["state"] == "waiting_for_nfc")
|
||||
assert service.snapshot()["detected_nfc_ids"] == ["E113B203"]
|
||||
nfc["card"] = {"action": "present", "uid": "E113B203"}
|
||||
assert wait_until(lambda: len(publications) == 12)
|
||||
assert service.snapshot()["state"] == "burst_complete"
|
||||
@@ -150,6 +151,29 @@ def test_each_detection_publishes_three_frames_per_device_and_then_stops() -> No
|
||||
service.close()
|
||||
|
||||
|
||||
def test_detected_devices_remain_latched_until_publisher_stops() -> None:
|
||||
nfc: dict[str, Any] = {"card": {"action": "present", "uid": "E113B203"}}
|
||||
service = NfcExtensionToyPublisher(
|
||||
nfc_provider=lambda: nfc,
|
||||
catalog_provider=lambda: [capability("light-on")],
|
||||
publisher=lambda _payload: {"delivery_status": "queued"},
|
||||
burst_window_s=0.03,
|
||||
poll_interval_s=0.005,
|
||||
)
|
||||
service.start()
|
||||
try:
|
||||
assert wait_until(
|
||||
lambda: service.snapshot()["detected_nfc_ids"] == ["E113B203"]
|
||||
)
|
||||
nfc["card"] = None
|
||||
assert wait_until(lambda: service.snapshot()["state"] == "waiting_for_nfc")
|
||||
assert service.snapshot()["detected_nfc_ids"] == ["E113B203"]
|
||||
finally:
|
||||
service.close()
|
||||
|
||||
assert service.snapshot()["detected_nfc_ids"] == []
|
||||
|
||||
|
||||
def test_no_matching_ability_publishes_nothing() -> None:
|
||||
nfc = {"card": {"action": "present", "uid": "E113B203"}}
|
||||
publications: list[dict[str, Any]] = []
|
||||
@@ -168,6 +192,7 @@ def test_no_matching_ability_publishes_nothing() -> None:
|
||||
assert wait_until(
|
||||
lambda: service.snapshot()["state"] == "no_matching_extension"
|
||||
)
|
||||
assert service.snapshot()["detected_nfc_ids"] == ["E113B203"]
|
||||
time.sleep(0.05)
|
||||
assert publications == []
|
||||
finally:
|
||||
|
||||
@@ -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-persistent-57",
|
||||
"/self-extension.js?v=nfc-session-58",
|
||||
"/energy-monitor.js?v=energy-monitor-5-performance",
|
||||
):
|
||||
assert asset in html
|
||||
|
||||
@@ -65,15 +65,15 @@ def test_unified_console_exposes_device_id_based_nfc_authorization() -> None:
|
||||
assert 'async function hydratePeripheralCatalog()' in script
|
||||
assert 'api("peripherals")' in script
|
||||
assert 'signalSource: "backend_catalog"' in script
|
||||
assert '当前没有已绑定外设' in script
|
||||
assert '外设绑定 NFC 后将持续显示,解除绑定后移除' in script
|
||||
assert '等待 NFC 外设靠近' in script
|
||||
assert '检测到已绑定的 NFC 后,外设将在本次运行期间持续显示' 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-persistent-57' in html
|
||||
assert 'self-extension.js?v=nfc-session-58' in html
|
||||
assert 'import "../self-extension.css";' in vite_entry
|
||||
|
||||
|
||||
|
||||
@@ -102,3 +102,17 @@ def test_device_card_surfaces_extension_toy_mqtt_delivery() -> None:
|
||||
assert "extensionToySnapshot.last_error" in apply_toy
|
||||
assert "applyExtensionToySnapshot(value.extension_toy" in apply_nfc
|
||||
assert "正在准备 extension/toy 消息" in apply_nfc
|
||||
|
||||
|
||||
def test_homepage_only_reveals_nfc_devices_detected_in_this_run() -> None:
|
||||
source = SOURCE_PATH.read_text(encoding="utf-8")
|
||||
unified = _function_source(
|
||||
source,
|
||||
" function buildUnifiedExtensionSync() {",
|
||||
"\n function init()",
|
||||
)
|
||||
|
||||
assert "detected_nfc_ids" in source
|
||||
assert "const detectedNfcIds = new Set(" in unified
|
||||
assert "detectedNfcIds.has(normalizeCardUid(device.cardUid))" in unified
|
||||
assert "检测到已绑定的 NFC 后,外设将在本次运行期间持续显示" in unified
|
||||
|
||||
Reference in New Issue
Block a user