Repository navigation
Closed
Description
Context
Port the observer TX/RX airtime over the last hour, with a duty-cycle warning, to our fork. This is an evaluated port, not a blind cherry-pick.
Source repository: Kpa-clawbot/CoreScope
Pull request number: 2208 (upstream issue 661)
PR head commit SHA: c6685c61
State: merged 2026-10-14
Why it matters for us. In the firmware, Dispatcher.cpp:26 computes duty_cycle = 1/(1 + airtime_factor), and simple_repeater defaults to airtime_factor = 1.0 (MyMesh.cpp:891). A repeater therefore allows itself 50% TX airtime per hour and does not enforce the EU 868 MHz g3 sub-band limit of 10%. Our Danish deployment (869.5 MHz) is subject to that limit.
Our fork already has
observer_metrics.tx_air_secs/rx_air_secs(ingestor);GET /api/observers/metrics/summary(GetMetricsSummary,cmd/server/db.go);- a per-sample
tx_airtime_pct/rx_airtime_pctin the RF Health charts (public/analytics.js).
Work
- Add
tx_airtime_pct_1h/rx_airtime_pct_1hper observer to the summary.- The value is the share over the last hour, computed from the cumulative counters.
- A counter that goes down marks a reboot; only the stretch after the last reset counts.
- Return
nullwhen the span is shorter than the minimum or the newest sample is stale. Use the merged upstream constants: a minimum span of 30 min and a maximum age of 15 min. - Clamp the value to 0..100 and round it to 2 decimals.
- Add
duty_cycle_warn_pct, set from the new config keydutyCycleWarnPct(default 10). Add it toconfig.example.json. - Add a sortable numeric
TX 1hcolumn to the observer list and aTX 1hstat on the RF Health tiles. Both use one shared helper and a warning style based on CSS variables. - Reconcile with our existing per-sample
tx_airtime_pct. The UI labels must make clear which value is the rolling 1 h share and which is the per-interval value. Do not change the meaning of existing fields. - Use a named Go struct for the summary response. No new
map[string]interface{}, and remove one if the handler currently uses one. - Perf: one extra bulk query per summary request, on the existing timestamp index. The observer list makes one extra bulk call with a client TTL, not one per observer.
- Tests, adapted to our layout with root-level
test-*.js:- Go unit tests: steady state, a reboot mid-hour, a single sample, a span under the minimum, a stale newest sample, nil counters, clamping and rounding;
- summary integration;
- frontend helper;
- E2E for the column, the warning state and sorting.
- Customizer. Note in the PR that the threshold should be exposed in the customizer later (AGENTS.md rule 8). Do not build that here.
Acceptance criteria
The values match the upstream semantics on our data. The warning appears at or above the threshold in both themes. Existing RF Health values are unchanged.
Metadata
Metadata
Assignees
Labels
No labels
Activity
Declare ObserverAirtime as an eslint global
dborup commentedon Oct 15, 2026
Fixed by #481 (merged as
4fce0d40e57a2e7b903e9b1bd6b332ed229b8fde).Fixed by #481: port of upstream 2208, rolling 1 h observer TX/RX airtime with a configurable duty-cycle warning (default 10%, EU g3) (APPROVE with nits, CI green).