Skip to content

Commit bd74589

Browse files
zkoalexeyclaude
andauthored
[switch] Persist the state when a restore mode is switched on at run time (#69)
A switch whose Start mode is changed to "Last" from the dashboard or the front-panel menu came back at the mode's default after a reboot instead of where it was left. publish_state() is the only writer of rtc_ and it drops duplicates, so the update() that follows set_restore_mode() stored nothing when the state had not changed. And under JETHOME_DYNAMIC_ENTITY_SETTINGS rtc_ was created inside get_initial_state(), which get_initial_state_with_restore_mode() only calls for a persistent mode - so a switch that booted as ALWAYS_OFF had no preference to write to in the first place. set_restore_mode() now stores the current state when persistence goes on and syncs it, since a mode change is a rare, deliberate act that a user is likely to power-cycle right after. The preference is created whatever the mode is. Before setup() rtc_ has no backend, save() fails and nothing is written, so the boot-time apply of stored settings cannot overwrite the state it is about to restore. Verified under QEMU on jxd-r6-e1eth-lcd, over both paths (dashboard and the front-panel menu) and against a control build without the fix. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d8e7dda commit bd74589

2 files changed

Lines changed: 19 additions & 2 deletions

File tree

esphome/components/switch/switch.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ void Switch::toggle() {
3131

3232
void Switch::update() { this->write_state(this->state != this->inverted_); }
3333

34+
void Switch::set_restore_mode(SwitchRestoreMode restore_mode) {
35+
// publish_state() dedups, so switching persistence on has to store the state itself, and
36+
// sync it - the user may power-cycle right after. Before setup() rtc_ has no backend.
37+
const bool becomes_persistent =
38+
(restore_mode & RESTORE_MODE_PERSISTENT_MASK) && !(this->restore_mode & RESTORE_MODE_PERSISTENT_MASK);
39+
this->restore_mode = restore_mode;
40+
if (becomes_persistent && this->rtc_.save(&this->state))
41+
global_preferences->sync();
42+
}
43+
3444
optional<bool> Switch::get_initial_state() {
3545
#ifdef JETHOME_DYNAMIC_ENTITY_SETTINGS
3646
this->rtc_ = global_preferences->make_preference<bool>(this->get_preference_hash());
@@ -48,12 +58,18 @@ optional<bool> Switch::get_initial_state() {
4858
return initial_state;
4959
}
5060
optional<bool> Switch::get_initial_state_with_restore_mode() {
61+
#ifdef JETHOME_DYNAMIC_ENTITY_SETTINGS
62+
// the mode is runtime-changeable, so rtc_ has to exist whatever it currently is
63+
const optional<bool> restored_state = this->get_initial_state();
64+
#endif
5165
if (restore_mode & RESTORE_MODE_DISABLED_MASK) {
5266
return {};
5367
}
5468
bool initial_state = restore_mode & RESTORE_MODE_ON_MASK; // default value *_OFF or *_ON
5569
if (restore_mode & RESTORE_MODE_PERSISTENT_MASK) { // For RESTORE_*
56-
optional<bool> restored_state = this->get_initial_state();
70+
#ifndef JETHOME_DYNAMIC_ENTITY_SETTINGS
71+
const optional<bool> restored_state = this->get_initial_state();
72+
#endif
5773
if (restored_state.has_value()) {
5874
// Invert value if any of the *_INVERTED_* modes
5975
initial_state = restore_mode & RESTORE_MODE_INVERTED_MASK ? !restored_state.value() : restored_state.value();

esphome/components/switch/switch.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ class Switch : public EntityBase, public EntityBase_DeviceClass {
125125

126126
bool is_inverted() const;
127127

128-
void set_restore_mode(SwitchRestoreMode restore_mode) { this->restore_mode = restore_mode; }
128+
/// Set the restore mode. Turning persistence on stores the current state right away.
129+
void set_restore_mode(SwitchRestoreMode restore_mode);
129130

130131
void update();
131132

0 commit comments

Comments
 (0)