From 0ff3408564f3a509360f99b8900b3c26e1c381a0 Mon Sep 17 00:00:00 2001 From: agessaman Date: Sat, 19 Sep 2026 11:27:01 -0700 Subject: [PATCH] fix(wifi): keep the legacy power-save default off; store explicit min as 3 Stored 0 was the shipped default from 2026-01-02 to 2026-03-28, and every association path has run it with power save off since. Reading it as MIN_MODEM, as F11 did, would have put every node set up in that window into modem sleep on its next association. 0 now reads as `none`, and `set wifi.powersave min` stores a new value 3. /mqtt.json accepts 0..3; older firmware repairs 3 to `none` on load. Binary snapshots are no longer written, so 3 never reaches a legacy layout. --- src/helpers/MQTTPrefsSerializer.h | 3 ++- src/helpers/WifiPowerSavePolicy.h | 15 +++++++++------ .../test_wifi_power_save_policy.cpp | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/helpers/MQTTPrefsSerializer.h b/src/helpers/MQTTPrefsSerializer.h index f783b52c4..e256e6a9d 100644 --- a/src/helpers/MQTTPrefsSerializer.h +++ b/src/helpers/MQTTPrefsSerializer.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef WITH_MQTT_BRIDGE @@ -50,7 +51,7 @@ class MQTTPrefsSerializer : public ConfigSerializer { public: explicit WifiPrefs(MQTTPrefs* prefs) : _prefs(prefs), _power_save(prefs->wifi_power_save) {} bool apply(bool* repaired) { - if (_power_save < 0 || _power_save > 2) { + if (_power_save < 0 || _power_save > WifiPowerSavePolicy::kMaxStoredValue) { _power_save = 1; *repaired = true; } diff --git a/src/helpers/WifiPowerSavePolicy.h b/src/helpers/WifiPowerSavePolicy.h index bdebd57e1..bb8a35719 100644 --- a/src/helpers/WifiPowerSavePolicy.h +++ b/src/helpers/WifiPowerSavePolicy.h @@ -11,15 +11,18 @@ // to `min` silently ran with power save off after its first reconnect while // `get wifi.powersave` still said min. // -// Stored values are fleet state — never renumber them. The product default is -// `none`, which is a *default* (MQTTDefaults.h), not a reinterpretation of an -// operator's explicit `min`. +// Stored values are fleet state — never renumber them. 0 was the shipped +// default from 2026-01-02 to 2026-03-28, and every association path ran it with +// power save off, so it keeps meaning `none`; an explicit `min` is stored as 3. +// Older firmware repairs 3 to `none` when it loads /mqtt.json. namespace WifiPowerSavePolicy { enum StoredValue : uint8_t { - kMin = 0, // WIFI_PS_MIN_MODEM - kNone = 1, // WIFI_PS_NONE (default) - kMax = 2, // WIFI_PS_MAX_MODEM + kLegacyDefault = 0, // WIFI_PS_NONE: the old default, never an operator choice + kNone = 1, // WIFI_PS_NONE (default) + kMax = 2, // WIFI_PS_MAX_MODEM + kMin = 3, // WIFI_PS_MIN_MODEM + kMaxStoredValue = kMin, }; // Mirrors wifi_ps_type_t. MQTTBridge.cpp static_asserts these against the SDK. diff --git a/test/test_wifi_power_save_policy/test_wifi_power_save_policy.cpp b/test/test_wifi_power_save_policy/test_wifi_power_save_policy.cpp index 66803aa5e..35eb39f94 100644 --- a/test/test_wifi_power_save_policy/test_wifi_power_save_policy.cpp +++ b/test/test_wifi_power_save_policy/test_wifi_power_save_policy.cpp @@ -13,8 +13,18 @@ TEST(WifiPowerSavePolicy, StoredValuesMapToOneModeEach) { EXPECT_EQ(kModeMaxModem, modeFor(kMax)); } +// Stored 0 was the default for nodes set up 2026-01-02..03-28, and they have +// always run with power save off; reading it as `min` would put them to sleep. +TEST(WifiPowerSavePolicy, LegacyDefaultKeepsPowerSaveOff) { + EXPECT_EQ(kModeNone, modeFor(kLegacyDefault)); + EXPECT_STREQ("none", nameFor(kLegacyDefault)); + uint8_t parsed = 0xFF; + ASSERT_TRUE(parseName("min", &parsed)); + EXPECT_NE(kLegacyDefault, parsed); +} + TEST(WifiPowerSavePolicy, NamesRoundTripWithStoredValues) { - for (uint8_t stored = 0; stored <= 2; stored++) { + for (uint8_t stored = kNone; stored <= kMaxStoredValue; stored++) { uint8_t parsed = 0xFF; ASSERT_TRUE(parseName(nameFor(stored), &parsed)) << "stored " << (int)stored; EXPECT_EQ(stored, parsed); @@ -25,7 +35,7 @@ TEST(WifiPowerSavePolicy, NamesRoundTripWithStoredValues) { // A byte outside the stored range must read as the product default, not as // whatever mode happens to sit at that index. TEST(WifiPowerSavePolicy, OutOfRangeStoredValueReadsAsDefault) { - for (int stored = 3; stored <= 255; stored++) { + for (int stored = kMaxStoredValue + 1; stored <= 255; stored++) { EXPECT_EQ(kModeNone, modeFor((uint8_t)stored)) << "stored " << stored; EXPECT_STREQ("none", nameFor((uint8_t)stored)); }