Skip to content

A fresh radio profile writes ~60 schema defaults the operator never chose #128

Description

@ww8l

A radio profile that has never been read off a radio is seeded with a schema
default for every field
, saved with all of them, and then written to the radio
in full. The operator sees a form they never filled in, and the radio gets ~60
settings nobody chose.

The path

  1. src/lib/profiles.tsseedValues(fields, saved, defaults = true) fills any
    field not present in the saved profile with fieldDefault(f):

    type seeded value
    boolean false
    integer field.min ?? 0
    select options[0] — the first entry in the list
    text ""
  2. src/components/profiles/ProfileEditor.tsx — for a cable radio the third
    argument is !isCardRadio = true, so defaults are seeded. It then saves the
    whole map: non_channel_settings: JSON.stringify(values).

  3. src-tauri/src/commands/program.rswrite_radio_settings reads
    non_channel_settings back out of the DB and hands the entire object to
    the driver. The driver's patch skips only keys that are absent or null; a
    seeded default is neither.

So "create a profile, hit Write" pushes a value for every field in the schema.

Why it matters

Card radios are already exempt — isCardRadio turns defaults off, and the
comment there cites #90, where keying that decision off the wrong flag "seeded
~300 schema defaults into a file the radio itself wrote". The same reasoning
applies to a cable radio
; it just was not extended, because a cable radio's
settings looked like they came from nowhere rather than from a file.

The blast radius grows with schema size. The TM-D710 now ships 95 controls, and
its first-option-in-the-list defaults include things like APRS beacon method,
data band, and TX delay.

⚠ This is not the empty-string case. That half was fixed for the TM-D710 in
ae7e718: image_settings::patch treats "" as "not set" and leaves the radio's
bytes alone, because otherwise a fresh profile would have blanked the operator's
call sign, all five status texts and all five position records. Selects and
booleans have no equivalent "unset" value, so they still go out.

Where to fix it

Probably not in each driver — this is the form layer, and every driver would need
the same guard. The shape that fits the existing design is to send only what the
operator or a radio read actually supplied:

  • don't seed defaults for a cable radio either (seedValues(..., false)), so an
    untouched field is simply absent from the saved JSON and every driver's
    existing "skip absent keys" logic already does the right thing; or
  • keep the form's defaults for display but write only keys that differ from the
    seeded baseline — ProfileEditor already tracks baseline for a related
    reason.

The first is closer to how card radios already behave and to
no-default-settings-for-card-radios.

What a fix has to be checked against

Read a profile off a real radio, change one setting, write it, and confirm the
other 94 come back byte-identical — the TM-D710 is a good test bed because its
settings read/write round trip is hardware-proven and its backup captures both
transports.

Not urgent

Raised while shipping v26.9.6 (#113). Nothing is known to have been damaged by
it; it needs an operator to create a fresh profile and write it without reading
from the radio first.

Activity

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

    bugSomething isn't workingcorrectnessProduces a silently wrong resulthardware-riskCan damage, brick or mis-program a physical radio

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions