The viz package existed solely to render one interactive HTML chart for
tx-optimize sweeps, pulling in Plotly — a heavyweight dependency serving a
single artifact while every other timeline renders in-terminal via
braillechart. Drop it with no replacement: delete meshterm/viz, the
--viz/--no-viz flag and its render call, the now-unused output_dir setting
(config field, default, loader, example), the Plotly test, and the README
and pyproject references.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add TCP alongside serial and BLE as a first-class companion transport, end to
end: discovery/connection/selection learn a tcp: stable_id and host:port target,
--tcp and TCP config profiles reach a WiFi board or network proxy, and a network
companion -- which isn't discoverable -- is rebuilt into the picker from its
remembered endpoint.
Device picker gains the network-device affordances:
- "Add a network device" prompts for host:port, smoke-tests it, and remembers it.
The row is marked "- experimental" in the cautionary hue.
- Delete on a network row opens a chromeless Cancel/Remove confirm (Cancel left,
Remove right and default: Enter removes, Esc cancels), then DeviceStore.forget
prunes it and re-draws the list. Serial/BLE rows stay inert.
- The "Del remove" footer atom now follows the highlight, surfacing on the box's
bottom border only while the cursor sits on a deletable row; the box is sized
to the fullest footer so it never widens mid-navigation.
Folds in the accompanying courier, channels, and config refinements built along
the way. Screens stay readable at 72 cols; full suite green (745 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The monitor seeds its six-hour activity buckets and its by-kind tallies
from stored observations at session start, so the header pulse, the
dashboard chart, and the traffic panel open mid-story after a restart
instead of counting from zero. A once-per-session sweep prunes
observations past the new history_days retention setting (default 365,
0 = keep forever), keeping the always-recording database bounded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A direct message was transmitted once and marked failed the moment its
single delivery-ack window lapsed, leaving Ctrl-R as the only recourse.
Now the send path re-sends an unacknowledged message automatically before
giving up, matching the mesh convention of a few tries.
_deliver_direct loops send_direct_message until an ack lands or the budget
is spent; both the initial send and the manual Ctrl-R resend route through
it. Each attempt already blocks a full ack window before the next, so the
retries are spaced by that window rather than hammering the radio, and a
hard send rejection propagates without retry.
The budget is the new direct_message_soft_retries setting: retries + 1
tries, clamped to 0..2 so a message is never re-broadcast more than twice
(0 = one shot, default 2 = up to three tries).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Companions can now be reached over Bluetooth as well as USB. Rather than
a parallel BLE path, this generalizes the existing serial abstractions so
a device is identified by a transport (serial | ble) plus a target (COM
port or Bluetooth address); every feature rides the same Device interface
and works over either link unchanged.
- discovery: DiscoveredDevice gains transport/address/name and
transport-aware stable_id/label/confidence/target; discover_ble_devices
scans for MeshCore* adverts via bleak, discover_all merges both.
- connection: MeshCoreDevice opens create_ble vs create_serial by
transport; new link_present() liveness (OS port enumeration for serial,
the BLE client's is_connected flag for Bluetooth); is_connection_lost
recognizes Bleak errors and BLE transport callbacks; probe_device is the
transport-aware smoke test.
- disconnect: the session watcher polls transport-neutral link_alive(), so
a dropped BLE link raises the same reconnect dialog and auto-resume as an
unplugged cable; reconnect rebuilds the same endpoint by address.
- selection/context/store/profiles: --ble address, remembered BLE default,
and BLE device profiles all resolve to the right transport.
- UI/CLI: startup picker scans both transports behind a spinner and tags
Bluetooth companions; `devices` lists them (--no-ble to skip); global
--ble/--ble-pin flags; README and example config updated.
Validated on hardware: discovery, connect, reconnect, and channel
messaging (send + receive) over BLE.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the default config directory from ~/.config/meshterm (with
XDG_CONFIG_HOME support) to a plain ~/.meshterm dotfolder under the
OS-defined home directory.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the package directory, imports, brand strings, env-var prefixes,
console-script entry point, and the config/data dir (~/.config/meshterm).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>