Add telemetry config to radio settings

This commit is contained in:
Jack Kingsman
2026-04-27 12:44:52 -07:00
parent 53f701938b
commit d019ab4ee1
4 changed files with 142 additions and 0 deletions
+24
View File
@@ -101,6 +101,18 @@ class RadioConfigResponse(BaseModel):
default=False,
description="Whether the radio sends an extra direct ACK transmission",
)
telemetry_mode_base: int = Field(
default=0,
description="Base telemetry sharing mode (0=deny, 1=per-contact, 2=allow-all)",
)
telemetry_mode_loc: int = Field(
default=0,
description="Location telemetry sharing mode (0=deny, 1=per-contact, 2=allow-all)",
)
telemetry_mode_env: int = Field(
default=0,
description="Environment sensor sharing mode (0=deny, 1=per-contact, 2=allow-all)",
)
class RadioConfigUpdate(BaseModel):
@@ -123,6 +135,15 @@ class RadioConfigUpdate(BaseModel):
default=None,
description="Whether the radio sends an extra direct ACK transmission",
)
telemetry_mode_base: int | None = Field(
default=None, ge=0, le=2, description="Base telemetry sharing mode"
)
telemetry_mode_loc: int | None = Field(
default=None, ge=0, le=2, description="Location telemetry sharing mode"
)
telemetry_mode_env: int | None = Field(
default=None, ge=0, le=2, description="Environment sensor sharing mode"
)
class PrivateKeyUpdate(BaseModel):
@@ -360,6 +381,9 @@ async def get_radio_config() -> RadioConfigResponse:
path_hash_mode_supported=radio_manager.path_hash_mode_supported,
advert_location_source=advert_location_source,
multi_acks_enabled=bool(info.get("multi_acks", 0)),
telemetry_mode_base=info.get("telemetry_mode_base", 0),
telemetry_mode_loc=info.get("telemetry_mode_loc", 0),
telemetry_mode_env=info.get("telemetry_mode_env", 0),
)
+24
View File
@@ -51,6 +51,30 @@ async def apply_radio_config_update(
if result is not None and result.type == EventType.ERROR:
raise RadioCommandRejectedError(f"Failed to set multi ACKs: {result.payload}")
if update.telemetry_mode_base is not None:
logger.info("Setting telemetry_mode_base to %d", update.telemetry_mode_base)
result = await mc.commands.set_telemetry_mode_base(update.telemetry_mode_base)
if result is not None and result.type == EventType.ERROR:
raise RadioCommandRejectedError(
f"Failed to set telemetry mode (base): {result.payload}"
)
if update.telemetry_mode_loc is not None:
logger.info("Setting telemetry_mode_loc to %d", update.telemetry_mode_loc)
result = await mc.commands.set_telemetry_mode_loc(update.telemetry_mode_loc)
if result is not None and result.type == EventType.ERROR:
raise RadioCommandRejectedError(
f"Failed to set telemetry mode (location): {result.payload}"
)
if update.telemetry_mode_env is not None:
logger.info("Setting telemetry_mode_env to %d", update.telemetry_mode_env)
result = await mc.commands.set_telemetry_mode_env(update.telemetry_mode_env)
if result is not None and result.type == EventType.ERROR:
raise RadioCommandRejectedError(
f"Failed to set telemetry mode (environment): {result.payload}"
)
if update.name is not None:
logger.info("Setting radio name to %s", update.name)
await mc.commands.set_name(update.name)
@@ -183,6 +183,9 @@ export function SettingsRadioSection({
const [pathHashMode, setPathHashMode] = useState('0');
const [advertLocationSource, setAdvertLocationSource] = useState<'off' | 'current'>('current');
const [multiAcksEnabled, setMultiAcksEnabled] = useState(false);
const [telemetryModeBase, setTelemetryModeBase] = useState(0);
const [telemetryModeLoc, setTelemetryModeLoc] = useState(0);
const [telemetryModeEnv, setTelemetryModeEnv] = useState(0);
const [gettingLocation, setGettingLocation] = useState(false);
const [busy, setBusy] = useState(false);
const [rebooting, setRebooting] = useState(false);
@@ -218,6 +221,9 @@ export function SettingsRadioSection({
setPathHashMode(String(config.path_hash_mode));
setAdvertLocationSource(config.advert_location_source ?? 'current');
setMultiAcksEnabled(config.multi_acks_enabled ?? false);
setTelemetryModeBase(config.telemetry_mode_base ?? 0);
setTelemetryModeLoc(config.telemetry_mode_loc ?? 0);
setTelemetryModeEnv(config.telemetry_mode_env ?? 0);
}, [config]);
useEffect(() => {
@@ -313,6 +319,15 @@ export function SettingsRadioSection({
...(multiAcksEnabled !== (config.multi_acks_enabled ?? false)
? { multi_acks_enabled: multiAcksEnabled }
: {}),
...(telemetryModeBase !== (config.telemetry_mode_base ?? 0)
? { telemetry_mode_base: telemetryModeBase }
: {}),
...(telemetryModeLoc !== (config.telemetry_mode_loc ?? 0)
? { telemetry_mode_loc: telemetryModeLoc }
: {}),
...(telemetryModeEnv !== (config.telemetry_mode_env ?? 0)
? { telemetry_mode_env: telemetryModeEnv }
: {}),
radio: {
freq: parsedFreq,
bw: parsedBw,
@@ -468,6 +483,9 @@ export function SettingsRadioSection({
path_hash_mode: config.path_hash_mode,
advert_location_source: config.advert_location_source ?? 'current',
multi_acks_enabled: config.multi_acks_enabled ?? false,
telemetry_mode_base: config.telemetry_mode_base ?? 0,
telemetry_mode_loc: config.telemetry_mode_loc ?? 0,
telemetry_mode_env: config.telemetry_mode_env ?? 0,
});
const downloadJson = (profile: object, suffix: string) => {
@@ -539,6 +557,10 @@ export function SettingsRadioSection({
if (data.advert_location_source === 'off' || data.advert_location_source === 'current')
setAdvertLocationSource(data.advert_location_source);
if (typeof data.multi_acks_enabled === 'boolean') setMultiAcksEnabled(data.multi_acks_enabled);
if (typeof data.telemetry_mode_base === 'number')
setTelemetryModeBase(data.telemetry_mode_base);
if (typeof data.telemetry_mode_loc === 'number') setTelemetryModeLoc(data.telemetry_mode_loc);
if (typeof data.telemetry_mode_env === 'number') setTelemetryModeEnv(data.telemetry_mode_env);
};
const buildUpdateFromImport = (data: Record<string, unknown>): RadioConfigUpdate => {
@@ -554,6 +576,12 @@ export function SettingsRadioSection({
update.advert_location_source = data.advert_location_source;
if (typeof data.multi_acks_enabled === 'boolean')
update.multi_acks_enabled = data.multi_acks_enabled;
if (typeof data.telemetry_mode_base === 'number')
update.telemetry_mode_base = data.telemetry_mode_base as number;
if (typeof data.telemetry_mode_loc === 'number')
update.telemetry_mode_loc = data.telemetry_mode_loc as number;
if (typeof data.telemetry_mode_env === 'number')
update.telemetry_mode_env = data.telemetry_mode_env as number;
if (config.path_hash_mode_supported && typeof data.path_hash_mode === 'number')
update.path_hash_mode = data.path_hash_mode as number;
return update;
@@ -954,6 +982,66 @@ export function SettingsRadioSection({
</div>
</div>
<Separator />
{/* ── Telemetry Sharing ── */}
<div className="space-y-3">
<h3 className="text-base font-semibold tracking-tight">Telemetry Sharing</h3>
<p className="text-[0.8125rem] text-muted-foreground">
Controls what this radio shares when other nodes request its telemetry. &ldquo;Deny&rdquo;
blocks all requests, &ldquo;Per-Contact&rdquo; uses per-contact permission flags on the
radio, and &ldquo;Allow All&rdquo; shares with any requester.
</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div className="space-y-1.5">
<Label htmlFor="telemetry-mode-base" className="text-sm">
Battery &amp; Base
</Label>
<select
id="telemetry-mode-base"
value={telemetryModeBase}
onChange={(e) => setTelemetryModeBase(Number(e.target.value))}
className="w-full h-9 px-3 rounded-md border border-input bg-background text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
>
<option value={0}>Deny</option>
<option value={1}>Per-Contact</option>
<option value={2}>Allow All</option>
</select>
</div>
<div className="space-y-1.5">
<Label htmlFor="telemetry-mode-loc" className="text-sm">
Location
</Label>
<select
id="telemetry-mode-loc"
value={telemetryModeLoc}
onChange={(e) => setTelemetryModeLoc(Number(e.target.value))}
className="w-full h-9 px-3 rounded-md border border-input bg-background text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
>
<option value={0}>Deny</option>
<option value={1}>Per-Contact</option>
<option value={2}>Allow All</option>
</select>
</div>
<div className="space-y-1.5">
<Label htmlFor="telemetry-mode-env" className="text-sm">
Environment Sensors
</Label>
<select
id="telemetry-mode-env"
value={telemetryModeEnv}
onChange={(e) => setTelemetryModeEnv(Number(e.target.value))}
className="w-full h-9 px-3 rounded-md border border-input bg-background text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
>
<option value={0}>Deny</option>
<option value={1}>Per-Contact</option>
<option value={2}>Allow All</option>
</select>
</div>
</div>
</div>
{error && (
<div className="text-sm text-destructive" role="alert">
{error}
+6
View File
@@ -17,6 +17,9 @@ export interface RadioConfig {
path_hash_mode_supported: boolean;
advert_location_source?: 'off' | 'current';
multi_acks_enabled?: boolean;
telemetry_mode_base?: number;
telemetry_mode_loc?: number;
telemetry_mode_env?: number;
}
export interface RadioConfigUpdate {
@@ -28,6 +31,9 @@ export interface RadioConfigUpdate {
path_hash_mode?: number;
advert_location_source?: 'off' | 'current';
multi_acks_enabled?: boolean;
telemetry_mode_base?: number;
telemetry_mode_loc?: number;
telemetry_mode_env?: number;
}
export type RadioDiscoveryTarget = 'repeaters' | 'sensors' | 'all';