Skip to content

Conversation

@TheJulianJES
Copy link
Member

Proposed change

This PR fixes an issue where the ZHA migration completely fails if the user unplugs the old or new radio in between steps.
New dialogs are added in the config/options flow to allow the user to plug back in the old or new adapter respectively.

Initial implementation of puddly's suggestion here: zigpy/backlog#59 (comment)

No user-facing changes with old + new adapters plugged in

If both adapters are plugged in during the entire migration (which we expected when it was rewritten), nothing changes!
These dialogs are only shown if the user unplugged the old or new adapter during the migration, when we need to communicate with it to (1) reset or (2) restore the backup.

Images of new dialogs

Old adapter not found for reset New adapter not found for restore
Bildschirmfoto 2025-10-31 um 04 09 03 Bildschirmfoto 2025-10-31 um 04 09 20

Config flow steps

When migrating adapters, something like this happens in the background:

  1. Old adapter: Take fresh backup (optional, uses existing backup otherwise)
  2. New adapter: List in serial port selector
  3. New adapter: Probe to test
  4. Old adapter: Resetting
  5. New adapter: Restoring backup

Currently, if the old adapter is unplugged for step 4, the whole migration blows up.
The same also applies if the new adapter is unplugged for step 5.

Why do users do this?

Sometimes, users have a valid reason to unplug the old adapter at that point: they have a device with only one (free) USB port for their Zigbee adapter. So, we should not catastrophically fail anymore.

With this PR, retry steps are added to step 4 and 5:

  • For step 4, the retry dialog also allows the option to skip the reset, as this needs to be done when the user uses the "Migrate" option, but no longer has their old adapter (e.g. if it's broken/lost).
  • For step 5, the retry dialog only allows you to retry, not skip restoring the backup, as that would obviously defeat the purpose of the whole migration. This dialog needs to be added, as users may unplug the new adapter to reset their old adapter in step 4.

Allowing migration with one USB port

Theoretically, this also allows the user to perform a migration with just one free USB port for the Zigbee adapter.
Mostly, you just need to follow the instructions. There are two new main possibilities for this:

Detailed(!) steps for doing migration with one USB port (CLICK TO OPEN)

'Live' migration

  1. Have old/active adapter plugged in
  2. Start migration flow: backup will be taken of old adapter
  3. Before choosing to re-configure or migrate in first step:
  4. Unplug the old adapter
  5. Plug in the new adapter
  6. Choose "Migration", NOT "Re-configure current radio"
  7. Choose new adapter from serial port selector (-> it's probed)
  8. Choose "Migrate automatically" strategy
  9. User will get a prompt to plug in old adapter for resetting it. (New! Failed before)
    This reset can also be skipped (continue with step 17), otherwise proceed normally:
  10. Unplug new adapter
  11. Plug in old adapter
  12. Click "Retry" to retry reset
  13. After successful reset, the user gets a prompt about plugging in the new adapter again. (New! Failed before)
  14. Unplug old adapter
  15. Plug in new adapter
  16. Click "Submit" to try restoring backup to new adapter again
  17. Success: Migration completed with one USB port

Old backup migration

  1. Unplug old adapter
  2. Plug in new adapter
  3. Start migration flow: no backup can be taken of old adapter, as it's not plugged in, so an existing one is used
  4. Choose "Migration", NOT "Re-configure current radio" in first step
  5. Choose new adapter from serial port selector
  6. Choose "Migrate automatically" strategy
  7. User will get a prompt to plug in the old adapter to reset OR skip reset. (New! Failed before)
  8. User skips resetting old adapter, as they won't plug it in*.
  9. Success: Migration continued normally, as new adapter is plugged in the entire time

*: It is also possible to reset the old adapter at this point. It just needs to be plugged in briefly. The new adapter can be unplugged during that time. Another dialog will ask to plug in the new adapter afterwards.
This is very similar to steps 10 to 16 from the "Live migration".

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

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 link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

@home-assistant
Copy link

Hey there @dmulcahey, @Adminiuga, @puddly, mind taking a look at this pull request as it has been labeled with an integration (zha) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of zha can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign zha Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component) on the pull request.

Comment on lines +492 to +494
# config entry should always exist here, skip if not
if not config_entries:
return await self.async_step_maybe_confirm_ezsp_restore()
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the one line missing test coverage. Can write a test for this, but we should never be able to hit this.

We could assert that config_entries is not empty, or we could also save the "old adapter device path" to some instance variable in async_step_maybe_reset_old_radio.
Passing it as an additional argument also works, but it's not a pattern I've seen used for any config flows really.

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.

[Core] ZHA migration flow fails if old adapter is unplugged

4 participants