[micro_wake_word] Own the enabled-state preference key

voice_assistant derived the same fnv1_hash(id) key independently to restore
downloaded models, with nothing tying the two together. Expose the derivation as
WakeWordModel::enabled_preference_key so the component that owns the persistence
format owns the key too.
This commit is contained in:
Kevin Ahrendt
2026-08-10 08:11:47 -04:00
parent 398f29dced
commit 757f7aee4f
3 changed files with 12 additions and 5 deletions
@@ -270,7 +270,7 @@ WakeWordModel::WakeWordModel(const std::string &id, const uint8_t *model_start,
this->current_stride_step_ = 0;
this->internal_only_ = internal_only;
this->pref_ = global_preferences->make_preference<bool>(fnv1_hash(id));
this->pref_ = global_preferences->make_preference<bool>(WakeWordModel::enabled_preference_key(id));
bool enabled;
if (this->pref_.load(&enabled)) {
// Use the enabled state loaded from flash
@@ -305,7 +305,7 @@ WakeWordModel::WakeWordModel(const std::string &id, std::shared_ptr<ModelData> m
this->current_stride_step_ = 0;
this->internal_only_ = false; // Runtime models are always exposed to Home Assistant
this->pref_ = global_preferences->make_preference<bool>(fnv1_hash(id));
this->pref_ = global_preferences->make_preference<bool>(WakeWordModel::enabled_preference_key(id));
bool enabled;
if (this->pref_.load(&enabled)) {
// Use the enabled state loaded from flash
@@ -157,6 +157,13 @@ class WakeWordModel final : public StreamingModel {
bool get_internal_only() { return this->internal_only_; }
/// @brief Derives the preference key holding a model's enabled state. This component owns the persistence
/// format, so anything reading or writing that state (voice_assistant restores downloaded models from it)
/// must derive the key through here rather than repeating the hash.
/// @param id (std::string) identifier for the model
/// @return The preference key for the model's enabled state
static uint32_t enabled_preference_key(const std::string &id) { return fnv1_hash(id); }
protected:
// Kept for runtime-downloaded models so the model buffer stays alive for the model's lifetime.
// Null for compiled-in models (their data lives in flash).
@@ -1309,8 +1309,8 @@ void VoiceAssistant::restore_runtime_models_() {
// Only re-download models the user had enabled before the reboot. Read the key directly: make_preference
// allocates a backend that is never freed, and this runs for every advertised model on every request.
bool enabled = false;
if (global_preferences->load_from_key(fnv1_hash(cached_ww.id), reinterpret_cast<uint8_t *>(&enabled),
sizeof(enabled)) &&
if (global_preferences->load_from_key(micro_wake_word::WakeWordModel::enabled_preference_key(cached_ww.id),
reinterpret_cast<uint8_t *>(&enabled), sizeof(enabled)) &&
enabled) {
ESP_LOGD(TAG, "Restoring runtime model %s", cached_ww.id.c_str());
this->model_download_queue_.push_back(cached_ww);
@@ -1345,7 +1345,7 @@ void VoiceAssistant::erase_pending_wake_word_(const std::string &id) {
void VoiceAssistant::mark_model_load_failed_(const std::string &id) {
// Persist disabled so a broken model isn't retried on every boot, and drop the optimistic active entry so
// HA sees the real (inactive) state.
auto pref = global_preferences->make_preference<bool>(fnv1_hash(id));
auto pref = global_preferences->make_preference<bool>(micro_wake_word::WakeWordModel::enabled_preference_key(id));
bool enabled = false;
pref.save(&enabled);
this->erase_pending_wake_word_(id);