Technician tool v0.1
Imported from the Google Drive folder 004-technician_tool/v0.1.
This commit is contained in:
commit
e5a33a1c34
28 files changed
+2988
No files matched your search
@@ -0,0 +1,76 @@
|
||||
"""Serial port discovery.
|
||||
|
||||
Technicians connect through the ESP32-S3 native USB (USB-Serial-JTAG,
|
||||
VID 0x303A / PID 0x1001). Its USB serial number is the chip MAC, which
|
||||
is stable across resets and power cycles, so it is used to re-find the
|
||||
same box after the port disappears (power switch toggled).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
|
||||
from serial.tools import list_ports
|
||||
|
||||
ESPRESSIF_VID = 0x303A
|
||||
USB_SERIAL_JTAG_PID = 0x1001
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PortInfo:
|
||||
device: str
|
||||
description: str
|
||||
vid: int | None
|
||||
pid: int | None
|
||||
serial_number: str | None
|
||||
|
||||
@property
|
||||
def is_sensorbox(self) -> bool:
|
||||
return self.vid == ESPRESSIF_VID
|
||||
|
||||
@property
|
||||
def mac(self) -> str | None:
|
||||
"""USB-Serial-JTAG reports the MAC as 'AA:BB:CC:DD:EE:FF'."""
|
||||
sn = self.serial_number or ""
|
||||
parts = sn.split(":")
|
||||
if len(parts) == 6 and all(len(p) == 2 for p in parts):
|
||||
return sn.upper()
|
||||
return None
|
||||
|
||||
@property
|
||||
def label(self) -> str:
|
||||
if self.is_sensorbox:
|
||||
kind = "Sensorbox (native USB)" if self.pid == USB_SERIAL_JTAG_PID else "Espressif USB"
|
||||
return f"{self.device} - {kind}" + (f" [{self.mac}]" if self.mac else "")
|
||||
return f"{self.device} - {self.description}"
|
||||
|
||||
|
||||
def _from_pyserial(p) -> PortInfo:
|
||||
return PortInfo(p.device, p.description or "", p.vid, p.pid, p.serial_number)
|
||||
|
||||
|
||||
def list_serial_ports(include_all: bool = False) -> list[PortInfo]:
|
||||
ports = [_from_pyserial(p) for p in list_ports.comports()]
|
||||
if sys.platform == "darwin":
|
||||
# Each macOS device appears twice; /dev/tty.* blocks on open waiting
|
||||
# for carrier detect, /dev/cu.* is the right one for us.
|
||||
ports = [p for p in ports if not p.device.startswith("/dev/tty.")]
|
||||
if not include_all:
|
||||
ports = [p for p in ports if p.is_sensorbox]
|
||||
# Sensorboxes first, then by name.
|
||||
return sorted(ports, key=lambda p: (not p.is_sensorbox, p.device))
|
||||
|
||||
|
||||
def find_port(serial_number: str | None, device: str | None) -> PortInfo | None:
|
||||
"""Re-locate a board: prefer the USB serial number (MAC), fall back to name."""
|
||||
ports = list_serial_ports(include_all=True)
|
||||
if serial_number:
|
||||
for p in ports:
|
||||
if p.serial_number == serial_number:
|
||||
return p
|
||||
if device:
|
||||
for p in ports:
|
||||
if p.device == device:
|
||||
return p
|
||||
return None
|
||||
Reference in new issue
Block a user