Covers the two Python behaviours a build YAML cannot: the conditional AUTO_LOAD
that keeps sha256 and json out of builds that do not download models, and the
validator rejecting http_request_id without micro_wake_word.
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.
equals_hex needs exactly 64 hex characters, so an empty or truncated model_hash
downloaded the whole model and then failed with a hash mismatch, which points at
a corrupt download rather than a missing field. Skip the entry when it is
cached instead.
The disable-all loop iterates get_wake_words(), which filters out internal-only
models, but the enable path uses get_model_by_id(), which does not. Enabling one
from a set_configuration request left it active with no way to turn it off short
of a reboot.
make_preference allocates a backend that ESPPreferenceObject never frees, and
restore_runtime_models_ runs for every advertised model on every configuration
request Home Assistant sends. Use load_from_key for the read instead.
A WakeWordModel claims a preference backend that is never released, so a model
built only to be rejected by add_runtime_model() costs internal RAM that never
comes back. The duplicate case is expected here, since a config change can
re-queue a download that is already in flight.
Move the check above the construction. The id was free a moment earlier and
this runs on the main loop, so a failed add is now always a genuine failure.
clang-tidy's performance-inefficient-string-concatenation flags building
the absolute URL with operator+, which allocates a temporary for the
prefix and another for the result. Insert the prefix instead.
A chunked response reports no usable content length, 0 on ESP-IDF and
SIZE_MAX on Arduino, so both the manifest and the model were rejected.
Bound the model read by the size Home Assistant advertised, falling back
to the content length when it advertised none, and read a manifest of
unknown length up to the existing cap. The read still has to deliver the
expected number of bytes, and the SHA256 check is unchanged.
Also move http_request_ out of the block documented as main loop only,
since the download task uses it.
Home Assistant sends the list of external wake words it knows about with
every configuration request. This picks up the ones micro_wake_word can
run, downloads them on demand, and hands them to micro_wake_word so they
can be activated like a compiled-in model.
Set the new http_request option to turn this on. It requires
micro_wake_word to be configured, and pulls in the sha256 and json
components used to verify a download and read its manifest.
How it works:
- The advertised wake words are cached on each configuration request, so
entries Home Assistant stops sending drop out and their models are
unloaded.
- When Home Assistant activates a wake word whose model is not loaded
yet, the model is queued and fetched by a background task. The wake
word is reported as active straight away so the UI reflects the request
while the download runs.
- Downloads are checked against the size and hash from the manifest, and
the model has to use the same feature step size as the frontend.
- A model that fails to load is stored as disabled so it is not retried
on every boot.
- Loaded models are restored after a reboot.
The configuration response now comes entirely from voice_assistant, which
merges the compiled-in models with the cached external wake words, so the
API no longer needs to append them itself.