Add duty cycle display to repeaters. Closes #308.

This commit is contained in:
Jack Kingsman
2026-07-08 14:47:50 -07:00
parent a6c64279e8
commit 2fcbefac3b
7 changed files with 69 additions and 3 deletions
+9
View File
@@ -602,6 +602,15 @@ class RepeaterRadioSettingsResponse(BaseModel):
radio: str | None = Field(default=None, description="Radio settings (freq,bw,sf,cr)")
tx_power: str | None = Field(default=None, description="TX power in dBm")
airtime_factor: str | None = Field(default=None, description="Airtime factor")
duty_cycle_limit: str | None = Field(
default=None,
description=(
"Configured duty-cycle limit as a percentage string (e.g. '25.0%'), derived "
"by firmware from airtime_factor (100/(af+1)). This is the configured ceiling, "
"not the current measured duty cycle. Only available on firmware >= 1.15; None "
"on older nodes that don't support 'get dutycycle'."
),
)
repeat_enabled: str | None = Field(default=None, description="Repeat mode enabled")
flood_max: str | None = Field(default=None, description="Max flood hops")
+10
View File
@@ -345,10 +345,20 @@ async def repeater_radio_settings(public_key: str) -> RepeaterRadioSettingsRespo
("get radio", "radio"),
("get tx", "tx_power"),
("get af", "airtime_factor"),
("get dutycycle", "duty_cycle_limit"),
("get repeat", "repeat_enabled"),
("get flood.max", "flood_max"),
],
)
# `get dutycycle` only exists on firmware >= 1.15. Older nodes fall through to
# the generic unknown-config handler and reply "??: dutycycle" (or an ERROR
# string), which extract_response_text passes back verbatim. Drop those so the
# field reads as unsupported rather than surfacing the sentinel to the UI.
dc = results.get("duty_cycle_limit")
if dc is not None:
dc = dc.strip()
if dc.startswith("??") or dc.lower().startswith("error"):
results["duty_cycle_limit"] = None
return RepeaterRadioSettingsResponse(**results)