Skip to content

feat(connection): host-link strength meter for BLE and WiFi/TCP - #769

Merged
rinchen merged 3 commits into
mainfrom
rssi
Aug 2, 2026
Merged

feat(connection): host-link strength meter for BLE and WiFi/TCP#769
rinchen merged 3 commits into
mainfrom
rssi

Conversation

@rinchen

@rinchen rinchen commented Aug 2, 2026

Copy link
Copy Markdown
Member

Summary

  • Add a hybrid host↔radio link meter on Meshtastic/MeshCore Connection panels: Noble BLE RSSI (dBm) on macOS/Windows, HTTP/TCP RTT link quality (ms → bars) on all platforms, and Unavailable (Web Bluetooth) for Linux BLE
  • Show the same Signal / Link quality UI on enabled Reticulum BLE RNode and TCP Client interface rows (always show BLE Signal with when RSSI is unknown)
  • Cover IPC/probe helpers, ConnectionPanel wiring, and Reticulum meters with unit/UI tests; stabilize Reticulum poll hooks so inline interfaces[] identity churn cannot restart probes in a loop

Test plan

  • Meshtastic BLE on macOS/Windows: connected card shows Signal and live dBm updates
  • Meshtastic BLE on Linux: shows Unavailable (Web Bluetooth) (no fake bars)
  • Meshtastic HTTP and TCP: Link quality appears and updates with RTT ms
  • MeshCore TCP/IP: Link quality uses TCP RTT probe
  • Serial connections: no Signal / Link quality meter
  • Reticulum: enabled BLE RNode row shows Signal ( or dBm); enabled TCP Client shows Link quality; disabled/serial rows hide meters
  • pnpm exec vitest run on host-link / ConnectionPanel.hostLinkMeter / ReticulumInterfacesPanel meter tests

Summary by CodeRabbit

  • New Features

    • Added connection link-quality indicators showing BLE signal strength and IP round-trip latency.
    • Added HTTP and TCP connectivity measurements for supported device connections.
    • Added BLE signal monitoring for connected devices and Reticulum interfaces.
    • Added unavailable-state indicators when link measurements cannot be collected.
  • Bug Fixes

    • Improved handling of failed, invalid, unavailable, or interrupted link-quality measurements.
  • Tests

    • Added comprehensive coverage for BLE signal updates, latency probes, display states, and cleanup behavior.

Show Noble BLE RSSI and HTTP/TCP RTT link quality on Meshtastic/MeshCore
Connection panels, plus Reticulum BLE RNode and TCP Client rows. Linux BLE
shows Unavailable (Web Bluetooth).
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rinchen, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: d235a688-944b-4ff3-bb82-d1a20daeca60

📥 Commits

Reviewing files that changed from the base of the PR and between 1145c53 and 1a6d85e.

📒 Files selected for processing (3)
  • .coderabbit.yaml
  • src/renderer/hooks/useHostLinkMeter.test.ts
  • src/renderer/hooks/useHostLinkMeter.ts
📝 Walkthrough

Walkthrough

The PR adds connected-link quality monitoring. Noble BLE sessions emit RSSI updates, while HTTP and TCP probes measure RTT. Typed IPC APIs, renderer hooks, Reticulum maps, and connection meters expose these measurements.

Changes

Host Link Quality Monitoring

Layer / File(s) Summary
BLE RSSI polling lifecycle
src/main/noble-ble-manager.ts, src/main/index.ts, src/preload/index.ts, src/shared/electron-api.types.ts
Connected Noble BLE sessions poll RSSI, emit session-scoped updates, and expose them through typed IPC listeners.
HTTP and TCP RTT IPC
src/main/host-link-rtt.ts, src/main/index.ts, src/preload/index.ts, src/shared/electron-api.types.ts
The main process probes HTTP and TCP hosts with timeout and failure handling. Preload APIs expose nullable RTT results.
Host quality parsing and monitoring
src/renderer/lib/hostLinkQuality.ts, src/renderer/hooks/useHostLinkMeter.ts
Renderer utilities parse protocol-specific targets, map RTT to signal levels, and monitor BLE or IP links.
Reticulum interface measurements
src/renderer/hooks/useReticulumBleRnodeRssiMap.ts, src/renderer/hooks/useReticulumTcpLinkQualityMap.ts
Reticulum hooks poll enabled BLE RNodes and TCP clients, normalize results, and manage cleanup.
Connection and interface meters
src/renderer/components/ConnectionLinkMeter.tsx, src/renderer/components/SignalBars.tsx, src/renderer/components/ConnectionPanel.tsx, src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx
Connection and Reticulum panels render RSSI, RTT, and unavailable states with signal bars and formatted measurements.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ConnectedRadio
  participant UseHostLinkMeter
  participant ElectronAPI
  participant MainProcess
  participant HostOrPeripheral
  ConnectedRadio->>UseHostLinkMeter: provide connection state
  UseHostLinkMeter->>ElectronAPI: subscribe to RSSI or request RTT
  ElectronAPI->>MainProcess: forward IPC operation
  MainProcess->>HostOrPeripheral: poll RSSI or probe HTTP/TCP
  HostOrPeripheral-->>MainProcess: measurement or failure
  MainProcess-->>ElectronAPI: nullable result
  ElectronAPI-->>UseHostLinkMeter: update link quality state
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding host-link strength meters for BLE and WiFi/TCP connections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rssi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
src/main/noble-ble-manager.test.ts-149-167 (1)

149-167: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add behavioral coverage for link RSSI polling.

The current tests only assert source-string containment. Add a fake-timer/mocked peripheral test that exercises linkRssiPollInflight, the NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS bound, cleared links on linkRssiPollTimer, and connect-time + periodic linkRssi emission behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/noble-ble-manager.test.ts` around lines 149 - 167, Extend the link
RSSI polling tests around NobleBleManager to use fake timers and a mocked
peripheral, exercising linkRssiPollInflight and enforcing the
NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS bound. Verify linkRssiPollTimer cleanup clears
tracked links, and cover both the initial connect-time linkRssi emission and
subsequent periodic emissions instead of relying only on SOURCE string
assertions.
🧹 Nitpick comments (8)
src/renderer/hooks/useReticulumTcpLinkQualityMap.ts (1)

39-52: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Make the target key encoding collision-safe.

encodeTcpProbeTargetKey joins records with |. host comes from user-editable interface configuration and is only trimmed, so it can contain |. In that case decodeTcpProbeTargetKey splits one record into two, and the effect probes a wrong host with port NaN. Use JSON.stringify for the key instead.

♻️ Proposed change
 function encodeTcpProbeTargetKey(targets: readonly TcpProbeTarget[]): string {
-  return targets
-    .map((t) => `${t.id}\0${t.host}\0${t.port}`)
-    .sort()
-    .join('|');
+  const sorted = [...targets].sort((a, b) =>
+    `${a.id}\u0000${a.host}\u0000${a.port}`.localeCompare(`${b.id}\u0000${b.host}\u0000${b.port}`),
+  );
+  return JSON.stringify(sorted);
 }
 
 function decodeTcpProbeTargetKey(targetKey: string): TcpProbeTarget[] {
   if (!targetKey) return [];
-  return targetKey.split('|').map((part) => {
-    const [id, host, portStr] = part.split('\0');
-    return { id, host, port: Number(portStr) };
-  });
+  return JSON.parse(targetKey) as TcpProbeTarget[];
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/hooks/useReticulumTcpLinkQualityMap.ts` around lines 39 - 52,
Update encodeTcpProbeTargetKey and decodeTcpProbeTargetKey to serialize and
deserialize the target array with JSON.stringify and JSON.parse instead of
delimiter-based joining and splitting. Preserve deterministic key ordering by
sorting targets before serialization, and keep an empty key decoding to an empty
array.
src/renderer/hooks/useReticulumBleRnodeRssiMap.test.ts (1)

128-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that no scan request is issued.

The test name states that no scan occurs, but the assertion only checks map size. An empty map also occurs when a scan runs and returns no matching device. Add a call assertion on proxyGet to test the stated behavior.

♻️ Proposed change
     expect(result.current.size).toBe(0);
+    expect(window.electronAPI.reticulum.proxyGet).not.toHaveBeenCalled();
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/hooks/useReticulumBleRnodeRssiMap.test.ts` around lines 128 -
143, Add an assertion in the test around useReticulumBleRnodeRssiMap verifying
that proxyGet is not called when all BLE RNode targets are disabled. Keep the
existing empty-map assertion, and use the test’s existing proxyGet mock rather
than introducing a separate request mechanism.
src/renderer/lib/hostLinkQuality.ts (1)

10-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate HOST_LINK_RTT_PROBE_TIMEOUT_MS definition.

HOST_LINK_RTT_PROBE_TIMEOUT_MS is defined here and also in src/main/host-link-rtt.ts (export const HOST_LINK_RTT_PROBE_TIMEOUT_MS = 3 * MS_PER_SECOND;). Two independent declarations of the same cross-process timing contract can drift out of sync if one file changes and the other does not. Define the constant once in a shared module (for example src/shared/timeConstants.ts) and import it in both the main-process probe and this renderer module.

As per coding guidelines, "Reuse shared validation helpers, including clampTcpPort() and time constants derived from MS_PER_SECOND; do not duplicate inline parsers or clamps."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/lib/hostLinkQuality.ts` around lines 10 - 11, Move
HOST_LINK_RTT_PROBE_TIMEOUT_MS into a shared time-constants module, then import
and reuse that symbol in both hostLinkQuality and the main-process host-link RTT
probe. Remove the duplicate local declarations while preserving the existing
3-second timeout contract and MS_PER_SECOND-based definition.

Source: Coding guidelines

src/renderer/hooks/useHostLinkMeter.ts (1)

94-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Direct protocol === 'meshcore' comparisons instead of ProtocolCapabilities. Both files route logic by hardcoded protocol string comparison rather than a capability-driven check, against the stated guideline to gate on ProtocolCapabilities/useRadioProvider(protocol).

  • src/renderer/hooks/useHostLinkMeter.ts#L94-L103: run() branches on protocol === 'meshtastic' and protocol === 'meshcore' to pick the HTTP vs. TCP probe; replace with a per-protocol probe-config lookup (e.g., a map keyed by protocol providing the probe function and default port).
  • src/renderer/hooks/useHostLinkMeter.ts#L53-L56: active excludes reticulum via protocol !== 'reticulum'; derive this from a capability flag instead of a protocol-name comparison.
  • src/renderer/hooks/useHostLinkMeter.ts#L68: sessionId is derived via protocol === 'meshcore' ? 'meshcore' : 'meshtastic'; source the session id from protocol metadata instead of an inline ternary.
  • src/renderer/lib/hostLinkQuality.ts#L57-L71: parseTcpProbeTarget selects parseTcpAddress vs. parseMeshtasticTcpAddress via protocol === 'meshcore'; route through the same protocol-config lookup used above.

As per coding guidelines, "Use ProtocolCapabilities and useRadioProvider(protocol) for feature gating; do not compare protocol === 'meshcore'."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/hooks/useHostLinkMeter.ts` around lines 94 - 103, Replace
hardcoded protocol comparisons with a shared protocol configuration/capability
lookup. In src/renderer/hooks/useHostLinkMeter.ts:94-103, select the probe and
default port from that configuration; at :53-56, derive active from the
reticulum capability; and at :68, obtain sessionId from protocol metadata. In
src/renderer/lib/hostLinkQuality.ts:57-71, use the same configuration to select
the TCP target parser, preserving the existing per-protocol behavior without
inline protocol-name checks.

Source: Coding guidelines

src/main/noble-ble-manager.ts (1)

128-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use MS_PER_SECOND-derived values for the new time constants.

NOBLE_LINK_RSSI_POLL_MS and NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS use raw millisecond literals (4_000, 5_000). src/main/host-link-rtt.ts derives its timeout from MS_PER_SECOND (3 * MS_PER_SECOND) in the same PR. Use the same pattern here for consistency.

♻️ Proposed fix
+import { MS_PER_SECOND } from '../shared/timeConstants';
+
 /** Host↔radio BLE RSSI poll while GATT is connected (Connection panel meter). */
-export const NOBLE_LINK_RSSI_POLL_MS = 4_000;
+export const NOBLE_LINK_RSSI_POLL_MS = 4 * MS_PER_SECOND;
 /** Bound a single updateRssiAsync so a hung stack cannot stall the poll loop. */
-const NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS = 5_000;
+const NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS = 5 * MS_PER_SECOND;

As per coding guidelines, "Reuse shared validation helpers, including clampTcpPort() and time constants derived from MS_PER_SECOND; do not duplicate inline parsers or clamps."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/noble-ble-manager.ts` around lines 128 - 131, Update the time
constants near the BLE RSSI poll declarations, NOBLE_LINK_RSSI_POLL_MS and
NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS, to derive their values from the shared
MS_PER_SECOND constant instead of raw millisecond literals, preserving the
existing 4-second and 5-second durations.

Source: Coding guidelines

src/renderer/components/ConnectionPanel.tsx (1)

419-438: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Simplify the duplicated host-address ternary.

hostAddress in the useHostLinkMeter call recomputes the same branching logic as activeHostAddress (lines 419-424), just keyed off state.connectionType instead of the local connectionType selector. Because the connection-type selector is hidden once connected, and state.connectionType always matches the local connectionType used to establish the connection, these two expressions resolve to the same value in every reachable case:

  • state.connectionType === 'tcp' → both use tcpAddress.
  • state.connectionType === 'http' → both use ${tcpHost}:${tcpPort} for meshcore, httpAddress for meshtastic.

Keep one source of truth to avoid the two ternaries silently diverging after a future edit.

Confirm the equivalence holds across the auto-connect and reconnect flows before applying, since this component has many state-transition paths.

♻️ Proposed simplification
   const hostLinkMeter = useHostLinkMeter({
     protocol,
     connectionType: state.connectionType,
     status: state.status,
-    hostAddress:
-      state.connectionType === 'tcp'
-        ? tcpAddress
-        : state.connectionType === 'http'
-          ? protocol === 'meshcore'
-            ? `${tcpHost}:${tcpPort}`
-            : httpAddress
-          : activeHostAddress,
+    hostAddress: activeHostAddress,
     platform: window.electronAPI.getPlatform() as NodeJS.Platform,
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/components/ConnectionPanel.tsx` around lines 419 - 438, Use the
existing activeHostAddress value as the hostAddress passed to useHostLinkMeter,
removing the duplicated state.connectionType/protocol ternary. Confirm
auto-connect and reconnect flows preserve the established address selection,
including TCP, HTTP, and meshcore cases.
src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx (2)

2185-2226: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add role="group" to match the established accessible-name pattern.

Both status <span> elements set aria-label without an ARIA role. A <span> defaults to role "generic" in the accessibility tree, and generic-role elements are not guaranteed to expose aria-label as their accessible name to assistive technology. ConnectionLinkMeter.tsx uses role="group" together with aria-label for the equivalent BLE/RTT status indicator. Match that pattern here so the accessible name is reliably exposed.

Confirm actual screen-reader behavior for aria-label on role-less spans before deciding this is required, since browser/AT handling of this case varies.

♿ Proposed fix
                       {showBleRnodeSignal ? (
                         <span
+                          role="group"
                           className="text-muted flex shrink-0 items-center gap-1 text-xs"
                           aria-label={t('connectionPanel.hostSignal')}
                           data-testid={`reticulum-ble-signal-${iface.id}`}
                         >
                       {showTcpLinkQuality ? (
                         <span
+                          role="group"
                           className="text-muted flex shrink-0 items-center gap-1 text-xs"
                           aria-label={t('connectionPanel.linkQuality')}
                           data-testid={`reticulum-tcp-link-${iface.id}`}
                         >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx` around lines
2185 - 2226, Confirm the current screen-reader behavior for aria-label on
role-less status spans, then update the BLE and TCP status spans in the
Reticulum interfaces panel only if needed to ensure their labels are exposed. If
required, add role="group" alongside aria-label to both spans, matching the
established ConnectionLinkMeter pattern.

2185-2226: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated link-meter markup instead of reusing ConnectionLinkMeter.

This block reimplements the same "SignalBars + rounded value, or SignalBars noData + unavailable label" pattern that ConnectionLinkMeter.tsx already defines for BLE RSSI and IP RTT. Both implementations independently call Math.round(), the same connectionPanel.bleRssiDbm / connectionPanel.linkQualityMs / connectionPanel.hostSignalUnavailable / connectionPanel.linkQualityUnavailable keys, and rttToSignalLevel. A future change to rounding, level mapping, or i18n keys in one location can silently diverge from the other.

Extract the inner "bars + label" content (independent from ConnectionLinkMeter's outer flex justify-between row wrapper, which doesn't fit this compact list-row layout) into a small shared presentational helper that both ConnectionLinkMeter and this panel can call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx` around lines
2185 - 2226, Extract the shared “SignalBars plus value or unavailable label”
rendering from ConnectionLinkMeter into a small presentational helper,
preserving its existing rounding, rttToSignalLevel mapping, and translation
keys. Update both ConnectionLinkMeter and the ReticulumInterfacesPanel block
using showBleRnodeSignal/showTcpLinkQuality to call the helper, while keeping
ConnectionLinkMeter’s outer flex/justify-between wrapper separate from the
compact panel layout.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/index.ts`:
- Around line 6358-6365: The hostLink:probeTcpRtt handler currently duplicates
TCP port validation inline. Replace that condition with the shared
clampTcpPort() helper, preserving rejection of invalid inputs and passing the
clamped valid port to probeTcpRttMs.

In `@src/renderer/hooks/useHostLinkMeter.ts`:
- Around line 80-117: Update the polling effect’s run function to use a
generation counter for probe requests: increment it whenever a probe is issued,
capture that generation, and only apply the result when it matches the latest
generation and the effect is not cancelled. Keep the existing interval, cleanup,
and transport-specific probe selection unchanged.

---

Other comments:
In `@src/main/noble-ble-manager.test.ts`:
- Around line 149-167: Extend the link RSSI polling tests around NobleBleManager
to use fake timers and a mocked peripheral, exercising linkRssiPollInflight and
enforcing the NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS bound. Verify linkRssiPollTimer
cleanup clears tracked links, and cover both the initial connect-time linkRssi
emission and subsequent periodic emissions instead of relying only on SOURCE
string assertions.

---

Nitpick comments:
In `@src/main/noble-ble-manager.ts`:
- Around line 128-131: Update the time constants near the BLE RSSI poll
declarations, NOBLE_LINK_RSSI_POLL_MS and NOBLE_LINK_RSSI_UPDATE_TIMEOUT_MS, to
derive their values from the shared MS_PER_SECOND constant instead of raw
millisecond literals, preserving the existing 4-second and 5-second durations.

In `@src/renderer/components/ConnectionPanel.tsx`:
- Around line 419-438: Use the existing activeHostAddress value as the
hostAddress passed to useHostLinkMeter, removing the duplicated
state.connectionType/protocol ternary. Confirm auto-connect and reconnect flows
preserve the established address selection, including TCP, HTTP, and meshcore
cases.

In `@src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx`:
- Around line 2185-2226: Confirm the current screen-reader behavior for
aria-label on role-less status spans, then update the BLE and TCP status spans
in the Reticulum interfaces panel only if needed to ensure their labels are
exposed. If required, add role="group" alongside aria-label to both spans,
matching the established ConnectionLinkMeter pattern.
- Around line 2185-2226: Extract the shared “SignalBars plus value or
unavailable label” rendering from ConnectionLinkMeter into a small
presentational helper, preserving its existing rounding, rttToSignalLevel
mapping, and translation keys. Update both ConnectionLinkMeter and the
ReticulumInterfacesPanel block using showBleRnodeSignal/showTcpLinkQuality to
call the helper, while keeping ConnectionLinkMeter’s outer flex/justify-between
wrapper separate from the compact panel layout.

In `@src/renderer/hooks/useHostLinkMeter.ts`:
- Around line 94-103: Replace hardcoded protocol comparisons with a shared
protocol configuration/capability lookup. In
src/renderer/hooks/useHostLinkMeter.ts:94-103, select the probe and default port
from that configuration; at :53-56, derive active from the reticulum capability;
and at :68, obtain sessionId from protocol metadata. In
src/renderer/lib/hostLinkQuality.ts:57-71, use the same configuration to select
the TCP target parser, preserving the existing per-protocol behavior without
inline protocol-name checks.

In `@src/renderer/hooks/useReticulumBleRnodeRssiMap.test.ts`:
- Around line 128-143: Add an assertion in the test around
useReticulumBleRnodeRssiMap verifying that proxyGet is not called when all BLE
RNode targets are disabled. Keep the existing empty-map assertion, and use the
test’s existing proxyGet mock rather than introducing a separate request
mechanism.

In `@src/renderer/hooks/useReticulumTcpLinkQualityMap.ts`:
- Around line 39-52: Update encodeTcpProbeTargetKey and decodeTcpProbeTargetKey
to serialize and deserialize the target array with JSON.stringify and JSON.parse
instead of delimiter-based joining and splitting. Preserve deterministic key
ordering by sorting targets before serialization, and keep an empty key decoding
to an empty array.

In `@src/renderer/lib/hostLinkQuality.ts`:
- Around line 10-11: Move HOST_LINK_RTT_PROBE_TIMEOUT_MS into a shared
time-constants module, then import and reuse that symbol in both hostLinkQuality
and the main-process host-link RTT probe. Remove the duplicate local
declarations while preserving the existing 3-second timeout contract and
MS_PER_SECOND-based definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 971df5f9-fa2f-414c-8745-c00283f4348f

📥 Commits

Reviewing files that changed from the base of the PR and between d93f72c and 1145c53.

⛔ Files ignored due to path filters (16)
  • src/renderer/locales/cs/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/de/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/en/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/es/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/fr/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/id/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/it/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/ja/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/ko/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/nl/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/pl/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/pt-BR/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/ru/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/tr/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/uk/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/zh/translation.json is excluded by !src/renderer/locales/**
📒 Files selected for processing (28)
  • src/main/host-link-rtt.test.ts
  • src/main/host-link-rtt.ts
  • src/main/index.contract.test.ts
  • src/main/index.ts
  • src/main/noble-ble-manager.test.ts
  • src/main/noble-ble-manager.ts
  • src/preload/index.ts
  • src/renderer/App.test.tsx
  • src/renderer/components/ConnectionLinkMeter.test.tsx
  • src/renderer/components/ConnectionLinkMeter.tsx
  • src/renderer/components/ConnectionPanel.hostLinkMeter.test.tsx
  • src/renderer/components/ConnectionPanel.tsx
  • src/renderer/components/SignalBars.test.tsx
  • src/renderer/components/SignalBars.tsx
  • src/renderer/components/reticulum/ReticulumInterfacesPanel.test.tsx
  • src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx
  • src/renderer/hooks/hostLinkQuality.probes.test.ts
  • src/renderer/hooks/useHostLinkMeter.test.ts
  • src/renderer/hooks/useHostLinkMeter.ts
  • src/renderer/hooks/useReticulumBleRnodeRssiMap.test.ts
  • src/renderer/hooks/useReticulumBleRnodeRssiMap.ts
  • src/renderer/hooks/useReticulumTcpLinkQualityMap.test.ts
  • src/renderer/hooks/useReticulumTcpLinkQualityMap.ts
  • src/renderer/lib/devElectronApiStub.ts
  • src/renderer/lib/hostLinkQuality.test.ts
  • src/renderer/lib/hostLinkQuality.ts
  • src/renderer/vitest.electronApiMock.ts
  • src/shared/electron-api.types.ts

Comment thread src/main/index.ts
Comment on lines +6358 to +6365
ipcMain.handle('hostLink:probeTcpRtt', async (event, host: unknown, port: unknown) => {
assertIpcSender(event, 'hostLink:probeTcpRtt');
validateHttpHost(host);
if (!Number.isInteger(port) || (port as number) < 1 || (port as number) > 65535) {
throw new Error('Invalid port');
}
return probeTcpRttMs(host, port as number);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Locate clampTcpPort() and confirm renderer callers of hostLink.probeTcpRtt pass a bare host.
rg -n -C3 'function clampTcpPort|export.*clampTcpPort' --type=ts
rg -n -C5 'probeTcpRtt\(' src/renderer

Repository: Colorado-Mesh/mesh-client

Length of output: 163


🏁 Script executed:

#!/bin/bash
set -u

echo "== files =="
git ls-files | sed -n '1,120p'

echo "== clampTcpPort candidates =="
rg -n "clampTcpPort" . || true

echo "== hostLink probe handlers and relevant helpers =="
rg -n -C4 "probeTcpRtt|probeHttpRtt|validateHttpHost|hostLink:probeTcpRtt|hostLink:probeHttpRtt|clampTcpPort" src/main src/shared src/renderer || true

echo "== renderer probeTcpRtt call sites =="
rg -n -C5 "\.probeTcpRtt|probeTcpRtt\(" . || true

Repository: Colorado-Mesh/mesh-client

Length of output: 50381


Use clampTcpPort() for the TCP probe port validation.

clampTcpPort() is the shared TCP-port helper in src/shared/tcpPort.ts; replace the inline Number.isInteger(port) || port < 1 || port > 65535 check with it to satisfy the shared-validation guideline.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from 'child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/index.ts` around lines 6358 - 6365, The hostLink:probeTcpRtt handler
currently duplicates TCP port validation inline. Replace that condition with the
shared clampTcpPort() helper, preserving rejection of invalid inputs and passing
the clamped valid port to probeTcpRttMs.

Source: Coding guidelines

Comment thread src/renderer/hooks/useHostLinkMeter.ts
rinchen added 2 commits August 2, 2026 12:14
Gate HTTP/TCP meter updates on a probe generation so a slower older
poll cannot overwrite a newer RTT after overlapping interval runs.
Exclude Dependabot/Renovate auto-reviews and ignore common lockfile and
generated paths alongside the existing Free-tier filters.
@rinchen
rinchen merged commit 9140cc9 into main Aug 2, 2026
9 checks passed
@rinchen
rinchen deleted the rssi branch August 2, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant