Commit graph

9 commits

Author SHA1 Message Date
f3df7a0a6d
feat(transport): CompositeTransport for dual Meshtastic+MeshCore (Phase 4) (#6)
* feat(transport): CompositeTransport for dual Meshtastic+MeshCore (Phase 4)

Adds CompositeTransport (transport: both) that fans broadcasts to both
meshes, sizes to min(children) for uniform messages, and routes DM
replies back over the originating mesh via a transport hint threaded from
the inbound MeshMessage. Per-child self-filtering; supervisor watchdog now
resolves the Meshtastic child inside the composite. Additive/optional
throughout; single-transport behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(sizing): fixed universal mesh budget (mesh_max_chars=140)

Replace per-transport / min-of-active max_chars with a single fixed
universal constant (mesh_max_chars, default 140 = MeshCore LCD). Every
message is built once against one deterministic budget regardless of
which radios are connected; no runtime variance, no per-transport
retooling. Meshtastic sizing intentionally moves 200 -> 140.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 12:33:17 -06:00
225a5d37df
refactor(sizing): single mesh packet budget via transport.max_chars (Phase 3) (#5)
Route all mesh message sizing (renderer, digest, reply chunker, NWS
one-packet fit) through the active transport's max_chars instead of
scattered literal 200s. Meshtastic pinned at 200 (byte-identical output);
MeshCore uses its configured ~140. Sets up uniform-to-smaller sizing for
the composite transport. No behavior change on the Meshtastic path.

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 10:46:11 -06:00
316ae7351e
feat(transport): MeshCoreTransport over pyMC companion TCP (Phase 2) (#4)
* feat(transport): MeshCoreTransport over pyMC companion TCP (Phase 2)

Implements MeshCoreTransport (MeshTransport impl) using the meshcore lib
over TCP to a pyMC companion frame server, bridged behind the sync
interface via a dedicated event-loop thread. Outbound channel/DM sends,
inbound message normalization into MeshMessage(transport="meshcore"),
contact/self lookups. Factory wires transport="meshcore"; supervisor is
now transport-aware (Meshtastic watchdog guarded). Dormant unless
configured; meshtastic path unchanged; full suite matches baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(transport): sync loop-thread readiness before dispatch (MeshCore)

Wait on a threading.Event set from inside the event loop (via call_soon)
before dispatching the first coroutine in connect(), eliminating a startup
race where run_coroutine_threadsafe could be rejected by an is_running()
pre-check before run_forever() had begun spinning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(transport): MeshCore broadcasts use configured channel index

The channel arg carries Meshtastic-index semantics that don't map to
MeshCore's channel table; broadcasts now always use the configured
meshcore_channel_index (also fixes explicit channel=0 being treated as
falsy). DM path unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 10:15:39 -06:00
61278ece28
refactor(transport): introduce MeshTransport abstraction (Phase 1) (#3)
Behavior-preserving seam for a future MeshCore transport. Adds a
MeshTransport ABC + factory; renames MeshConnector -> MeshtasticTransport
(with a back-compat alias); generalizes MeshMessage additively (transport
tag, optional packet); adds a `transport` config field defaulting to
"meshtastic". No runtime behavior change; full suite matches baseline.

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 09:33:50 -06:00
3c3ad3f42e
fix(nws): keep weather alerts in one packet without dropping storm path (#1)
Weather alerts ran ~250-310 chars and were blind-sliced to 200 in the
central consumer, silently dropping storm motion + the impacted-town
list. The town list was also pre-capped to 80 chars at parse time,
destroying the middle/end of the storm path before formatting.

- nws_handler: preserve the full impacted-town list; when the message
  overflows one mesh packet, sample the path (first -> middle -> last)
  instead of truncating the tail, so both path endpoints survive
- consumer: pass precomposed titles through verbatim (no [:200] chop)
- adapter_config: add nws.single_packet_max_chars (default 200)
- tests: path-sampling + short-list coverage

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 22:14:26 -06:00
e6bc101194 fix(nws): normalize severities to schema; drop CAP pre-filter
Remove the nws_handler broadcast_severities/warning_suffix_promotes
pre-filter (GATE A) that dropped sub-Severe NWS products before the
pipeline. All NWS alerts now normalize to routine/priority/immediate
(map_severity) and breadth is governed solely by the per-toggle
dispatcher threshold. Warning-class categories are promoted to
immediate so a wrong/missing CAP severity int cant under-rank a real
warning. broadcast_severities/warning_suffix_promotes are now inert
(marked deprecated). Fixes sub-Severe alerts (Special Weather
Statements / advisories) for Magic Valley / East Idaho never reaching
the mesh despite a routine toggle threshold.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 00:12:27 +00:00
Ubuntu
e982cbbdf6 refactor(danger-zones): drop min_severity + position_max_age; table snow (grayed/inert)
- alert on any hazard touching a node (severity gate removed)
- alert regardless of position staleness; only skip nodes with no position
- snow tabled: grayed-out in GUI + skipped in correlator pending snowfall+elevation pipeline

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 06:56:22 +00:00
Ubuntu
95b1a23ef1 feat(danger-zones): configurable infra-node hazard correlation + fire age-gate
- danger_zones config section (isolated dataclass + danger_zones.yaml + GUI panel on Notifications page); defaults disabled + dry_run
- DangerZoneCorrelator: distance-based correlation of hazard events vs infra nodes (CLIENT_BASE/ROUTER/ROUTER_LATE), DM delivery, cooldown
- fire age-gate (wfigs max_declare_age_seconds) suppresses stale/closed fires announced as "New"
- tests: correlator + fire-gate boundary; wfigs fixture fix

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 06:33:54 +00:00
Ubuntu
2e1fb325f7 refactor: move source tree into work/, multi-stage Docker build, fix satpass
- Move all application source (meshai/, dashboard-frontend/, tests/,
  config/, docs/, Dockerfile, etc.) into work/ directory
- Add Node.js multi-stage build to Dockerfile for frontend compilation;
  remove compiled static assets from git tracking
- Fix satpass missing time windows: consolidation was splitting wire on
  newline and only putting line 1 in event.title, dropping the time
  window line that the composer uses for precomposed broadcasts
- Fix satpass burst flooding: stagger consolidation timers (+60s per
  pending pass) so Central batch publishes don't blast the mesh
- Update CI workflow build context to work/
- Anchor lib/ and data/ gitignore patterns to repo root to prevent
  false matches on nested directories
- Add dashboard-frontend/node_modules/ to .dockerignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-16 03:40:31 +00:00