c8dac10348
* Start of MCP server and test suite * Add MCP server for interacting with meshtastic devices and testing framework / TUI * Update mcp-server/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix mcp-server review feedback from thread Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/91dc128a-ed50-4d07-8bb2-3dc6623a05f7 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Enhance StreamAPI and PhoneAPI for improved log record handling and concurrency control * Semgrep fixes * Trunk and semgrep fixes * optimize pio streaming tee file writes Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/04e26c6b-6a2b-45be-bbeb-79ae4d0be633 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * chore: remove redundant log handle assignment Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/04e26c6b-6a2b-45be-bbeb-79ae4d0be633 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Consolidate type imports and remove placeholder test files * Add tests for config persistence and more exchange messages * Refactor position test to validate on-demand request/reply behavior * Remove position request/reply test and update README for telemetry behavior * Fix transmit history file to get removed on factory reset --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
76 lines
2.9 KiB
Python
76 lines
2.9 KiB
Python
"""USB/serial device discovery.
|
|
|
|
Combines the canonical `meshtastic.util.findPorts()` allowlist/blocklist with
|
|
the richer metadata (`serial.tools.list_ports.comports()`) so callers see
|
|
VID/PID, descriptions, and manufacturer strings alongside the "is this likely
|
|
a Meshtastic device" signal.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from serial.tools import list_ports
|
|
|
|
|
|
def _to_hex(value: int | None) -> str | None:
|
|
if value is None:
|
|
return None
|
|
return f"0x{value:04x}"
|
|
|
|
|
|
def list_devices(include_unknown: bool = False) -> list[dict[str, Any]]:
|
|
"""Return enriched info for serial ports, flagging Meshtastic candidates.
|
|
|
|
`likely_meshtastic` is True when the port's USB VID matches the Meshtastic
|
|
allowlist (`0x239a` Adafruit/RAK, `0x303a` Espressif). When no allowlisted
|
|
ports are present, ports whose VID is NOT in the blocklist (J-Link, ST-LINK,
|
|
PPK2, etc.) are surfaced as `likely_meshtastic=False` candidates.
|
|
|
|
With `include_unknown=False` (default), we return only ports that are
|
|
plausibly Meshtastic. With `include_unknown=True`, every serial port the
|
|
OS knows about is returned (useful for debugging "why isn't my board
|
|
detected").
|
|
"""
|
|
# Import lazily so the module loads even without the `meshtastic` package
|
|
# (useful for introspection / schema generation).
|
|
from meshtastic import util as mt_util # type: ignore[import-untyped]
|
|
|
|
meshtastic_ports: set[str] = set(mt_util.findPorts(eliminate_duplicates=True))
|
|
whitelist = getattr(mt_util, "whitelistVids", {})
|
|
blacklist = getattr(mt_util, "blacklistVids", {})
|
|
|
|
results: list[dict[str, Any]] = []
|
|
for info in list_ports.comports():
|
|
port_path = info.device
|
|
vid = info.vid
|
|
in_whitelist = vid is not None and vid in whitelist
|
|
in_blacklist = vid is not None and vid in blacklist
|
|
|
|
likely = port_path in meshtastic_ports and in_whitelist
|
|
# If no allowlisted ports were found, findPorts falls back to
|
|
# everything-not-in-blacklist; treat those as plausible candidates
|
|
# but not "likely".
|
|
fallback_candidate = port_path in meshtastic_ports and not in_whitelist
|
|
|
|
if not likely and not fallback_candidate and not include_unknown:
|
|
continue
|
|
|
|
results.append(
|
|
{
|
|
"port": port_path,
|
|
"vid": _to_hex(vid),
|
|
"pid": _to_hex(info.pid),
|
|
"description": info.description or None,
|
|
"manufacturer": info.manufacturer or None,
|
|
"product": info.product or None,
|
|
"serial_number": info.serial_number or None,
|
|
"likely_meshtastic": likely,
|
|
"blacklisted": in_blacklist,
|
|
}
|
|
)
|
|
|
|
# Stable ordering: likely_meshtastic first, then by port path
|
|
results.sort(key=lambda r: (not r["likely_meshtastic"], r["port"]))
|
|
return results
|