diff --git a/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue b/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue index ae2fe414..5582c4ca 100644 --- a/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue +++ b/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue @@ -453,10 +453,27 @@ const starting = ref(false) const flashJob = ref(null) let flashPollTimer: ReturnType | null = null -// Best-effort guess from the already-resolved board image label — the user -// can always override; a wrong guess just means the checkbox below draws -// their attention to the warning before anything destructive happens. -const boardAutoDetected = computed(() => /v4/i.test(deviceImage.value.label) || /v3/i.test(deviceImage.value.label)) +const detectedInfo = computed(() => + mesh.status?.detected_device_info?.find(d => d.path === devicePath.value) +) + +// Mirrors mesh::flash::resolve_flash_board (core/archipelago/src/mesh/flash.rs) +// exactly — matching on the display label was wrong: a Heltec V3's CP2102 +// bridge chip reports "CP2102 USB to UART Bridge Controller" in its USB +// strings, not "Heltec", so meshDeviceImages.ts falls back to a generic +// "LoRa radio (CP2102 serial)" label that never matched /v3/i, showing the +// "couldn't confirm automatically" warning even though the backend CAN +// safely auto-detect V3 via vid:pid. Heltec V4 deliberately has no entry +// here, same reasoning as the backend: its vid:pid (303a:1001) is the +// ESP32-S3's generic native-USB descriptor, not V4-specific, so it can't be +// safely auto-matched and always requires manual selection. +const resolvedFlashBoard = computed(() => { + const info = detectedInfo.value + if (info?.vid?.toLowerCase() === '10c4' && info?.pid?.toLowerCase() === 'ea60') return 'heltec-v3' + return '' +}) + +const boardAutoDetected = computed(() => !!resolvedFlashBoard.value) const flashStageLabel = computed(() => { switch (flashJob.value?.stage) { @@ -472,7 +489,7 @@ const flashStageLabel = computed(() => { function openFlashStep() { flashFamily.value = (probe.value?.kind as FlashFirmwareFamily) ?? '' - flashBoard.value = /v4/i.test(deviceImage.value.label) ? 'heltec-v4' : /v3/i.test(deviceImage.value.label) ? 'heltec-v3' : '' + flashBoard.value = resolvedFlashBoard.value flashConfirmed.value = false flashJob.value = null error.value = ''