Repository navigation
vNext: Reconfigure menu with single-device edit - #554
Open
RobHofmann wants to merge 5 commits into
Open
RobHofmann wants to merge 5 commits into
RobHofmann wants to merge 5 commits into
Conversation
Reconfigure always ran discovery and walked through the forms of every device. On a cloud entry it also logged in again, and the new token logs the user out of the Gree app. Reconfigure now shows a menu that loops back to itself: - Edit a device: pick one device and run its connection and option forms. No discovery and no cloud login. When the bind settings are unchanged, the running device is reused and no bind runs. A changed cloud bind uses the stored MQTT token. - Rediscover devices: the old discovery path, with a login for cloud entries. Configured devices only get their host and controller MACs refreshed; only new devices go through the forms. A cloud-only device does not get a local host. - Remove devices. - Save and reload: one write, one reload. The changes collect in one pending device map. The existing steps are reused: connection_options and device_options return to the menu during reconfigure, the bind code moved to _bind_device(), and _async_finish writes the pending map. create_mqtt_transport() is shared with async_setup_entry. Controller-level local settings (host, port, encryption version) are copied to the other units behind the same VRF controller. Reconfigure aborts for entries the YAML import manages, because the YAML puts its values back at the next start. Reauth is unchanged. Tested on a real unit (local-only entry): rename without a bind, bind setting change with a real bind, rediscover, YAML abort. Offline harness for the VRF copy and the cloud-only rule: 6/6. pytest 255 passed. Bump to 5.0.0-alpha.108.
Review feedback on the reconfigure menu: - Rediscover now lists only devices that add something: devices not in the entry, and configured devices for which discovery found a missing connection (a cloud-only device found locally). Every picked device goes through its forms, with the found host filled in. Other found devices get their host refreshed silently. Rediscover never removes a device. When nothing is new, a short step returns to the menu instead of aborting, so pending changes are kept. - The config flow closes its own MQTT and UDP transports: the previous one before a new cloud login, all of them before the entry is written, and in async_remove when the flow ends or is closed. - Entry unload shuts the coordinators down and then disconnects every transport explicitly, also when an unbind fails. The coordinator shutdown runs once. - A refused MQTT connection at setup is logged with the broker's reason before reauth starts. Tested on the real unit (local-only entry): rediscover with nothing new returns to the menu and saves, reload is clean, and a fresh Add Integration flow binds and creates the entry. Offline harness for the VRF copy and the rediscover filter: 11/11.
Owner
Author
|
@p-monteiro thanks for the test run. The new commit addresses your points:
About
|
Config flow tests (tests/config_flow/) run a real Home Assistant through pytest-homeassistant-custom-component 0.13.371 (HA 2026.10.0), against the fake Gree Cloud over loopback HTTP, a fake MQTT broker that forwards to fake units, and fake units on loopback UDP. They cover user setup with cloud and local, reconfigure (finish at once, edit with and without a new bind, rediscover of a cloud-only unit found on the LAN, nothing new, remove, VRF host copy), the YAML abort, reauth after a refused token, and closing a flow after a login. Every test counts the cloud logins and checks that only the loaded entry has open connections. Device registry lookups now go through get_device_by_identifier(). It uses async_get_device_by_identifier on HA 2026.8 and newer, because async_get_device is deprecated and stops working in 2027.8, and falls back to async_get_device with an owner check on older versions. This replaces all five calls in config_flow.py and migration.py. A cloud-only bind in the flow no longer passes a UDP transport. Before, aiogree logged "No controller MAC provided for local transport" at error level on every cloud-only bind. The flow now uses the same rule as the entry setup: a local transport only with a host and a local controller. pytest: 271 passed.
When the MQTT broker refuses the stored token ("Bad user name or
password", MQTT 3.1.1 rc 4/5, which paho reports as 134/135), the
integration now logs in once with the stored email, password and region,
stores the new token and binds again. This happens at entry setup and in
reconfigure when Edit a device binds a cloud device. If that login fails
or the new token is refused too, setup raises ConfigEntryAuthFailed as
before, so reauth still works. One refresh per setup or flow, no loop.
A login that returns another account is refused.
Regression tests for every reported failure: reload with a refused
token, Edit a device with Prefer cloud and a refused token (code 134),
rediscover of a cloud-only unit found on the LAN (now also listed as
Local in Edit a device), two picked units both go through the forms,
and no MQTT client left open after two logins, an edit and a reload.
Docs describe what is known about Gree sessions: a cloud login logs the
app out, an MQTT connection does not, and why a token stops working is
not known (possibly a login rate limit).
pytest: 285 passed.
The connection form showed the raw key cannot_connect_mqtt, because it had no text in any language. The entry card showed bad_credentials, no_account_info or "Failed to initiate Gree device" as plain messages, because ConfigEntryAuthFailed got them as text instead of a translation key. A successful reauth ended on the raw reason reauth_successful, and a second flow for the same account on already_in_progress. - Texts for cannot_connect_mqtt, reauth_successful and already_in_progress in en, nl and pt. - ConfigEntryAuthFailed now uses translation keys: bad_credentials, no_account_info and device_bind_failed (with the MAC). - tests/config_flow/test_translations.py reads every error, abort reason, step, menu option and exception key from the source, plus the reasons Home Assistant aborts with itself, and fails when one has no text in en, nl or pt. pytest: 292 passed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reconfigure now opens a menu, so one device can be edited without discovery and without a cloud login.
1. Reconfigure menu
Symptom. Reconfigure always ran discovery and walked through the forms of every device in the entry. On a cloud entry it also logged in again. The new token logs the user out of the Gree app.
Cause.
async_step_reconfigurewent straight into the setup chain:cloud_add(withlogin()),local_add,device_picker, then the forms for every picked device.Change. Reconfigure shows a menu. Every action returns to it:
entry.runtime_datais reused. No bind runs. Changing only the scan interval or the timeout does not bind either.mac_controller_local. When an edit changes them, the other units get the same values. The form text says so.2. Connection cleanup
async_removecloses them when the flow ends or the user closes it. Before this, every login in a flow left a connection open.async_unload_entryshuts the coordinators down and then disconnects every transport explicitly. The last unbind already closed MQTT in the normal case. This also covers an unbind that fails.GreeCoordinator.async_shutdownnow runs only once, because Home Assistant calls it again from the unload callbacks.3. Refresh a refused cloud token
When the MQTT broker refuses the stored token with "Bad user name or password" (MQTT 3.1.1 rc 4 or 5, which paho reports as 134 or 135), the integration logs in once with the stored email, password and region. Then it stores the new token and binds again. This happens at entry setup, and when Edit a device binds a cloud device. If that login fails, or the new token is refused too, setup raises
ConfigEntryAuthFailedas before, so reauth still works. There is one refresh per setup or flow, so it cannot loop. A login that returns another account is refused.What is known about Gree sessions, from testing: a cloud login logs the Gree app out, and an MQTT connection with an existing token does not. Why Gree stops accepting a token is not known. It can happen soon after a login, and the cloud may rate-limit logins. The docs say exactly this, and suggest a separate account if it happens often.
4. Device registry lookups
device_registry.async_get_deviceis deprecated and stops working in Home Assistant 2027.8. All five calls (config_flow.py,migration.py) now go throughget_device_by_identifier(). It usesasync_get_device_by_identifieron Home Assistant 2026.8 and newer (checked per release). On older versions it falls back toasync_get_devicewith an owner check, becausehacs.jsonstill allows 2026.3.5. Cloud-only bind logged an error
A cloud-only bind in the config flow always passed a UDP transport, so aiogree logged
No controller MAC provided for local transportat error level. This was already on5.0-dev. The flow now uses the same rule as entry setup: a local transport only with a host and a local controller.6. Untranslated keys
Some keys reached the UI without a text:
cannot_connect_mqttin the connection form had no text in any language.ConfigEntryAuthFailedgotbad_credentials,no_account_infoand "Failed to initiate Gree device" as plain messages, so the entry card showed them raw.reauth_successful, and a second flow for the same account onalready_in_progress.All of them now have a text in en, nl and pt. The auth failures use translation keys (
bad_credentials,no_account_info,device_bind_failedwith the MAC). Home Assistant passes these keys on to the entry card (config_entries.py).tests/config_flow/test_translations.pyreads every error, abort reason, step, menu option and exception key from the source, plus the reasons Home Assistant aborts with itself. It fails when one of them has no text in en, nl or pt.7. YAML-managed entries
Reconfigure aborts with
yaml_managedfor an entry that the YAML import handled since the start. The YAML puts its own values back at the next start, so a UI edit would be lost.entry.sourcecannot be used for this: when an import updates an entry that was made in the UI, the source staysuser. The import step records the unique_id inhass.data. That record lives only while Home Assistant runs, so the entry can be edited again after its YAML block is removed and Home Assistant restarts. The 4.xgree:block counts too, because the migration imports it into the local-only entry.8. Reuse
Pedro asked for reuse, not parallel code paths. So:
_async_finishwrites it. Its registry cleanup and its move from local-only to cloud are unchanged.connection_optionsanddevice_optionsare the same steps. During reconfigure they return to the menu instead of finishing._bind_device(), with the same logic._collect_device_configs()is shared by the setup path and the reconfigure path.create_mqtt_transport()andasync_disconnect_transports()inhelpers.pyare shared by__init__.pyand the config flow.setup_picker_schemagot amultipleflag for the radio list.Reauth is unchanged:
async_step_reauth->cloud_add-> the reauth branch of_async_finish. An auth failure still only asks for the cloud login.Config flow tests
New
tests/config_flow/runs a real Home Assistant throughpytest-homeassistant-custom-component0.13.371 (Home Assistant 2026.10.0). It runs against the fake Gree Cloud over loopback HTTP, a fake MQTT broker that forwards to fake units, and fake units on loopback UDP. Every test counts the cloud logins, and checks that only the loaded entry has open connections. It runs in the existing CI job "Tests".Each failure from the review has its own regression test:
bad_credentialstest_setup_refreshes_a_revoked_token_oncecannot_connect_mqttcode 134test_edit_with_prefer_cloud_refreshes_a_revoked_tokentest_rediscover_finds_a_cloud_only_unit_on_the_lantest_rediscover_runs_the_forms_of_every_picked_unittest_flows_with_two_logins_and_an_edit_leave_no_client_openThey also cover: user setup with cloud and local, finish at once without a login, edit with and without a new bind, nothing new, remove, VRF host copy, the YAML abort, reauth when the stored-account login fails too, closing a flow after a login, and both branches of the registry helper.
Testing
Real unit (
U-CS532Z(LT)V3.75, local-only entry), driven through the config flow API:YAML abort: with the YAML blocks present, reconfigure aborts with
yaml_managed.Rename only: the log shows "unchanged, using its running bind". No discovery, one reload, and the new name is stored.
Bind setting changed: encryption version set to auto. A real bind ran in the flow, found v1, and stored
"1".Rediscover: local discovery found the unit and refreshed it without device forms. Back to the menu, saved, and the unit bound again after the reload.
Rediscover, nothing new: returns to the menu and saves. The reload is clean.
Add Integration: a fresh flow (local) finds the unit, binds it and creates the entry, and the entry binds.
Offline harness, 11/11 passed:
pytest: 292 passed. Ruff check and format pass. mypy showed no new errors on the first commit; it was not run again on the later commits. The code was compiled under Python 3.14 in the dev container.
Pedro tested the first commit on his cloud entry: rediscover of a cloud-only unit with its IP (switches to local), remove, and edit. His failures are fixed in the later commits and covered by the regression tests above.
Notes
Gree Cloud refused the stored tokenshows each refresh.unique_id_mismatch) still aborts and loses the pending changes. Home Assistant has no way back to the menu from an abort.mac_controller_local.pt.jsontexts were not written by a native speaker. Please check them.config_flow.py,config_schema.pyandhelpers.py. The PR that merges second needs a rebase.5.0.0-alpha.108.Documentation
docs/configuration.md: the Reconfigure section is rewritten around the menu.docs/config-entry.md: new "Reconfigure" section, and the YAML marking.docs/architecture.mdanddocs/troubleshooting.md: updated for the menu and for when a cloud login happens.docs/config-entry.md: "Token refresh" and "Connection lifetime" sections.docs/development.md,AGENTS.mdand.claude/skills/run-tests/SKILL.md: how to run the config flow tests and what they fake.