vNext: YAML import for the gree_custom block - #512
Conversation
The integration already started an import flow for every item under gree_custom: in configuration.yaml, but async_step_import aborted with not_implemented and there was no CONFIG_SCHEMA, so the YAML was never validated and never applied. Changes: - config_schema.py: CONFIG_SCHEMA for the shape in manual-configuration.yaml. Normalizes MAC keys (separators and case), derives the controller MACs when they are not given, fills the connection defaults, validates the mode and feature lists, and checks that every device can be reached (a local block, or a cloud account plus a cloud block). Only one item may leave out the cloud block. __init__.py imports the schema so Home Assistant and hassfest find it. - config_flow.py: async_step_import resolves the target entry (local-only or cloud account), skips devices that already belong to another entry, then creates the entry or updates it with async_update_reload_and_abort. Unchanged YAML causes no reload. Cloud login happens only when no entry stores the same email, region and password; otherwise the stored uid and token are reused. - A yaml_import_failed repair issue and an import_failed abort reason. - Docs: README example was still the 4.x flat shape. manual-configuration.yaml now matches the schema (faults was listed as a feature, the controller MACs are optional). docs/config-entry.md and docs/architecture.md describe the import. Tested on HA 2026.9.3 against the real unit (U-CS532Z, encryption v1): update of an existing UI entry (name and features applied, one setup, no reload), unchanged YAML on restart (entry not modified, one setup), and creation after the entry was removed (source import, bound, six entities). Schema harness: 19/19 checks. ruff check and format pass. Pylint and mypy were not run, neither is available here.
Complete nl.json with every key from en.json, including the new import_failed abort reason and the yaml_import_failed repair issue.
|
|
||
| return all( | ||
| stored.get(key) == cloud_conf.get(key) | ||
| for key in (CONF_EMAIL, CONF_REGION, CONF_PASSWORD) |
There was a problem hiding this comment.
This might break if the user changes the email of the account. That's why the config entry unique_ids is the account ID, which is a fixed int, and can only be obtained after login. Here's the thing: logging in every time you import to check this will log you out of wherever you have logged in previously, like the Gree app, which may be annoying. Not saying it cannot be done, but at least inform the users in manual-configuration.yaml and the README about that behaviour.
There was a problem hiding this comment.
Good point on the sessions. The match is only there to avoid a login when nothing changed. A login happens on the first import and after a change of email, region or password. A changed email fails the match, so there is one login, and the user id from that login is the unique_id, so the import still lands on the same entry. Without a login there is no uid to match on, so I kept the three YAML fields. I documented when a login happens and that it ends the other sessions (Gree app) in the README, manual-configuration.yaml and docs/config-entry.md in e222e2c.
| password=cloud_conf[CONF_PASSWORD], | ||
| ) | ||
| try: | ||
| credentials = await cloud_api.login() |
There was a problem hiding this comment.
Same as the other comment. This will log you out of other sessions.
There was a problem hiding this comment.
Same answer as above: this login only runs when no entry stores the same email, region and password, so on the first import or after one of those changes. Documented in e222e2c.
| local: # optional, if ommitted requires cloud | ||
| mac_controller_local: "20fabb123456" # MAC Address of the local controller (see above) | required | str | ||
| local: # optional, if omitted requires cloud | ||
| mac_controller_local: "20fabb123456" # MAC Address of the local controller (see above) | optional | str | default = the controller MAC derived from the device MAC |
There was a problem hiding this comment.
For VRF, this is not possible. It goes the other way around. Here is the MAC address of the device that holds the connection (with the IP), and the MAC key above has to be obtained by discovering subdevices.
Granted, the extract_mac function used will parse the value above and split it, but if the device is a VRF and only the subdevice MAC is provided above, the controller will not be derived.
There was a problem hiding this comment.
You are right, I had the direction wrong. Fixed in e222e2c: for a 14 character MAC with a local block, mac_controller_local is now required and the schema rejects the config with a message that says it must be the MAC of the unit that holds the connection. A normal unit still defaults to its own MAC. The cloud controller defaults to the first 12 characters, as protocol.md says. manual-configuration.yaml and docs/config-entry.md are updated, and the harness has a case for both.
There was a problem hiding this comment.
Reworked in 57ee11c, back on gree_extract_macs for the device key, so it behaves like discovery: a 14 character main device MAC ending in 00 derives the first 12, and a key written as @ sets the controller for both local and cloud. A sub-device MAC without @ is rejected for a local block, with a message that names both ways to fix it. A given mac_controller_local or mac_controller_cloud still wins. manual-configuration.yaml and docs/config-entry.md describe the @ form.
- A VRF sub-device (14 character MAC) has its local controller in another unit, so mac_controller_local cannot be derived from the device MAC. The schema now requires it for a 14 character MAC with a local block, and rejects the config with a clear message otherwise. For a normal unit the default stays the device MAC. The cloud controller defaults to the first 12 characters, as protocol.md says. - README, manual-configuration.yaml and docs/config-entry.md now say when a cloud login happens (first import, or a change of email, region or password) and that every login ends the other sessions of the account, so the Gree app logs out at that moment. Schema harness: 20/20 checks.
The device key now goes through gree_extract_macs(), the same function local and cloud discovery use. That brings back the two cases the last commit dropped: a VRF main device MAC that ends in 00 derives the first 12 characters as controller, and a key written as <mac>@<controller mac> sets the controller for a VRF sub-device. A given mac_controller_local or mac_controller_cloud still wins over the derived value. A VRF sub-device key without @ has no known local controller, so the schema rejects it when a local block is given, with a message that names both ways to fix it. Schema harness: 22/22 checks.
SETUP_SCHEMA and the five setup_*_schema() builders now live next to CONFIG_SCHEMA, without the leading underscore and with docstrings. config_flow.py imports them. The temperature step form uses MIN_TARGET_TEMP_STEP and MAX_TARGET_TEMP_STEP instead of the literals. No behaviour change.
|
Review nits done in 6554d16: SETUP_SCHEMA and the five setup_*_schema() builders moved to config_schema.py, public names with docstrings, config_flow.py imports them. The temperature step form now uses MIN_TARGET_TEMP_STEP and MAX_TARGET_TEMP_STEP. No behaviour change; the moved bodies are identical apart from the renames. The probatio IP and MAC validators are a good follow-up once HA supports them fully. |
What
YAML import for the
gree_custom:block inconfiguration.yaml. The integration already started an import flow per YAML item inasync_setup, butasync_step_importaborted withnot_implementedand there was noCONFIG_SCHEMA, so the YAML was never validated or applied.1. Schema
New
config_schema.pywithCONFIG_SCHEMA, for the shape inmanual-configuration.yaml.gree_extract_macs(), the same function discovery uses, so the controller MACs default the way discovery sets them: the device MAC for a normal unit, the first 12 characters for a VRF main device MAC ending in00, and the part after@for a key written as<mac>@<controller mac>. A VRF sub-device key without@has no known local controller, so the schema requiresmac_controller_localfor it. A given controller MAC always wins over the derived one.docs/config-entry.md. Option lists are only stored when written; the platforms already fall back to their defaults.encryption_versionin"0","1","2"(int accepted),target_temp_stepa multiple of 0.5 between 0.5 and 5,scan_intervalat leastMIN_SCAN_INTERVAL, external sensors as entity ids. Every device needs alocalblock, or a top levelcloudaccount plus acloudblock. Only one item may leave outcloud, because all local-only devices share one entry.__init__.pyimports the schema, which also clears the hassfestconfig_schemawarning.2. Import flow
async_step_importinconfig_flow.py:local_onlywithout acloudblock. With one, it looks for an entry that already stores the same email, region and password and reuses itsunique_id,uidandtoken, so there is no cloud login on every restart. Only when there is none does it log in: on the first import, or after a change of email, region or password. A changed email still lands on the same entry, because the user id from the login is theunique_id. The README andmanual-configuration.yamlsay that every login ends the other sessions of the account (the Gree app logs out).yaml_import_failedrepair issue when nothing is left.async_update_reload_and_abort(..., reload_even_if_entry_is_unchanged=False). Unchanged YAML changes nothing. Changed YAML updates the entry and reloads it once. The reload waits for the entry'ssetup_lock, so it is safe while the entry is still being set up at startup. Devices dropped from the YAML are removed from the entry and the device registry.The rules for users are in the README section and at the top of
manual-configuration.yaml: the YAML is applied at every start and wins over UI changes to the same entry.3. Docs
gree_customexample plus the rules.manual-configuration.yamlnow matches the schema:faultsis not a feature (it is auto-detected), the controller MACs are optional, the external sensors are omitted instead of"None",encryption_versionshows"0".docs/config-entry.mdhas a "YAML import" section.docs/architecture.mdlistsconfig_schema.py.4. Dutch translation
New
translations/nl.jsonwith every key fromen.json, including the two new keys above. Checked by script: same key set, same placeholders. Reviewed by a native speaker (the repo owner).Testing
Real unit (U-CS532Z(LT)V3.75, encryption v1) on Home Assistant 2026.9.3:
switch.*_display_lightcreated from the new featuremodified_atunchanged, one setup, no errorssource: import, bound, six entities, same entity idscheck_configOffline: a schema harness with 20 checks (the shipped example, MAC normalization, VRF controller MAC, every reject rule above). All pass.
ruff checkandruff format --checkpass. Pylint and mypy were not run; neither was available.Not tested: the cloud paths (no account here) and the "device already in another entry" path (needs two entries).
Notes
CONFIG_SCHEMA. UI entries then do not load either until the YAML is fixed.manifest.jsonis not bumped.