vNext: VRF gateways: ask all sub-device list forms, hold sent values until confirmed - #522
Merged
Merged
Conversation
…onfirmed Port of the VRF fixes that landed on master in #507 and #508. #454 needs no port: 5.0 already gives each indoor unit its own device. Sub-device list: a gateway answers one or more of three request forms, depending on its WiFi module firmware (device key, generic key, subDev). The old single hybrid form was never confirmed on hardware. V1 gateways now get all three forms and the lists are joined by MAC; V2 gets the device key form. The generic key form is encrypted with the device key but answered with the generic key, so request_json() takes an optional response_cipher. Gateways from a scan are handled side by side. On the GR-Gcloud V3.2.M gateway from #507 the forms gave 4, 3 and 4 units, joined 4. Stale state: a gateway answers from a cache for a few seconds after a command, so the read right after it showed the old value again. The device state now holds sent values until the device reports them, for at most 8 s. The coordinator polls again every 2 s while values are held. A standalone unit confirms on the first read and gets no extra poll. Suite: 247 passed (227 before).
The stale cache was only seen on VRF gateways. For a standalone unit a hold has a cost: when the unit corrects a value it cannot take, for example an unsupported swing mode, it reports its own value, and the hold kept the refused value on screen for up to 8 s. Standalone units now behave as they did before the hold. To find out whether a standalone unit or the MQTT transport caches too, every device now logs at debug level when the read right after a command does not report the sent value, with the transport and whether it is a sub-unit. Suite: 249 passed.
RobHofmann
added a commit
that referenced
this pull request
Sep 23, 2026
#508 re-applies the commanded values over the read-back for up to 8 s, so a VRF gateway's stale cache does not revert the UI. It did that for every unit. A standalone unit is read directly, and when it corrects a value it cannot take (for example an unsupported swing mode), it reports its own value. The hold then kept the refused value on screen for up to 8 s, where before the UI showed the corrected value at once. The hold and the 2 s follow-up read now only apply when the device is a sub-unit (sub MAC differs from the gateway MAC). Standalone units behave as before #508. The same change went into the 5.0 port (#522).
The generic-key form is answered with the generic key, but its request used the bound device key, so transport.request_json() needed a separate response_cipher for this one case. A probe on a GR-Gcloud V3.2.M gateway (PR 507) showed the gateway ignores the request pack of this form: device, generic and random keys all got the same answer, 3 units, readable with the generic key. The request now uses the generic key too, and response_cipher is removed again. The form stays in the union, because it adds units on some gateways.
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.
Port of the VRF (multi-split) fixes that landed on master in #507, #454 and #508, released in 4.0.7. I checked each one against 5.0 first. Two of the four parts were already covered here, so this PR only fills the two real gaps.
subCnt > 0subCnt == 1already worked1. Sub-device list forms
Symptom. A gateway whose firmware does not answer the one form 5.0 sent gives no indoor units, or only some of them.
Cause. 5.0 sent one hybrid request: envelope
subListwithi: 0, and the reply read with the device key. That matches none of the forms seen on real modules. There was a TODO saying it needed real-world data.Change. The forms below come from #507, captured on real gateways by @meirlo, @Ilya-Draigor and @vellad1. They cover GR-Gcloud
V3.2.Mand the olderW06 V1.1.0.0.t,idevice-keypack,0generic-keysubList,1subDev(older W06 modules)pack,0device-keyform, as on master.generic-keyform uses the generic key both ways. Its reply comes in the generic key, and @meirlo's probe on a GR-Gcloud V3.2.M (Fix VRF sub-device discovery: defer + concurrent subList, robust parsing #507) showed the gateway ignores its request pack: device, generic and random keys all got the same answer. So noresponse_cipheris needed in the transport. The same change for master is Send the generic-key subList request with the generic key #527.asyncio.gather, so a slow gateway does not hold up the others.Measured on master (#507). On a GR-Gcloud V3.2.M the forms gave: device key 4, generic key 3, subDev 4, joined 4.
2. Stale state after a command
Symptom. You set 25 °C on a VRF indoor unit, and the UI jumps back to 21 °C for a moment.
Cause. The gateway caches the state of its units. For a few seconds after a command it answers a status request from that cache.
push_device_status()reads straight after the command, so it gets the old state.Change.
DeviceState.hold()keeps the sent values. While a prop is held,get()returns the sent value.HELD_VALUE_TTL(8 s). Then the reported value wins again, so a rejected command is not shown forever.pending(changes not sent yet) andheld(sent, not confirmed yet) are separate.has_pending_updatescompares with the held value first. Without that, going back to the value the stale cache still shows would be skipped as "no change".async_call_laterandasync_refresh().async_request_refresh()would not work, because its debouncer pushes a second request within its cooldown back to 10 s.async_shutdown().GreeDevice.is_sub_unit: the device MAC differs from the controller MAC). The stale cache was only seen on VRF gateways. For a standalone unit a hold has a cost, raised by @p-monteiro: a unit that corrects a value it cannot take (for example an unsupported swing mode) reports its own value, and a hold would keep the refused value on screen for up to 8 s. Standalone units behave as before, with no hold and no extra poll.state_held.The overlay works like the one on master, with two differences. First, master held values for every unit until #525 scoped it to sub-units the same way. Second, master polls once after 2 s, and here the poll repeats until the hold ends. With a single poll, a rejected command stayed visible until the next scan interval (up to 60 s).
Testing
_get_sub_devices_list()called directly against my standalone unit, which answers no form: result[], one warning, no crash, no command sent.set_temperature22 shows 22 on the first read, with no hold, no extra poll and no debug line about an unconfirmed value. After that I set it back to 21.5.0-dev), in apython:3.14-slimcontainer.tests/test_discovery_vrf.py:subCnt=1subDevtests/test_device_state.pyandtests/test_device.py, with a fake clock:remove()FakeVrfGatewayanswers each form on its own, with its own subset of units. The base fake can report stale state for N reads after a command, or ignore a command.pylint_home_assistant.Notes
uid: 0for thesubDevform. This PR keeps the existinguser_idargument, which is 0 for local discovery. If a W06 module needsuid: 0when discovery runs together with the cloud, this needs a change.Documentation
docs/protocol.md: new "VRF gateways" section with the forms, the key per form, the join, the stale cache and the hold.docs/architecture.md: discovery, state model and coordinator.docs/development.md: the new fake options.docs/troubleshooting.md: two user entries, for missing indoor units and for a value that jumps back.