fix(hardware): restore informative serial-port names (Arduino IDE parity) - #947
Conversation
…ity) Serial ports have shown only the vendor/manufacturer string since 4.2.3 (the xml2st retirement, #843), collapsing every port of the same vendor to an identical label. The old xml2st path used pyserial's `description` (friendly name, distinct per port); the rewrite mapped `name` to `serialport`'s `manufacturer` field instead, which drops the per-port distinction. Mimic the Arduino IDE: the device path is always the primary, unique, cross-platform label, enriched with a descriptor in parentheses when available. `getAvailableSerialPorts()` now runs two best-effort scans in parallel and merges them by path: - arduino-cli `board list --format json` identifies the connected board from the installed core's VID/PID (boards.txt) -> "COM3 (Arduino Uno)". Reuses the module's existing binary path + --config-file, so it sees the same cores as compile/upload. - serialport supplies the reliable port set plus a manufacturer fallback -> "COM6 (com0com - serial port emulator)"; bare path when unknown. Each scan degrades independently (empty map on failure), so a missing binary / no cores / malformed JSON still yields plain serialport output. The merge/label rules live in a pure `mergeSerialPortList()` helper with unit tests. No shared surface, port interface, or {name,address} shape changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 43 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughSerial port discovery now performs concurrent Arduino CLI and serial-port scans, reconciles macOS device paths, merges results, and labels ports using board names or manufacturers. Tests cover fallback labeling, ordering, deduplication, and empty scans. ChangesSerial port discovery
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant getAvailableSerialPorts
participant arduino-cli
participant NodeSerialPort
participant mergeSerialPortList
getAvailableSerialPorts->>arduino-cli: board list --format json
getAvailableSerialPorts->>NodeSerialPort: list serial ports
arduino-cli-->>getAvailableSerialPorts: board paths and names
NodeSerialPort-->>getAvailableSerialPorts: port paths and manufacturers
getAvailableSerialPorts->>mergeSerialPortList: merge scan maps
mergeSerialPortList-->>getAvailableSerialPorts: deduplicated SerialPort[]
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
macOS exposes each serial device twice — a call-in node (/dev/tty.*) and a call-out node (/dev/cu.*). serialport reports the tty.* node (with the manufacturer) while arduino-cli reports the cu.* node (with the board id), so the two scans keyed on different paths and the same device appeared twice in the dropdown (e.g. "/dev/tty.usbmodem11301 (Arduino)" AND "/dev/cu.usbmodem11301 (Opta)"). Group the two scans on the shared suffix after the tty./cu. prefix and emit a single entry per device, displaying the call-out (cu.*) node — the one used for talking to a device and the one the Arduino IDE selects. Board name still wins over the manufacturer descriptor. Linux (/dev/ttyUSB0) and Windows (COM3) paths don't match the dotted pattern and are never merged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the fragile tty./cu. cross-node grouping with a single canonicalization: `toCalloutPath` rewrites `/dev/tty.` -> `/dev/cu.` before merging. serialport's native macOS binding hardcodes the dial-in (tty.*) node while arduino-cli reports the call-out (cu.*) node; rewriting tty. -> cu. at the source makes both scans agree on one path, so the merge collapses back to a plain union deduped by path. The call-out (cu.*) node is the one callers must use to talk to a device and the one the Arduino IDE selects; IOKit always publishes both nodes for a serial service, so the rewrite is guaranteed to resolve to a real device. node-serialport itself can't be configured to emit cu.* (the path is hardcoded in the compiled binding, `darwin_list.cpp` kIODialinDeviceKey; serialport#1729), so canonicalizing in JS is the build-free equivalent. Linux (/dev/ttyUSB0) and Windows (COM3) paths don't match the dotted pattern and pass through untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Format Check (Prettier) collapsed a single-object toEqual([...]) that fits within the 120-col width onto one line. No behavioural change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem
Since 4.2.3 (the xml2st retirement, #843), the Communication Port dropdown shows only the vendor/manufacturer string, so every port of the same vendor collapses to an identical label the user can't tell apart. Reported on the forum: works on 4.1.4, broken on 4.2.3→4.2.8.
Root cause: the old
xml2st --list-portspath used pyserial'sdescription(the friendly name, distinct per port). The rewrite to theserialportnpm package mappednametoport.manufacturer(vendor only), dropping the per-port distinction.Fix — mimic the Arduino IDE
The device path is always the primary, unique, cross-platform label, enriched with a descriptor in parentheses when available.
getAvailableSerialPorts()runs two best-effort scans in parallel and merges them by path:arduino-cli board list --format json— identifies the connected board from the installed core's VID/PID (boards.txt), exactly like the Arduino IDE →COM3 (Arduino Uno). Reuses the module's existing binary path +--config-file, so it sees the same cores as compile/upload.serialport— supplies the reliable port set plus the manufacturer fallback →COM6 (com0com - serial port emulator); bare path when nothing is known.Each scan degrades independently (empty map on failure), so a missing binary / no installed cores / malformed JSON still yields plain
serialportoutput. Running both is cheap: the list is static after build and only re-scanned on explicit user refresh.Resulting labels:
COM3 (Arduino Uno)→COM6 (com0com - serial port emulator)→/dev/ttyUSB0. The regression can't recur because the path always leads.Files
serial-port-list.ts(new) — puremergeSerialPortList()helper (no IO/electron) holding the label/merge rules.hardware-module.ts— two-scan enumeration +#identifyBoardsByPath()/#listSerialPortManufacturers().__tests__/serial-port-list.test.ts(new) — 7 tests: all label tiers, board-over-manufacturer preference, union/dedup/ordering, empty-descriptor, empty list.Scope / compatibility
frontend,middleware/shared,backend/shared,__architecture__) touched; theDevicePortinterface and{ name, address }shape are unchanged — no openplc-web mirror change needed.[]for local port enumeration by design (browsers can't enumerate local serial ports).Verification
npm run validate:arch— passed (no layer violations)tsc --noEmit— 0 errors project-wide🤖 Generated with Claude Code
Summary by CodeRabbit
Improvements
/dev/tty.*and/dev/cu.*entries, preferring the/dev/cu.*address and board name when available.Tests