Let a configured ls_secs survive the phone API round trip (#12145)

The config-send path copies config.power and then overwrites one field with
the default, unconditionally:

    fromRadioScratch.config.payload_variant.power = config.power;
    fromRadioScratch.config.payload_variant.power.ls_secs = default_ls_secs;

The note at the end of that state explains why the effective value is sent:
0 means "use the default" and the app needs to know what the device will
actually do. But the write is not conditional on the stored value being 0, so
a configured value is replaced on the way out. Every client, the CLI included,
reads back default_ls_secs (IF_ROUTER(ONE_DAY, 5 * 60)) no matter what is
stored, and a set that took effect looks like a set that was refused.

It also loses the setting. AdminModule's power case stores what it is sent
(src/modules/AdminModule.cpp:967), so a client that reads this config and
writes it back - which is what happens when a user changes any other field in
the same message - stores the default over the configured value.

Observed on an ESP32 node: ls_secs set to 60, read back as 300, and the node
then light slept on the 60 s cadence, so the stored value had taken all along.

Default::getConfiguredOrDefault() keeps the behaviour the note describes, since
0 is exactly the case it falls through on, and changes only the readback of a
non-zero stored value. Note that config.power.ls_secs is initialised to
default_ls_secs in NodeDB::installDefaultConfig() (src/mesh/NodeDB.cpp:1366)
and consumed raw in PowerFSM's lsIdle() (src/PowerFSM.cpp:133), so a stored 0
is not how a default-configured node looks in the first place.


Claude-Session: https://claude.ai/code/session_01GTiifXrVc4YdonmLRCeWhw

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jonathan BennettandClaude Opus 5 authored and GitHub committed 2026-10-14 17:01:50 +00:00
1 parent afdaccc25f
commit 0af1396ea3
1 file changed
+5 -1
+5 -1
View File
@@ -721,7 +721,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
LOG_DEBUG("Send config: power");
fromRadioScratch.config.which_payload_variant = meshtastic_Config_power_tag;
fromRadioScratch.config.payload_variant.power = config.power;
fromRadioScratch.config.payload_variant.power.ls_secs = default_ls_secs;
// The app is sent the effective value, so 0 ("use the default") still reads back as the default - see the
// note at the end of this state. A configured value has to survive the round trip, though: a client that
// writes this config back as it read it would otherwise store the default over it.
fromRadioScratch.config.payload_variant.power.ls_secs =
Default::getConfiguredOrDefault(config.power.ls_secs, default_ls_secs);
break;
case meshtastic_Config_network_tag:
LOG_DEBUG("Send config: network");