diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 124c2105be..fb9654da5b 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -297,9 +297,11 @@ void Fan::save_state_() { state.preset_mode = FanRestoreState::NO_PRESET; if (this->has_preset_mode()) { - // Use Fan-owned vector, or fall back to traits for deprecated path + // Use Fan-owned vector, or fall back to traits for deprecated path. + // Keep traits alive so the reference to compat_preset_modes_ doesn't dangle. + auto traits = this->supported_preset_modes_ ? FanTraits() : this->get_traits(); const auto &preset_modes = - this->supported_preset_modes_ ? *this->supported_preset_modes_ : this->get_traits().supported_preset_modes(); + this->supported_preset_modes_ ? *this->supported_preset_modes_ : traits.supported_preset_modes(); for (size_t i = 0; i < preset_modes.size(); i++) { if (preset_modes[i] == this->preset_mode_) { state.preset_mode = i; diff --git a/esphome/components/fan/fan_traits.h b/esphome/components/fan/fan_traits.h index 6a003f535f..046ae50929 100644 --- a/esphome/components/fan/fan_traits.h +++ b/esphome/components/fan/fan_traits.h @@ -48,6 +48,10 @@ class FanTraits { this->compat_preset_modes_ = preset_modes; } + // Deleted overloads to catch incorrect std::string usage at compile time with clear error messages + void set_supported_preset_modes(const std::vector &preset_modes) = delete; + void set_supported_preset_modes(std::initializer_list preset_modes) = delete; + /// Return if preset modes are supported bool supports_preset_modes() const { return (this->preset_modes_ && !this->preset_modes_->empty()) || !this->compat_preset_modes_.empty();