fix(meshcore): repeater telemetry via SendStatusReq + guest-login#3094
Conversation
Scheduled MeshCore telemetry retrieval was silently failing against every repeater because we only sent the LPP `GetTelemetryData` binary opcode. Repeaters don't ship environmental sensors, and stock firmware ships with `telemetry_mode_*` set restrictively for anonymous callers, so the LPP path returns empty across the board. The Android app reads the same data users want via a different opcode (`SendStatusReq` → `StatusResponse` — 16-field operational stats) and unlocks LPP by establishing a guest session via an empty-password login. For repeater / room-server targets the scheduler now calls `requestNodeStatus` (path #1), establishes a guest-login session (empty-password, cached per-connection on the manager), then attempts the LPP request. Companion targets are unchanged. Empty/timeout log lines are promoted from debug to info so failures are visible without turning up the log level. Native backend now passes through all 16 `StatusResponse` fields (previously only battery/uptime made it through). `MeshCoreStatus` gains the operational counters and `requestNodeStatus` decodes them. Refs: #3092
|
Claude finished @Yeraze's task —— View job Code Review Complete ✅I've thoroughly reviewed this PR that adds repeater telemetry support via Todo List:
✅ Strengths and Positive Observations1. Excellent Architecture & Design
2. Strong Code Quality
3. Robust Implementation
4. Excellent Test Coverage
|
MeshCore contacts learned via the companion's advert stream were never persisted to the `meshcore_nodes` SQL table. The only writer to that table was `setNodeTelemetryConfig`, which seeds a publicKey-only row with `advType=NULL`. As a result the remote-telemetry scheduler always read `target.advType === null`, computed `isRepeaterLike === false` (typeof null !== 'number'), and routed every target through the legacy LPP `GetTelemetryData` path — silently skipping the `SendStatusReq` + guest-login paths added in #3094. In production this meant: enabling Telemetry Retrieval on a Repeater or Room Server would only ever produce timeouts on a node whose firmware doesn't anonymously answer the LPP request. Three complementary edits make the table accurate enough for the scheduler (and any future per-source consumer) to classify targets correctly: 1. `meshcoreManager.handleBridgeEvent` — call a new `persistContact` helper from the `contact_advertised`, `contact_added`, and `contact_path_updated` handlers so every advert update mirrors to `meshcore_nodes` via `databaseService.meshcore.upsertNode`. 2. `meshcoreManager.refreshContacts` — bulk-persist all contacts when a full refresh runs. This backfills existing deployments without requiring the user to re-toggle telemetry-retrieval on each node. 3. `meshcoreRoutes` PATCH `.../telemetry-config` — look up the in-memory contact via the new `MeshCoreManager.getContact` getter and upsert advType/advName immediately, so a node that gets enabled before its first advert refresh still picks up the right classification on the very next scheduler tick. Persistence is fire-and-forget at the event level (failures logged, never thrown) so a transient DB error on a single advert can't break the contact-event pipeline. The full-refresh path awaits to keep the "Refreshed N contacts" log accurate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
GetTelemetryDatabinary opcode — repeaters don't ship environmental sensors and stock firmware hastelemetry_mode_*set restrictively for anonymous callers.SendStatusReq→StatusResponsereturns 16 operational counters (battery, queue, RSSI/SNR, packet counts, uptime, errors) and works on any reachable repeater with no login required. An empty-password guest login establishes a session that unlocks the LPP path on repeaters whose telemetry mode would otherwise refuse anonymous callers.requestNodeStatus(path "Claude PR Assistant workflow" #1) →ensureGuestLogin(best-effort) →requestRemoteTelemetry(path "Update Claude PR Assistant workflow" #3). Companion (1) targets keep the existing LPP-only behaviour. Empty/timeout log lines promoteddebug→infoso failures are visible without raising the global log level.Closes #3092. Diagnosis history: #3092 (comment).
Files changed
src/server/meshcoreNativeBackend.tsget_statusbridge case passes through all 16 fields frommeshcore.js'sgetStatus()(was dropping 14).src/server/meshcoreManager.tsMeshCoreStatusinterface expanded;requestNodeStatusdecodes the new fields; addedguestLoggedInNodes: Set<string>+ensureGuestLogin(publicKey)(idempotent, cleared on disconnect / reconnect).src/server/services/meshcoreRemoteTelemetryScheduler.tstickOneManagerbranches ontarget.advType; newstatusToTelemetryRowshelper maps the 16-field status blob intomc_status_*telemetry rows (battery normalised mV → V).src/server/services/meshcoreRemoteTelemetryScheduler.test.tsrequestNodeStatus/ensureGuestLogin; 6 new scheduler-branch cases + astatusToTelemetryRowsunit suite.Telemetry types added
Under the existing
mc_prefix, allmc_status_*:battery_volts(V),uptime_secs(s),queue_len,noise_floor(dB),last_rssi(dBm),last_snr(dB),packets_recv,packets_sent,air_time_secs(s),sent_flood,sent_direct,recv_flood,recv_direct,errors,direct_dups,flood_dups.Test plan
npx vitest run src/server/services/meshcoreRemoteTelemetryScheduler.test.ts→ 28 passed (was 17 + 11 new)npx tsc --noEmit→ cleanprotobufs/meshtastic/mesh.protoin this env — confirmed unrelated by stashing diff)lastMeshTxAtexpires), confirmmc_status_*rows appear in the telemetry tab. Specifically @HougeDK's setup should now show battery/uptime/queue/RSSI for every repeater that responds to status requests, and the 4 LPP channels he sees in the Android app should also appear if the guest-login unlocks them.info-level "No telemetry from … (status + LPP both empty/timeout)" line on repeaters that don't answer.Compatibility notes
lastTelemetryRequestAtis preserved (kept fair rotation among multiple eligible nodes). Failures still wait the full configured interval before retry — that's a known follow-up but out of scope here. The improvement for HougeDK's case comes from path "Claude PR Assistant workflow" #1 actually returning data, not from cadence changes.Generated by Claude Code
Generated by Claude Code