feat: introduce vendors.json for mapping vendors to models and refactor targets.ts to utilize new data structure

This commit is contained in:
Ben Allfree
2025-12-10 17:39:10 -08:00
parent c38e6b735c
commit cdc4959dca
6 changed files with 498 additions and 263 deletions

View File

@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Minor
- Added vendors.json mapping vendors to models and platformio targets
- Refactored targets.ts to use vendors.json and architecture-hierarchy.json instead of hardware-list.json
- Updated architecture-hierarchy.json generation to use actual PlatformIO environment names (removed normalization)
- Removed normalization from lib/utils.ts since all inputs now use standardized PlatformIO names
### Patch
- Fix Convex server functions being imported in browser by moving ArtifactType enum to client-safe location

View File

@@ -1,210 +1,210 @@
{
"betafpv2400txmicro": "esp32",
"betafpv900txnano": "esp32",
"betafpv_2400_tx_micro": "esp32",
"betafpv_900_tx_nano": "esp32",
"chatter2": "esp32",
"9m2ibraprsloratracker": "esp32",
"meshtasticdrdev": "esp32",
"9m2ibr_aprs_lora_tracker": "esp32",
"meshtastic-dr-dev": "esp32",
"hydra": "esp32",
"meshtasticdiyv1": "esp32",
"meshtasticdiyv11": "esp32",
"hackerboxesesp32io": "esp32",
"heltecv1": "esp32",
"heltecv20": "esp32",
"heltecv21": "esp32",
"heltecwirelessbridge": "esp32",
"heltecwslv21": "esp32",
"m5stackcore": "esp32",
"m5stackcoreink": "esp32",
"nanog1": "esp32",
"nanog1explorer": "esp32",
"radiomaster900bandit": "esp32",
"radiomaster900banditmicro": "esp32",
"radiomaster900banditnano": "esp32",
"meshtastic-diy-v1": "esp32",
"meshtastic-diy-v1_1": "esp32",
"hackerboxes-esp32-io": "esp32",
"heltec-v1": "esp32",
"heltec-v2_0": "esp32",
"heltec-v2_1": "esp32",
"heltec-wireless-bridge": "esp32",
"heltec-wsl-v2_1": "esp32",
"m5stack-core": "esp32",
"m5stack-coreink": "esp32",
"nano-g1": "esp32",
"nano-g1-explorer": "esp32",
"radiomaster_900_bandit": "esp32",
"radiomaster_900_bandit_micro": "esp32",
"radiomaster_900_bandit_nano": "esp32",
"rak11200": "esp32",
"stationg1": "esp32",
"station-g1": "esp32",
"tbeam": "esp32",
"tbeamdisplayshield": "tbeam",
"tbeam07": "esp32",
"tlorav1": "esp32",
"tlorav13": "esp32",
"tlorav2": "esp32",
"tlorav2116": "esp32",
"sugarcube": "tlorav2116",
"tlorav2116tcxo": "esp32",
"tlorav2118": "esp32",
"tlorav330tcxo": "esp32",
"tbeam-displayshield": "tbeam",
"tbeam0_7": "esp32",
"tlora-v1": "esp32",
"tlora_v1_3": "esp32",
"tlora-v2": "esp32",
"tlora-v2-1-1_6": "esp32",
"sugarcube": "tlora-v2-1-1_6",
"tlora-v2-1-1_6-tcxo": "esp32",
"tlora-v2-1-1_8": "esp32",
"tlora-v3-3-0-tcxo": "esp32",
"trackerd": "esp32",
"wiphone": "esp32",
"aic3": "esp32c3",
"esp32c3supermini": "esp32c3",
"esp32c3base": "esp32",
"hackerboxesesp32c3oled": "esp32c3",
"heltecht62esp32c3sx1262": "esp32c3",
"heltechru3601": "esp32c3",
"m5stackstampc3": "esp32c3",
"esp32c6base": "esp32",
"m5stackunitc6l": "esp32c6",
"tlorac6": "esp32c6",
"esp32s2base": "esp32",
"nuggets2lora": "esp32s2",
"CDEBYTEEoRaS3": "esp32s3",
"EBYTEESP32S3": "esp32s3",
"thinknodem2": "esp32s3",
"thinknodem5": "esp32s3",
"bpipicowesp32s3": "esp32s3",
"crowpanelesp32s35epaper": "esp32s3",
"crowpanelesp32s34epaper": "esp32s3",
"crowpanelesp32s32epaper": "esp32s3",
"myesp32s3diyeink": "esp32s3",
"myesp32s3diyoled": "esp32s3",
"tenergys3e22": "esp32s3",
"dreamcatcher2206": "esp32s3",
"crowpanelbase": "crowpanel",
"elecrowadv2428tft": "crowpanelsmallesp32s3base",
"elecrowadv35tft": "crowpanelsmallesp32s3base",
"elecrowadv1435070tft": "crowpanellargeesp32s3base",
"ESP32S3Pico": "esp32s3",
"esp32s3base": "esp32",
"hackadaycommunicator": "esp32s3",
"helteccapsulesensorv3": "esp32s3",
"heltecsensorhub": "esp32s3",
"heltecv3": "esp32s3",
"heltecv4base": "esp32s3",
"heltecv4": "heltecv4base",
"heltecv4tft": "heltecv4base",
"heltecvisionmastere213": "esp32s3",
"heltecvisionmastere213inkhud": "esp32s3",
"heltecvisionmastere290": "esp32s3",
"heltecvisionmastere290inkhud": "esp32s3",
"heltecvisionmastert190": "esp32s3",
"heltecwirelesspaper": "esp32s3",
"heltecwirelesspaperinkhud": "esp32s3",
"heltecwirelesspaperv10": "esp32s3",
"heltecwirelesstracker": "esp32s3",
"heltecwirelesstrackerV10": "esp32s3",
"heltecwirelesstrackerv2": "esp32s3",
"heltecwslv3": "esp32s3",
"ai-c3": "esp32c3",
"esp32c3_super_mini": "esp32c3",
"esp32c3_base": "esp32",
"hackerboxes-esp32c3-oled": "esp32c3",
"heltec-ht62-esp32c3-sx1262": "esp32c3",
"heltec-hru-3601": "esp32c3",
"m5stack-stamp-c3": "esp32c3",
"esp32c6_base": "esp32",
"m5stack-unitc6l": "esp32c6",
"tlora-c6": "esp32c6",
"esp32s2_base": "esp32",
"nugget-s2-lora": "esp32s2",
"CDEBYTE_EoRa-S3": "esp32s3",
"EBYTE_ESP32-S3": "esp32s3",
"thinknode_m2": "esp32s3",
"thinknode_m5": "esp32s3",
"bpi_picow_esp32_s3": "esp32s3",
"crowpanel-esp32s3-5-epaper": "esp32s3",
"crowpanel-esp32s3-4-epaper": "esp32s3",
"crowpanel-esp32s3-2-epaper": "esp32s3",
"my-esp32s3-diy-eink": "esp32s3",
"my-esp32s3-diy-oled": "esp32s3",
"t-energy-s3_e22": "esp32s3",
"dreamcatcher-2206": "esp32s3",
"crowpanel_base": "crowpanel",
"elecrow-adv-24-28-tft": "crowpanel_small_esp32s3_base",
"elecrow-adv-35-tft": "crowpanel_small_esp32s3_base",
"elecrow-adv1-43-50-70-tft": "crowpanel_large_esp32s3_base",
"ESP32-S3-Pico": "esp32s3",
"esp32s3_base": "esp32",
"hackaday-communicator": "esp32s3",
"heltec_capsule_sensor_v3": "esp32s3",
"heltec_sensor_hub": "esp32s3",
"heltec-v3": "esp32s3",
"heltec_v4_base": "esp32s3",
"heltec-v4": "heltec_v4_base",
"heltec-v4-tft": "heltec_v4_base",
"heltec-vision-master-e213": "esp32s3",
"heltec-vision-master-e213-inkhud": "esp32s3",
"heltec-vision-master-e290": "esp32s3",
"heltec-vision-master-e290-inkhud": "esp32s3",
"heltec-vision-master-t190": "esp32s3",
"heltec-wireless-paper": "esp32s3",
"heltec-wireless-paper-inkhud": "esp32s3",
"heltec-wireless-paper-v1_0": "esp32s3",
"heltec-wireless-tracker": "esp32s3",
"heltec-wireless-tracker-V1-0": "esp32s3",
"heltec-wireless-tracker-v2": "esp32s3",
"heltec-wsl-v3": "esp32s3",
"icarus": "esp32s3",
"link32s3v1": "esp32s3",
"m5stackcores3": "esp32s3",
"meshtabbase": "esp32s3",
"meshtab32TNresistive": "meshtabbase",
"meshtab32IPSresistive": "meshtabbase",
"meshtab35IPSresistive": "meshtabbase",
"meshtab35TNresistive": "meshtabbase",
"meshtab32IPScapacitive": "meshtabbase",
"meshtab35IPScapacitive": "meshtabbase",
"meshtab40IPScapacitive": "meshtabbase",
"nibbleesp32": "esp32s3",
"nuggets3lora": "esp32s3",
"picomputers3": "esp32s3",
"picomputers3tft": "picomputers3",
"link32-s3-v1": "esp32s3",
"m5stack-cores3": "esp32s3",
"mesh_tab_base": "esp32s3",
"mesh-tab-3-2-TN-resistive": "mesh_tab_base",
"mesh-tab-3-2-IPS-resistive": "mesh_tab_base",
"mesh-tab-3-5-IPS-resistive": "mesh_tab_base",
"mesh-tab-3-5-TN-resistive": "mesh_tab_base",
"mesh-tab-3-2-IPS-capacitive": "mesh_tab_base",
"mesh-tab-3-5-IPS-capacitive": "mesh_tab_base",
"mesh-tab-4-0-IPS-capacitive": "mesh_tab_base",
"nibble-esp32": "esp32s3",
"nugget-s3-lora": "esp32s3",
"picomputer-s3": "esp32s3",
"picomputer-s3-tft": "picomputer-s3",
"rak3312": "esp32s3",
"rakwismeshtapv2tft": "rakwismeshtaps3",
"seeedsensecapindicator": "esp32s3",
"seeedsensecapindicatortft": "seeedsensecapindicator",
"seeedxiaos3": "esp32s3",
"stationg2": "esp32s3",
"tdeck": "esp32s3",
"tdecktft": "tdeck",
"tdeckpro": "esp32s3",
"tethelite": "esp32s3",
"twatchs3": "esp32s3",
"tbeams3core": "esp32s3",
"tlorapager": "esp32s3",
"tlorapagertft": "tlorapager",
"tlorat3s3epaper": "esp32s3",
"tlorat3s3epaperinkhud": "esp32s3",
"tlorat3s3v1": "esp32s3",
"rak_wismesh_tap_v2-tft": "rak_wismeshtap_s3",
"seeed-sensecap-indicator": "esp32s3",
"seeed-sensecap-indicator-tft": "seeed-sensecap-indicator",
"seeed-xiao-s3": "esp32s3",
"station-g2": "esp32s3",
"t-deck": "esp32s3",
"t-deck-tft": "t-deck",
"t-deck-pro": "esp32s3",
"t-eth-elite": "esp32s3",
"t-watch-s3": "esp32s3",
"tbeam-s3-core": "esp32s3",
"tlora-pager": "esp32s3",
"tlora-pager-tft": "tlora-pager",
"tlora-t3s3-epaper": "esp32s3",
"tlora-t3s3-epaper-inkhud": "esp32s3",
"tlora-t3s3-v1": "esp32s3",
"tracksenger": "esp32s3",
"tracksengerlcd": "esp32s3",
"tracksengeroled": "esp32s3",
"tracksenger-lcd": "esp32s3",
"tracksenger-oled": "esp32s3",
"unphone": "esp32s3",
"unphonetft": "unphone",
"unphone-tft": "unphone",
"coverage": "native",
"buildroot": "portduino",
"pca10059diyeink": "nrf52840",
"thinknodem1": "nrf52840",
"thinknodem1inkhud": "nrf52840",
"thinknodem3": "nrf52840",
"thinknodem6": "nrf52840",
"ME25LS014Y10TD": "nrf52840",
"ME25LS014Y10TDeink": "nrf52840",
"pca10059_diy_eink": "nrf52840",
"thinknode_m1": "nrf52840",
"thinknode_m1-inkhud": "nrf52840",
"thinknode_m3": "nrf52840",
"thinknode_m6": "nrf52840",
"ME25LS01-4Y10TD": "nrf52840",
"ME25LS01-4Y10TD_e-ink": "nrf52840",
"ms24sf1": "nrf52840",
"makerpythonnrf52840sx1280eink": "nrf52840",
"makerpythonnrf52840sx1280oled": "nrf52840",
"TWCmeshv4": "nrf52840",
"makerpython_nrf52840_sx1280_eink": "nrf52840",
"makerpython_nrf52840_sx1280_oled": "nrf52840",
"TWC_mesh_v4": "nrf52840",
"canaryone": "nrf52840",
"WashTastic": "nrf52840",
"nrf52promicrodiytcxo": "nrf52840",
"nrf52promicrodiyinkhud": "nrf52840",
"seeedxiaonrf52840wiosx1262": "nrf52840",
"seeedxiaonrf52840e22900m30s": "seeedxiaonrf52840kit",
"seeedxiaonrf52840e22900m33s": "seeedxiaonrf52840kit",
"xiaoble": "seeedxiaonrf52840kit",
"featherdiy": "nrf52840",
"gat562meshtrialtracker": "nrf52840",
"heltecmeshnodet114": "nrf52840",
"heltecmeshnodet114inkhud": "nrf52840",
"heltecmeshpocket5000": "nrf52840",
"heltecmeshpocket5000inkhud": "nrf52840",
"heltecmeshpocket10000": "nrf52840",
"heltecmeshpocket10000inkhud": "nrf52840",
"heltecmeshsolarbase": "nrf52840",
"heltecmeshsolar": "heltecmeshsolarbase",
"heltecmeshsolareink": "heltecmeshsolarbase",
"heltecmeshsolarinkhud": "heltecmeshsolarbase",
"heltecmeshsolaroled": "heltecmeshsolarbase",
"heltecmeshsolartft": "heltecmeshsolarbase",
"nrf52_promicro_diy_tcxo": "nrf52840",
"nrf52_promicro_diy-inkhud": "nrf52840",
"seeed-xiao-nrf52840-wio-sx1262": "nrf52840",
"seeed_xiao_nrf52840_e22_900m30s": "seeed_xiao_nrf52840_kit",
"seeed_xiao_nrf52840_e22_900m33s": "seeed_xiao_nrf52840_kit",
"xiao_ble": "seeed_xiao_nrf52840_kit",
"feather_diy": "nrf52840",
"gat562_mesh_trial_tracker": "nrf52840",
"heltec-mesh-node-t114": "nrf52840",
"heltec-mesh-node-t114-inkhud": "nrf52840",
"heltec-mesh-pocket-5000": "nrf52840",
"heltec-mesh-pocket-5000-inkhud": "nrf52840",
"heltec-mesh-pocket-10000": "nrf52840",
"heltec-mesh-pocket-10000-inkhud": "nrf52840",
"heltec_mesh_solar_base": "nrf52840",
"heltec-mesh-solar": "heltec_mesh_solar_base",
"heltec-mesh-solar-eink": "heltec_mesh_solar_base",
"heltec-mesh-solar-inkhud": "heltec_mesh_solar_base",
"heltec-mesh-solar-oled": "heltec_mesh_solar_base",
"heltec-mesh-solar-tft": "heltec_mesh_solar_base",
"meshlink": "nrf52840",
"meshlinkeink": "nrf52840",
"meshlink_eink": "nrf52840",
"meshtiny": "nrf52840",
"monteopshw1": "nrf52840",
"muzibase": "nrf52840",
"nanog2ultra": "nrf52840",
"nrf52832base": "nrf52",
"nrf52840base": "nrf52",
"r1neo": "nrf52840",
"monteops_hw1": "nrf52840",
"muzi-base": "nrf52840",
"nano-g2-ultra": "nrf52840",
"nrf52832_base": "nrf52",
"nrf52840_base": "nrf52",
"r1-neo": "nrf52840",
"rak2560": "nrf52840",
"rak34011watt": "nrf52840",
"rak3401-1watt": "nrf52840",
"rak4631": "nrf52840",
"rak4631dbg": "rak4631",
"rak4631eink": "nrf52840",
"rak4631einkonrxtx": "nrf52840",
"rak4631ethgw": "nrf52840",
"rak4631ethgwdbg": "rak4631",
"rak4631nomadstarmeteorpro": "nrf52840",
"rak4631nomadstarmeteorprodbg": "rak4631nomadstarmeteorpro",
"rakwismeshtag": "nrf52840",
"rakwismeshtap": "nrf52840",
"seeedsolarnode": "nrf52840",
"seeedwiotrackerL1": "nrf52840",
"seeedwiotrackerL1eink": "nrf52840",
"seeedwiotrackerL1einkinkhud": "nrf52840",
"seeedxiaonrf52840kit": "nrf52840",
"seeedxiaonrf52840kiti2c": "seeedxiaonrf52840kit",
"techo": "nrf52840",
"techoinkhud": "nrf52840",
"techolite": "nrf52840",
"trackert1000e": "nrf52840",
"wiosdkwm1110": "nrf52840",
"wiot1000s": "nrf52840",
"wiotrackerwm1110": "nrf52840",
"challenger2040lora": "rp2040",
"rak4631_dbg": "rak4631",
"rak4631_eink": "nrf52840",
"rak4631_eink_onrxtx": "nrf52840",
"rak4631_eth_gw": "nrf52840",
"rak4631_eth_gw_dbg": "rak4631",
"rak4631_nomadstar_meteor_pro": "nrf52840",
"rak4631_nomadstar_meteor_pro_dbg": "rak4631_nomadstar_meteor_pro",
"rak_wismeshtag": "nrf52840",
"rak_wismeshtap": "nrf52840",
"seeed_solar_node": "nrf52840",
"seeed_wio_tracker_L1": "nrf52840",
"seeed_wio_tracker_L1_eink": "nrf52840",
"seeed_wio_tracker_L1_eink-inkhud": "nrf52840",
"seeed_xiao_nrf52840_kit": "nrf52840",
"seeed_xiao_nrf52840_kit_i2c": "seeed_xiao_nrf52840_kit",
"t-echo": "nrf52840",
"t-echo-inkhud": "nrf52840",
"t-echo-lite": "nrf52840",
"tracker-t1000-e": "nrf52840",
"wio-sdk-wm1110": "nrf52840",
"wio-t1000-s": "nrf52840",
"wio-tracker-wm1110": "nrf52840",
"challenger_2040_lora": "rp2040",
"catsniffer": "rp2040",
"featherrp2040rfm95": "rp2040",
"nibblerp2040": "rp2040",
"feather_rp2040_rfm95": "rp2040",
"nibble-rp2040": "rp2040",
"rak11310": "rp2040",
"rp2040lora": "rp2040",
"rp2040-lora": "rp2040",
"pico": "rp2040",
"picoslowclock": "rp2040",
"pico_slowclock": "rp2040",
"picow": "rp2040",
"senselorarp2040": "rp2040",
"senselora_rp2040": "rp2040",
"pico2": "rp2350",
"pico2w": "rp2350",
"CDEBYTEE77MBL": "stm32",
"CDEBYTE_E77-MBL": "stm32",
"rak3172": "stm32",
"wioe5": "stm32",
"wio-e5": "stm32",
"esp32c3": "esp32",
"esp32c6": "esp32",
"esp32s2": "esp32",
@@ -217,4 +217,4 @@
"rp2350": null,
"stm32": null,
"portduino": null
}
}

View File

@@ -1,4 +1,5 @@
import hardwareList from "@/vendor/web-flasher/public/data/hardware-list.json"
import architectureHierarchy from "@/constants/architecture-hierarchy.json"
import vendorsData from "@/constants/vendors.json"
export interface TargetMetadata {
name: string
@@ -6,17 +7,43 @@ export interface TargetMetadata {
architecture?: string
}
/**
* Trace a target back to its base architecture
*/
function getBaseArchitecture(target: string): string | null {
const parentMap = architectureHierarchy as Record<string, string | null>
const visited = new Set<string>()
let current: string | null = target
while (current && !visited.has(current)) {
visited.add(current)
if (!current) break
const parent: string | null | undefined = parentMap[current]
if (parent === null) {
return current
}
if (parent === undefined) {
return current
}
current = parent
}
return current || target
}
export const TARGETS: Record<string, TargetMetadata> = {}
// Sort by display name
const sortedHardware = [...hardwareList].sort((a, b) => (a.displayName || "").localeCompare(b.displayName || ""))
sortedHardware.forEach(hw => {
if (hw.platformioTarget) {
TARGETS[hw.platformioTarget] = {
name: hw.displayName || hw.platformioTarget,
category: hw.tags?.[0] || "Other",
architecture: hw.architecture,
// Build TARGETS from vendors.json and architecture-hierarchy.json
for (const [vendor, models] of Object.entries(vendorsData)) {
for (const [modelName, target] of Object.entries(models)) {
const architecture = getBaseArchitecture(target)
TARGETS[target] = {
name: modelName,
category: vendor,
architecture: architecture || undefined,
}
}
})
}

244
constants/vendors.json Normal file
View File

@@ -0,0 +1,244 @@
{
"B&Q": {
"Nano G1": "nano-g1",
"Nano G1 Explorer": "nano-g1-explorer",
"Nano G2 Ultra": "nano-g2-ultra",
"Station G1": "station-g1",
"Station G2": "station-g2"
},
"BetaFPV": {
"2400TX Micro": "betafpv_2400_tx_micro",
"900TX Nano": "betafpv_900_tx_nano"
},
"Canary": {
"One": "canaryone"
},
"CDEByte": {
"EoRa S3": "CDEBYTE_EoRa-S3",
"E77MBL": "CDEBYTE_E77-MBL"
},
"DIY": {
"DR-DEV": "meshtastic-dr-dev",
"Hydra": "hydra",
"V1": "meshtastic-diy-v1",
"V1.1": "meshtastic-diy-v1_1",
"NRF52 Pro-micro DIY": "nrf52_promicro_diy_tcxo",
"NRF52 Pro-micro DIY InkHUD": "nrf52_promicro_diy-inkhud",
"PCA10059 DIY E-Ink": "pca10059_diy_eink"
},
"EByte": {
"ESP32-S3": "EBYTE_ESP32-S3"
},
"Elecrow": {
"Crowpanel Adv 2.4/2.8 TFT": "elecrow-adv-24-28-tft",
"Crowpanel Adv 3.5 TFT": "elecrow-adv-35-tft",
"Crowpanel Adv 4.3/5.0/7.0 TFT": "elecrow-adv1-43-50-70-tft",
"ThinkNode M1": "thinknode_m1",
"ThinkNode M2": "thinknode_m2",
"ThinkNode M3": "thinknode_m3",
"ThinkNode M5": "thinknode_m5",
"ThinkNode M6": "thinknode_m6"
},
"Heltec": {
"V1": "heltec-v1",
"V2.0": "heltec-v2_0",
"V2.1": "heltec-v2_1",
"V3": "heltec-v3",
"V4": "heltec-v4",
"V4 TFT": "heltec-v4-tft",
"HT62": "heltec-ht62-esp32c3-sx1262",
"RU3601": "heltec-hru-3601",
"Wireless Bridge": "heltec-wireless-bridge",
"Wireless Stick Lite V2.1": "heltec-wsl-v2_1",
"Wireless Stick Lite V3": "heltec-wsl-v3",
"Wireless Paper": "heltec-wireless-paper",
"Wireless Paper InkHUD": "heltec-wireless-paper-inkhud",
"Wireless Paper V1.0": "heltec-wireless-paper-v1_0",
"Wireless Tracker": "heltec-wireless-tracker",
"Wireless Tracker V1.0": "heltec-wireless-tracker-V1-0",
"Wireless Tracker V2": "heltec-wireless-tracker-v2",
"Vision Master E213": "heltec-vision-master-e213",
"Vision Master E213 InkHUD": "heltec-vision-master-e213-inkhud",
"Vision Master E290": "heltec-vision-master-e290",
"Vision Master E290 InkHUD": "heltec-vision-master-e290-inkhud",
"Vision Master T190": "heltec-vision-master-t190",
"Capsule Sensor V3": "heltec_capsule_sensor_v3",
"Sensor Hub": "heltec_sensor_hub",
"Mesh Node T114": "heltec-mesh-node-t114",
"Mesh Node T114 InkHUD": "heltec-mesh-node-t114-inkhud",
"MeshPocket 5000": "heltec-mesh-pocket-5000",
"MeshPocket 5000 InkHUD": "heltec-mesh-pocket-5000-inkhud",
"MeshPocket 10000": "heltec-mesh-pocket-10000",
"MeshPocket 10000 InkHUD": "heltec-mesh-pocket-10000-inkhud",
"MeshSolar": "heltec-mesh-solar",
"MeshSolar E-Ink": "heltec-mesh-solar-eink",
"MeshSolar InkHUD": "heltec-mesh-solar-inkhud",
"MeshSolar OLED": "heltec-mesh-solar-oled",
"MeshSolar TFT": "heltec-mesh-solar-tft"
},
"HackerBoxes": {
"ESP32 IO": "hackerboxes-esp32-io",
"ESP32-C3 OLED": "hackerboxes-esp32c3-oled"
},
"LilyGo": {
"T-Beam": "tbeam",
"T-Beam Display Shield": "tbeam-displayshield",
"T-Beam V0.7": "tbeam0_7",
"T-Beam S3 Core": "tbeam-s3-core",
"T-Deck": "t-deck",
"T-Deck TFT": "t-deck-tft",
"T-Deck Pro": "t-deck-pro",
"T-Echo": "t-echo",
"T-Echo InkHUD": "t-echo-inkhud",
"T-Echo Lite": "t-echo-lite",
"T-LoRa V1": "tlora-v1",
"T-LoRa V1.3": "tlora_v1_3",
"T-LoRa V2": "tlora-v2",
"T-LoRa V2.1-1.6": "tlora-v2-1-1_6",
"T-LoRa V2.1-1.6 TCXO": "tlora-v2-1-1_6-tcxo",
"T-LoRa V2.1-1.8": "tlora-v2-1-1_8",
"T-LoRa V3.3.0 TCXO": "tlora-v3-3-0-tcxo",
"T-LoRa C6": "tlora-c6",
"T-LoRa Pager": "tlora-pager",
"T-LoRa Pager TFT": "tlora-pager-tft",
"T-LoRa T3-S3": "tlora-t3s3-v1",
"T-LoRa T3-S3 E-Paper": "tlora-t3s3-epaper",
"T-LoRa T3-S3 E-Paper InkHUD": "tlora-t3s3-epaper-inkhud",
"T-Watch S3": "t-watch-s3",
"Sugar Cube": "sugarcube"
},
"M5Stack": {
"Core": "m5stack-core",
"Core Ink": "m5stack-coreink",
"Core S3": "m5stack-cores3",
"Stamp C3": "m5stack-stamp-c3",
"Unit C6L": "m5stack-unitc6l"
},
"MakerPython": {
"NRF52840 SX1280 E-Ink": "makerpython_nrf52840_sx1280_eink",
"NRF52840 SX1280 OLED": "makerpython_nrf52840_sx1280_oled"
},
"muzi": {
"R1 Neo": "r1-neo"
},
"NomadStar": {
"Meteor Pro": "rak4631_nomadstar_meteor_pro",
"Meteor Pro Debug": "rak4631_nomadstar_meteor_pro_dbg"
},
"RAK": {
"WisBlock 11200": "rak11200",
"WisBlock 11310": "rak11310",
"WisBlock 4631": "rak4631",
"WisBlock 4631 Debug": "rak4631_dbg",
"WisBlock 4631 E-Ink": "rak4631_eink",
"WisBlock 4631 E-Ink on RX/TX": "rak4631_eink_onrxtx",
"WisBlock 4631 ETH Gateway": "rak4631_eth_gw",
"WisBlock 4631 ETH Gateway Debug": "rak4631_eth_gw_dbg",
"WisBlock 3312": "rak3312",
"WisBlock 3172": "rak3172",
"WisBlock 2560": "rak2560",
"WisBlock 3401 1Watt": "rak3401-1watt",
"WisMesh Tag": "rak_wismeshtag",
"WisMesh Tap": "rak_wismeshtap",
"WisMesh Tap V2 TFT": "rak_wismesh_tap_v2-tft"
},
"RadioMaster": {
"900 Bandit": "radiomaster_900_bandit",
"900 Bandit Micro": "radiomaster_900_bandit_micro",
"900 Bandit Nano": "radiomaster_900_bandit_nano"
},
"RPi": {
"Pico": "pico",
"Pico Slow Clock": "pico_slowclock",
"Pico W": "picow",
"Pico 2": "pico2",
"Pico 2W": "pico2w"
},
"Seeed": {
"Xiao ESP32-S3": "seeed-xiao-s3",
"Xiao NRF52840 Kit": "seeed_xiao_nrf52840_kit",
"Xiao NRF52840 Kit I2C": "seeed_xiao_nrf52840_kit_i2c",
"Xiao NRF52840 WIO SX1262": "seeed-xiao-nrf52840-wio-sx1262",
"Xiao NRF52840 E22900M30S": "seeed_xiao_nrf52840_e22_900m30s",
"Xiao NRF52840 E22900M33S": "seeed_xiao_nrf52840_e22_900m33s",
"Xiao BLE": "xiao_ble",
"Wio Tracker L1": "seeed_wio_tracker_L1",
"Wio Tracker L1 E-Ink": "seeed_wio_tracker_L1_eink",
"Wio Tracker L1 E-Ink InkHUD": "seeed_wio_tracker_L1_eink-inkhud",
"Wio Tracker WM1110": "wio-tracker-wm1110",
"Wio SDK WM1110": "wio-sdk-wm1110",
"Wio T1000S": "wio-t1000-s",
"SenseCAP Indicator": "seeed-sensecap-indicator",
"SenseCAP Indicator TFT": "seeed-sensecap-indicator-tft",
"Solar Node": "seeed_solar_node"
},
"Waveshare": {
"RP2040 LoRa": "rp2040-lora"
},
"Other": {
"Chatter2": "chatter2",
"9M2 IBRA PRS LoRa Tracker": "9m2ibr_aprs_lora_tracker",
"AIC3": "ai-c3",
"ESP32-C3": "esp32c3",
"ESP32-C3 Super Mini": "esp32c3_super_mini",
"ESP32-C6": "esp32c6",
"ESP32-S2": "esp32s2",
"ESP32-S3": "esp32s3",
"ESP32-S3 Pico": "ESP32-S3-Pico",
"Crowpanel ESP32-S3 2 E-Paper": "crowpanel-esp32s3-2-epaper",
"Crowpanel ESP32-S3 4 E-Paper": "crowpanel-esp32s3-4-epaper",
"Crowpanel ESP32-S3 5 E-Paper": "crowpanel-esp32s3-5-epaper",
"BPi Pico W ESP32-S3": "bpi_picow_esp32_s3",
"My ESP32-S3 DIY E-Ink": "my-esp32s3-diy-eink",
"My ESP32-S3 DIY OLED": "my-esp32s3-diy-oled",
"T-Energy S3 E22": "t-energy-s3_e22",
"Dreamcatcher 2206": "dreamcatcher-2206",
"Hackaday Communicator": "hackaday-communicator",
"Icarus": "icarus",
"Link32S3 V1": "link32-s3-v1",
"MeshTab 32 TN Resistive": "mesh-tab-3-2-TN-resistive",
"MeshTab 32 IPS Resistive": "mesh-tab-3-2-IPS-resistive",
"MeshTab 35 IPS Resistive": "mesh-tab-3-5-IPS-resistive",
"MeshTab 35 TN Resistive": "mesh-tab-3-5-TN-resistive",
"MeshTab 32 IPS Capacitive": "mesh-tab-3-2-IPS-capacitive",
"MeshTab 35 IPS Capacitive": "mesh-tab-3-5-IPS-capacitive",
"MeshTab 40 IPS Capacitive": "mesh-tab-4-0-IPS-capacitive",
"Nibble ESP32": "nibble-esp32",
"Nugget S2 LoRa": "nugget-s2-lora",
"Nugget S3 LoRa": "nugget-s3-lora",
"Pi Computer S3": "picomputer-s3",
"Pi Computer S3 TFT": "picomputer-s3-tft",
"T-Echo Lite": "t-eth-elite",
"Tracksenger": "tracksenger",
"Tracksenger LCD": "tracksenger-lcd",
"Tracksenger OLED": "tracksenger-oled",
"unPhone": "unphone",
"unPhone TFT": "unphone-tft",
"NRF52832": "nrf52832",
"NRF52840": "nrf52840",
"ThinkNode M1 InkHUD": "thinknode_m1-inkhud",
"ME25LS014Y10TD": "ME25LS01-4Y10TD",
"ME25LS014Y10TD E-Ink": "ME25LS01-4Y10TD_e-ink",
"MS24SF1": "ms24sf1",
"TWC Mesh V4": "TWC_mesh_v4",
"WashTastic": "WashTastic",
"Feather DIY": "feather_diy",
"Feather RP2040 RFM95": "feather_rp2040_rfm95",
"GAT562 Mesh Trial Tracker": "gat562_mesh_trial_tracker",
"MeshLink": "meshlink",
"MeshLink E-Ink": "meshlink_eink",
"MeshTiny": "meshtiny",
"MonteOps HW1": "monteops_hw1",
"R1 Neo": "r1-neo",
"Tracker T1000E": "tracker-t1000-e",
"Tracker D": "trackerd",
"WiPhone": "wiphone",
"Challenger 2040 LoRa": "challenger_2040_lora",
"Cat Sniffer": "catsniffer",
"Nibbler RP2040": "nibble-rp2040",
"SenseLoRa RP2040": "senselora_rp2040",
"Wio E5": "wio-e5",
"Coverage": "coverage",
"Buildroot": "buildroot"
}
}

View File

@@ -127,30 +127,19 @@ export function isRequiredByOther(
return false
}
/**
* Normalize architecture name (remove hyphens and underscores to match PlatformIO format)
* PlatformIO uses "esp32s3", "nrf52840" (no hyphens, no underscores)
* Hardware list uses "esp32-s3" (with hyphens)
* Some sources might use "esp32_s3" (with underscores)
*/
function normalizeArchitecture(arch: string): string {
return arch.replace(/[-_]/g, "")
}
/**
* Trace a target/variant/architecture back to its base architecture
* Follows the parent chain until it reaches a base architecture (null parent)
*/
export function getBaseArchitecture(name: string): string | null {
const normalized = normalizeArchitecture(name)
const parentMap = PARENT_MAP as Record<string, string | null>
const visited = new Set<string>()
let current = normalized
let current: string | null = name
while (current && !visited.has(current)) {
visited.add(current)
const parent = parentMap[current]
if (!current) break
const parent: string | null | undefined = parentMap[current]
// If parent is null, we've reached a base architecture
if (parent === null) {
@@ -162,11 +151,11 @@ export function getBaseArchitecture(name: string): string | null {
return current
}
current = normalizeArchitecture(parent)
current = parent
}
// Circular reference or unknown, return the last known
return current || normalized
return current || name
}
/**
@@ -174,17 +163,16 @@ export function getBaseArchitecture(name: string): string | null {
* (including itself and all parent architectures up to base)
*/
export function getCompatibleArchitectures(arch: string): string[] {
const normalized = normalizeArchitecture(arch)
const parentMap = PARENT_MAP as Record<string, string | null>
const compatible = [normalized]
const compatible = [arch]
const visited = new Set<string>()
let current = normalized
let current: string | null = arch
// Follow parent chain up to base architecture
while (current && !visited.has(current)) {
visited.add(current)
const parent = parentMap[current]
if (!current) break
const parent: string | null | undefined = parentMap[current]
if (parent === null) {
// Reached base architecture
@@ -196,12 +184,11 @@ export function getCompatibleArchitectures(arch: string): string[] {
break
}
const normalizedParent = normalizeArchitecture(parent)
if (!compatible.includes(normalizedParent)) {
compatible.push(normalizedParent)
if (!compatible.includes(parent)) {
compatible.push(parent)
}
current = normalizedParent
current = parent
}
return compatible
@@ -227,18 +214,16 @@ export function isPluginCompatibleWithTarget(
const parentMap = PARENT_MAP as Record<string, string | null>
// Normalize target name first (all keys in parentMap are normalized)
const normalizedTarget = normalizeArchitecture(targetName)
// Get all compatible names for the target (target itself + all parents up to base architecture)
const compatibleNames = new Set<string>([normalizedTarget])
const compatibleNames = new Set<string>([targetName])
const visited = new Set<string>()
let current = normalizedTarget
let current: string | null = targetName
// Follow parent chain (all keys and values in parentMap are already normalized)
// Follow parent chain
while (current && !visited.has(current)) {
visited.add(current)
const parent = parentMap[current]
if (!current) break
const parent: string | null | undefined = parentMap[current]
if (parent === null) {
// Reached base architecture
@@ -251,30 +236,21 @@ export function isPluginCompatibleWithTarget(
break
}
// Parent is already normalized (from JSON)
compatibleNames.add(parent)
current = parent
}
// Check excludes first - if target matches any exclude, it's incompatible
// compatibleNames are already normalized, normalize excludes for comparison
if (pluginExcludes && pluginExcludes.length > 0) {
const isExcluded = pluginExcludes.some(exclude => {
const normalizedExclude = normalizeArchitecture(exclude)
return compatibleNames.has(normalizedExclude)
})
const isExcluded = pluginExcludes.some(exclude => compatibleNames.has(exclude))
if (isExcluded) {
return false
}
}
// If includes are specified, target must match at least one include
// compatibleNames are already normalized, normalize includes for comparison
if (pluginIncludes && pluginIncludes.length > 0) {
return pluginIncludes.some(include => {
const normalizedInclude = normalizeArchitecture(include)
return compatibleNames.has(normalizedInclude)
})
return pluginIncludes.some(include => compatibleNames.has(include))
}
// If no includes/excludes specified, assume compatible with all (backward compatible)
@@ -301,34 +277,30 @@ export function isPluginCompatibleWithArchitecture(
export function getTargetsCompatibleWithIncludes(includes: string[]): Set<string> {
const parentMap = PARENT_MAP as Record<string, string | null>
const compatibleTargets = new Set<string>()
// Normalize includes
const normalizedIncludes = new Set(includes.map(include => normalizeArchitecture(include)))
const includesSet = new Set(includes)
// For each target in the parent map, check if it or any of its ancestors match the includes
for (const target of Object.keys(parentMap)) {
const normalizedTarget = normalizeArchitecture(target)
const visited = new Set<string>()
let current: string | null = normalizedTarget
let current: string | null = target
// Trace up the parent chain
while (current && !visited.has(current)) {
visited.add(current)
if (!current) break
// Check if current matches any of the includes
if (normalizedIncludes.has(current)) {
// Add both the normalized version and the original (for matching against TARGETS)
compatibleTargets.add(normalizedTarget)
if (includesSet.has(current)) {
compatibleTargets.add(target)
break
}
// Move to parent
const parentValue = parentMap[current]
if (parentValue === null || parentValue === undefined) {
const parent: string | null | undefined = parentMap[current]
if (parent === null || parent === undefined) {
break
}
current = normalizeArchitecture(parentValue)
current = parent
}
}

View File

@@ -9,14 +9,6 @@ const FIRMWARE_DIR = path.resolve(__dirname, "../vendor/firmware")
const VARIANTS_DIR = path.join(FIRMWARE_DIR, "variants")
const OUTPUT_FILE = path.resolve(__dirname, "../constants/architecture-hierarchy.json")
/**
* Normalize architecture/target name (remove hyphens and underscores)
* This ensures consistent format matching PlatformIO architecture names
*/
function normalizeName(name) {
return name.replace(/[-_]/g, "")
}
/**
* Parse PlatformIO ini file to extract sections and their properties
*/
@@ -342,15 +334,8 @@ function buildParentMapping() {
delete resolvedParentMap[key]
}
// Normalize all keys and values (strip hyphens and underscores)
const normalizedMap = {}
for (const [key, value] of Object.entries(resolvedParentMap)) {
const normalizedKey = normalizeName(key)
const normalizedValue = value !== null ? normalizeName(value) : null
normalizedMap[normalizedKey] = normalizedValue
}
return normalizedMap
// Return map with actual PlatformIO environment names (no normalization)
return resolvedParentMap
}
/**