Repository navigation
Conversation
A Gree zone controller (seen: LE60-13/GH with a ME31-00/C13 WiFi module) drives the dampers of one ducted unit. It answers like a VRF gateway, but its sub-units are the ducted unit (MAC suffix 00) and up to eight zones (01 to 08), and they behave differently from VRF indoor units. Protocol layer: - GreeDevice detects the role of a sub-unit after the info fetch: it reports the name of the controller (GR-ZCntrlr...), and the MAC suffix tells the ducted unit from a zone. - The ducted unit numbers its modes 1 Cool, 2 Heat, 3 Dry, 4 Fan, 5 Auto. Before this, Fan showed as Heat and Auto raised. operation_mode and set_operation_mode convert for every unit with a zone role. - It has no Tur column. Turbo is fan speed 6, only in Cool and Heat. - A zone has a target temperature, StTem, in degrees Celsius minus 16. Only zones poll it, so other units send the same columns as before. - Discovery names the sub-units "AC unit" and "Zone 1" to "Zone 8" instead of "zone" for every one of them. Home Assistant layer: - A zone gets no climate entity. It gets a switch with the name of the zone and a Target Temperature number. - The switch is the optional feature Zone Switch, so it can be left out for a zone that must always stay open. It is only offered on zones, and zones do not show the climate options. - GreeEntityDescription.device_filter limits an entity to some kinds of device when the props alone cannot tell. Tested against a real LE60-13/GH (firmware 362001062617 V1.27) and against a new fake controller in tests/fakes/zone.py.
ZoneRole only knew the sub-units of a zone controller. It is now DeviceType and every device gets one after the info fetch: ac_unit (a standalone unit), vrf_unit (an indoor unit behind a VRF gateway), zone_controller (the ducted unit, sub-unit 00 of a zone controller) and zone. Only the two zone controller types change behaviour, so standalone and VRF units work as before. The type is in the device diagnostics. Suggested by @p-monteiro in RobHofmann#549. The controller device of a zone controller was labelled "VRF gateway". It is now "Zone controller", taken from the type of a bound sub-unit. With no bound sub-unit the registry keeps what it has. The lookup moved into a small helper, so reconcile_vrf_controllers stays simple.
Every fan mode change called set_feature_quiet and set_feature_turbo, also on a unit without those columns. The state then logged "Property Quiet is unsupported on this device" at error level on every change. Seen on the ducted unit of a zone controller, but any unit without Quiet or Turbo did it. Units that have them work as before.
RobHofmann
left a comment
There was a problem hiding this comment.
Thanks for this, and for taking in the DeviceType idea. I ran the PR in my dev instance against my standalone test unit (encryption v1). The start is clean: same 29-column probe, Device type: ac_unit, and the poll still sends the same 20 columns, without StTem. A fan change still sends Quiet and Tur as before. pytest gives 272 passed. I could not test the zone parts myself, because I have no zone controller.
One blocker and one question.
1. translation_key=UNDEFINED breaks setup when no sub-unit is bound (blocker)
In reconcile_vrf_controllers(), when the controller device already exists and _controller_info() finds no bound sub-unit, model and translation_key stay UNDEFINED and go into async_get_or_create(). That works for model, but translation_key is typed str | None, and Home Assistant does this:
if translation_key:
name = self._resolve_translated_name(config_entry, translation_key, translation_placeholders)UNDEFINED is truthy, so the name lookup falls back to the key itself and then calls .format() on it. I ran Home Assistant's own _resolve_translated_name() with UNDEFINED in my dev container:
'vrf_controller' -> 'vrf_controller'
<UndefinedType._singleton: 0> -> RAISES AttributeError 'UndefinedType' object has no attribute 'format'
The call in async_setup_entry() has no try around it. So when a local VRF gateway is offline at Home Assistant start, I expect the whole entry to fail, including standalone units in the same entry, and without a retry because it is not ConfigEntryNotReady. That also hits plain VRF users, not only zone controllers. I did not run that full scenario end to end; the crash itself is reproduced. Mypy flags the same line (helpers.py:475, arg-type), and it is the only new mypy error in the changed files.
A simple fix: only pass model, translation_key and translation_placeholders when the type is known, for example by building the kwargs and leaving those keys out when they are UNDEFINED. A test with an existing controller device and no bound sub-unit would cover it.
2. Zone Switch default in a new setup (question)
async_step_device_options() uses the options of the previous sub-unit of the same controller as defaults (_options_by_controller), and the features default is defaults.get(CONF_FEATURES, valid_features). If the AC unit is configured first, its feature list has no zone, so as far as I can read it, Zone 1 opens with Zone Switch unticked. The other way round, the AC unit could get zone as a default that is not in its options. Did you see this on your controller, or did you tick it by hand? If it is real, filtering the copied defaults against valid_features would solve both cases.
When an existing controller device had no bound sub-unit at start, for example a VRF gateway that is offline, reconcile_vrf_controllers passed UNDEFINED as translation_key. Home Assistant types it str | None and checks it with "if translation_key:", so it tried to format UNDEFINED and the whole entry failed with AttributeError, with no retry. Found in review. The model and the name arguments are now left out when the type is not known, so the registry keeps what it has. A new device still gets the VRF label. The decision moved into _controller_label. Reproduced on Home Assistant 2026.10 with a VRF device whose gateway never answers: before, the entry stayed in setup_error with every entity unavailable; after, the entry loads and the other devices work.
When the device options step starts from the options of the device of the same controller set up before, it copied all of them. So each sub-unit's form showed the name of the device before it, and the features came from a device of another kind: a zone after the AC unit started with Zone Switch off, and the AC unit after a zone got a "zone" default it cannot take. Found in review. - Only the mode and feature lists are copied now, the same lists the copy by model already used. The name stays the device's own. - The Zone Switch is never copied, because only an always open zone goes without it: a zone starts with it on, any other device without it. - The feature default is filtered against the features the device has.
cleanup_device_connection_issues deleted issues from the issue registry while it looped over registry.issues. After a device that had a "connection failed" issue was removed from the config, the next setup deleted that issue and then failed with "RuntimeError: dictionary changed size during iteration", so the entry stayed in setup_error. A second stale issue was never reached. Found while testing the controller label fix: a VRF device whose gateway never answers was removed from the YAML again, and the entry failed at the next start. A manual reload worked, because the issue was already gone.
|
Thanks for testing it on your standalone unit. Both points are fixed, plus a bug I found while testing. 1. Confirmed end to end on Home Assistant 2026.10: with a VRF device whose gateway never answers in the same entry as the zone controller, the entry failed on every start, all its entities were unavailable, and there was no retry. Fixed in 5bfa6cb. The new Mypy missed it on my side because I had only tested in my Home Assistant instance, not with a local one in the dev environment, so every Home Assistant type was Question: A test for 2. Zone Switch default (question) It is real. I had ticked it by hand. My sub-units report no
3. Bug found while testing (already on
|
| # A zone controller and its sub-units report a name that starts with this. | ||
| ZONE_CONTROLLER_NAME_PREFIX = "GR-ZCntrlr" | ||
| # The model ids of the sub-units in the sub-device list. | ||
| ZONE_CONTROLLER_MID_AC_UNIT = "5000" |
There was a problem hiding this comment.
Are we certain that all Gree Zone controllers are 5000/5001 and not only your device?
| return self._device_type | ||
|
|
||
| @property | ||
| def zone_target_temperature(self) -> int | None: |
There was a problem hiding this comment.
Do we need this? Can this logic not be wired to the existing target_temperature properties?
Context
This adds support for Gree zone controllers. It is part of porting the changes I made for my own zone controller (Gree LE60-13/GH) to this project. #547, #548 and #549 prepared for it. It also takes in the
DeviceTypeidea that @p-monteiro suggested in #549. Two more PRs build on this one: the per mode memory of the zone target temperatures, and the same memory for every device. Background and the full plan: https://github.com/Twintop/ha-gree-zone-controller/blob/main/upstream/README.mdWhat a zone controller is
A zone controller drives the dampers of one ducted unit. Seen: LE60-13/GH with a ME31-00/C13 WiFi module, firmware
362001062617+U-W05SAV1.27.bin. It answers the scan like a VRF gateway, withsubCntabove zero, but its sub-units are not indoor units:00is the ducted unit (model id5000). It answersPow,Mod,WdSpdandAllErr, nothing more. ItsPowis the power of the whole system.01to08are the zones (model id5001). A zone answersPow(the damper),StTem(its target temperature, degrees Celsius minus 16) and a copy ofModandWdSpdof the ducted unit.GR-ZCntrlr_...), and the sub-device list calls every unitzone.Symptoms on 5.0-dev
zone, and the gateway device is labelled "VRF gateway".ValueErrorfromOperationMode(5).Turcolumn. Turbo is fan speed 6, so turbo is not offered, and fan speed 6 raisesValueErrorfromFanSpeed(6).StTemis not polled.Property Quiet is unsupported on this deviceat error level. This happens on any unit without Quiet.Change
Protocol layer:
DeviceTypefor every device, set after the info fetch:ac_unit(a standalone unit),vrf_unit(an indoor unit behind a VRF gateway),zone_controller(sub-unit00of a zone controller) andzone. The name the sub-unit reports and its MAC suffix tell them apart. Nothing is added to the config entry, so there is no migration. Only the two zone controller types change behaviour;ac_unitandvrf_unitwork as before. The type is in the device diagnostics.operation_modeandset_operation_mode()convert the zone controller mode numbers.feature_turbo,set_feature_turbo()andsupports_property(FEAT_TURBO_MODE)handle that. Leaving Cool or Heat drops turbo to High.GreeProp.ZONE_TARGET_TEMPERATURE(StTem). Only zones poll it (DeviceState.add_polled()), so all other units send the same columns as before. A test checks that.AC unitandZone 1toZone 8.Home Assistant layer:
GreeEntityDescription.device_filterlimits an entity to some kinds of device when the props alone cannot tell.reconcile_vrf_controllers()stays simple.en.json,icons.json,docs/protocol.md,docs/entities.md,docs/configuration.md,docs/architecture.mdandsupported-devices.mdare updated.Testing
pytest: 272 passed (255 on 5.0-dev, plus 17 new).ake controller that behaves like the real one: subDevform only, nocid, per unit columns, mode and fan mirrored to the zones, and an empty result above the column limit.probe_device_limits()measured 29 columns. The module answers 30 with an empty result, and requests far above that (about 40 columns and more) made it reboot.