Skip to content

Prepare for HACS custom wake words - #177676

Draft
synesthesiam wants to merge 3 commits into
devfrom
synesthesiam-20260730-hacs-custom-wake-words
Draft

Prepare for HACS custom wake words#177676
synesthesiam wants to merge 3 commits into
devfrom
synesthesiam-20260730-hacs-custom-wake-words

Conversation

@synesthesiam

Copy link
Copy Markdown
Contributor

Breaking change

Proposed change

Update the custom wake word logic for when HACS gains a new "wake_word" category: hacs/integration#5435
This is in service of the overall roadmap with regards to custom wake words: OpenHomeFoundation/roadmap#180

Specifically, this PR checks subdirectories in config/custom_wake_words since HACS will store them as custom_wake_words/<owner>/<repo_name>/<files>. The owner and repo name are retained for the wake word's internal id to avoid conflicts with the same named wake word.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:
  • Link to developer documentation pull request:
  • Link to frontend pull request:

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

Copilot AI review requested due to automatic review settings July 30, 2026 20:46
@home-assistant

Copy link
Copy Markdown
Contributor

Hey there @jesserockz, @kbx81, @bdraco, mind taking a look at this pull request as it has been labeled with an integration (esphome) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of esphome can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant mark-draft Mark the pull request as draft.
  • @home-assistant ready-for-review Remove the draft status from the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign esphome Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant update-branch Update the pull request branch with the base branch.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables ESPHome to discover HACS-installed custom wake words in nested directories while retaining namespaced IDs.

Changes:

  • Recursively discovers wake-word manifests.
  • Resolves model files from manifest metadata.
  • Updates fixtures and tests for nested paths.

Reviewed changes

Copilot reviewed 2 out of 4 changed files in this pull request and generated 3 comments.

File Description
homeassistant/components/esphome/assist_satellite.py Adds recursive discovery and namespaced IDs.
tests/components/esphome/test_assist_satellite.py Updates nested wake-word expectations.
tests/testing_config/custom_wake_words/choo_choo_homie/choo_choo_homie.json Relocates the test manifest.
tests/testing_config/custom_wake_words/choo_choo_homie/choo_choo_homie.tflite Relocates the test model.
Comments suppressed due to low confidence (1)

homeassistant/components/esphome/assist_satellite.py:928

  • Require a regular model file before opening it. Values such as "model": "." resolve to an existing directory, so exists() passes and the subsequent binary open raises IsADirectoryError, aborting discovery of all custom wake words.
            if not model_path.exists():

)
continue

model_path = config_path.parent / config["model"]
Comment thread homeassistant/components/esphome/assist_satellite.py Outdated
Comment thread homeassistant/components/esphome/assist_satellite.py Outdated
Copilot AI review requested due to automatic review settings July 30, 2026 20:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 4 changed files in this pull request and generated 1 comment.

if not model_path.exists():
# Missing model file
continue
for config_path in wake_words_dir.rglob("*.json"):
The custom wake word inventory is cached for the lifetime of the Home
Assistant process (_get_custom_wake_words is a singleton), so models
added, updated or removed on disk at runtime (e.g. by HACS) did not take
effect until a restart.

Add a reload_custom_wake_words service that drops the cached inventory,
re-scans the directory once, and dispatches a signal. Satellites that
negotiate configuration subscribe to the signal and re-push their config
so new models become available and removed models disappear immediately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 30, 2026 21:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

homeassistant/components/esphome/assist_satellite.py:1005

  • Invalidate the singleton decorator's cache, not only hass.data. Because synchronous @singleton functions are also wrapped in functools.lru_cache (homeassistant/helpers/singleton.py:50-55), this pop is bypassed after the first load and the original inventory is returned, so HACS installs and removals are never rescanned.
        hass.data.pop(_DATA_WAKE_WORDS, None)
        await async_get_custom_wake_words(hass)

homeassistant/components/esphome/assist_satellite.py:126

  • Keep model optional for existing local wake-word manifests. Previously, a manifest containing only type and wake_word loaded its same-stem .tflite; making this key required silently drops that valid legacy format, while the linked HACS validator only guarantees model for HACS-managed repositories. Fall back to the same-stem filename when omitted.
        vol.Required("model"): str,

tests/components/esphome/test_assist_satellite.py:2336

  • Test reload behavior through an observable filesystem change instead of mutating _DATA_WAKE_WORDS. The singleton's hidden lru_cache retains the original inventory, so assigning {} here lets the broken reload implementation pass without rescanning; add or remove a model after initial setup, invoke the service, and assert that the advertised inventory changes.
    assert _DATA_WAKE_WORDS in hass.data

    # Poison the cache so the service must re-scan disk to recover.
    hass.data[_DATA_WAKE_WORDS] = {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants