Skip to content

feat(observers): show RX 1h airtime (channel load) next to TX 1h, and both on the observer detail page #497

Closed

Description

@dborup

Context

#481 (the port of upstream 2208) added a rolling 1-hour airtime share per observer. GET /api/observers/metrics/summary already returns both tx_airtime_pct_1h and rx_airtime_pct_1h. The UI shows only TX 1h: in the observer list and on the RF Health tiles.

On meshview.dk, 2026-10-16, 58 of 59 observers had an RX 1h value, with a median of 3.7% and a maximum of 17.5%.

Meaning

  • TX 1h is the share of the last hour the observer's own radio spent transmitting. It is regulated, and the EU g3 limit is 10%, so it keeps the existing duty-cycle warning.
  • RX 1h is the share of the last hour the radio spent receiving packets. It measures how busy the channel is at that observer. It is not regulated, so it gets no warning colour. A high value shows a dense or congested area.

Work

  1. Observer list. Add a sortable numeric "RX 1h" column next to "TX 1h", using the same shared helper module (public/observer-airtime.js). Do not add a warning style. A missing value shows —.
  2. RF Health tiles. Show "RX 1h: x%" next to "TX 1h". The stats row must still wrap between items on narrow tiles, as Port upstream 2208: 1 h TX airtime + duty-cycle warning #481 fixed.
  3. Observer detail page (public/observer-detail.js, #/observers/{id}). Show TX 1h (with the duty-cycle warning at or above duty_cycle_warn_pct, same style as the list) and RX 1h (neutral) in the stats area.
    • Reuse data the page already loads. If neither value is available there, do one summary request with the existing client TTL cache, or extend the existing per-observer detail response with a named Go struct field. Do not add per-item calls or new map[string]interface{}.
    • Verify the actual response shapes first (AGENTS.md rule 4).
  4. Tooltips. TX: "Share of the last hour this observer transmitted; flagged at or above the duty-cycle limit." RX: "Share of the last hour this observer's radio was receiving packets — a measure of channel load at this location, not a limit." Keep them distinct from the existing per-sample tx_airtime_pct in the RF Health charts.

Tests

  • Unit tests for the helper: RX formatting, null and unknown values, and that RX never gets the warning class.
  • Frontend tests for the list column (sorting, — for null), the tile, and the detail page (TX warning at or above the threshold, RX neutral).
  • An E2E that serves a known summary through page.route, as test-issue-661/Port upstream 2208: 1 h TX airtime + duty-cycle warning #481 does, and checks the column, sorting, tile and detail page.
  • Browser-check both themes at desktop width and 360×800.

Acceptance criteria

RX 1h is visible in the observer list, on the RF Health tiles and on the observer detail page. TX 1h is also on the detail page, with the existing warning semantics. No per-observer request loops.

Activity

  1. dborup commented on Oct 16, 2026

    @dborup
    OwnerAuthor

    Fixed by #498 (merged as c8d60b0a56bb70261c5c7be6cd8779a0c06abe35).Fixed by #498: RX 1h (channel load, neutral) next to TX 1h in the observer list and RF Health, and TX/RX 1h cards on the observer detail page, filled without blocking the page render (APPROVE after round 2, CI green).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions