MeshTools is a Rich+Questionary interactive / Typer-scriptable toolkit for tuning a MeshCore mesh over a serial companion device, with SQLite-logged runs and Plotly visualizations. Includes the skeleton (trace, info, history), TX-power optimization, and a MockDevice simulator (--mock). Device discovery & selection: - core/discovery.py: enumerate serial ports (pyserial), flag/sort likely LoRa hardware by USB vendor id, stable_id for replug-safe identity. - core/device_store.py: remember the last successfully connected device in <config_dir>/devices.json (independent of --db). - core/selection.py: non-interactive resolver (--port > profile > remembered > sole device > guidance error) for the CLI. - ui/device_picker.py + menu startup: interactive picker preselecting the remembered default; banner reflects the choice. - tools/devices.py: `meshtools devices` lists/marks attached devices. - AppContext resolves the port and records last-known-good on connect success. Fixes: - theme.py: force UTF-8 on stdout/stderr so glyphs don't crash legacy consoles. - connection.py: friendly message when meshcore is missing; pin meshcore>=2.3. - trace/tx-optimize: fall back to free-text target entry when the device reports no contacts (autocomplete requires non-empty choices). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
1.7 KiB
Python
61 lines
1.7 KiB
Python
"""Shared Rich theme and console factory for a consistent, modern look."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from rich.console import Console
|
|
from rich.theme import Theme
|
|
|
|
MESH_THEME = Theme(
|
|
{
|
|
"brand": "bold #5eead4",
|
|
"accent": "bold #818cf8",
|
|
"ok": "bold #4ade80",
|
|
"warn": "bold #fbbf24",
|
|
"err": "bold #f87171",
|
|
"muted": "#94a3b8",
|
|
"snr.good": "bold #4ade80",
|
|
"snr.ok": "bold #fbbf24",
|
|
"snr.bad": "bold #f87171",
|
|
}
|
|
)
|
|
|
|
|
|
def make_console() -> Console:
|
|
"""Create the application's themed Rich console.
|
|
|
|
On legacy Windows consoles the standard streams default to ``cp1252``, which cannot
|
|
encode the box-drawing and marker glyphs (``◆ ● ★``) the UI uses; this reconfigures
|
|
them to UTF-8 where the runtime supports it so output never raises ``UnicodeEncodeError``.
|
|
|
|
Returns:
|
|
A :class:`rich.console.Console` configured with the MeshTools theme.
|
|
"""
|
|
import sys
|
|
|
|
for stream in (sys.stdout, sys.stderr):
|
|
reconfigure = getattr(stream, "reconfigure", None)
|
|
if reconfigure is not None:
|
|
try:
|
|
reconfigure(encoding="utf-8")
|
|
except (ValueError, OSError): # pragma: no cover - stream not reconfigurable
|
|
pass
|
|
return Console(theme=MESH_THEME)
|
|
|
|
|
|
def snr_style(snr: float | None) -> str:
|
|
"""Return a theme style name describing an SNR value's quality.
|
|
|
|
Args:
|
|
snr: An SNR reading in dB, or ``None``.
|
|
|
|
Returns:
|
|
``"snr.good"``, ``"snr.ok"``, ``"snr.bad"``, or ``"muted"`` for ``None``.
|
|
"""
|
|
if snr is None:
|
|
return "muted"
|
|
if snr >= 5:
|
|
return "snr.good"
|
|
if snr >= -5:
|
|
return "snr.ok"
|
|
return "snr.bad"
|