From 0af1396ea36339fc4664559d4c092d00a5f95464 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 14 Oct 2026 17:01:50 +0000 Subject: [PATCH] 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 --- src/mesh/PhoneAPI.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index f97a89a14a..c38147fe29 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -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");