Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A custom Home Assistant integration for Gree air conditioners. Domain `gree_cust

The domain is `gree_custom` and not `gree` because Home Assistant ships its own `gree` integration. Do not rename it.

Older releases (4.x) used the domain `gree` and a different code base. Their config entries are not compatible with this version and there is no migration.
Older releases (4.x) used the domain `gree` and a different code base. `migration.py` moves a 4.x setup over; see [docs/config-entry.md](docs/config-entry.md#migration-from-4x).

## Versions

Expand Down
21 changes: 19 additions & 2 deletions custom_components/gree_custom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
)
from .coordinator import GreeConfigEntry, GreeCoordinator
from .helpers import try_find_new_ip
from .migration import (
async_migrate_legacy_registry,
async_prepare_legacy_migration,
async_remove_unprovided_entities,
async_unload_legacy_entries,
)
from .services import async_setup_services

ISSUE_DEVICE_CONNECTION_FAILED = "device_connection_failed"
Expand All @@ -85,8 +91,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:

async_setup_services(hass)

# Setup YAML entries
for gree_config in config.get(DOMAIN, []):
# Bring over a 4.x setup, then set up the YAML entries
items = await async_prepare_legacy_migration(
hass, config, list(config.get(DOMAIN, []))
)
for gree_config in items:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -125,6 +134,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: GreeConfigEntry) -> bool

cleanup_device_connection_issues(hass, entry.entry_id, set(device_configs.keys()))

# Stop the 4.x entries of these devices, so only one client talks to a unit
await async_unload_legacy_entries(hass, entry)

for mac, dev_config in device_configs.items():
connection = dev_config.get(CONF_DEVICE_CONNECTION)
options = dev_config.get(CONF_DEVICE_OPTIONS)
Expand Down Expand Up @@ -242,7 +254,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: GreeConfigEntry) -> bool
entry.runtime_data = {}
entry.runtime_data = coordinators

# Move the 4.x registry rows before the entities are created
moved = await async_migrate_legacy_registry(hass, entry)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

async_remove_unprovided_entities(hass, entry, moved)
return True


Expand Down
49 changes: 49 additions & 0 deletions custom_components/gree_custom/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
get_discovery_addresses,
get_entry_matching_mac,
)
from .migration import async_setup_from_flow

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -282,6 +283,48 @@ def _abort_yaml_import(self, item_id: str, err: Exception) -> ConfigFlowResult:
create_yaml_import_issue(self.hass, item_id, f"cloud login failed: {err}")
return self.async_abort(reason="import_failed")

async def async_step_migrate(self, migrate_data: dict) -> ConfigFlowResult:
"""Add the devices of 4.x config entries to the local-only entry.

Started by `migration.py` when no YAML block manages the local devices.
Unlike the YAML import, this only adds devices and never removes one.
"""
devices: dict[str, Any] = dict(migrate_data[CONF_DEVICES])

await self.async_set_unique_id(CONFENTRY_ID_LOCAL_ONLY)

other_macs = get_configured_macs_in_entries(
self.hass, ignore_entries=[CONFENTRY_ID_LOCAL_ONLY]
)
for mac in [m for m in devices if m in other_macs]:
_LOGGER.info(
"Migration from 4.x: device %s is already in entry '%s'",
mac,
other_macs[mac].title,
)
devices.pop(mac)

entry = self.hass.config_entries.async_entry_for_domain_unique_id(
DOMAIN, CONFENTRY_ID_LOCAL_ONLY
)
known: dict[str, Any] = dict(entry.data.get(CONF_DEVICES, {})) if entry else {}
new_devices = {m: d for m, d in devices.items() if m not in known}

if not new_devices:
return self.async_abort(reason="already_configured")

if entry:
return self.async_update_reload_and_abort(
entry,
data={**entry.data, CONF_DEVICES: {**known, **new_devices}},
reason="reconfigure_successful",
)

return self.async_create_entry(
title="Local-only Devices",
data={CONF_CLOUD: None, CONF_DEVICES: new_devices},
)

@override
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
Expand All @@ -290,6 +333,9 @@ async def async_step_dhcp(

_LOGGER.debug("Gree device discovered from dhcp: %s", discovery_info)

if await async_setup_from_flow(self.hass):
return self.async_abort(reason="legacy_migration_started")

# Check what's under that device: Main device and sub-devices
# If it does not respond locally, there's no use of this information
discover = await gree_discover_device_local(
Expand Down Expand Up @@ -343,6 +389,9 @@ async def async_step_dhcp(
@override
async def async_step_user(self, user_input: dict | None = None) -> ConfigFlowResult:
"""Handle the initial step - how to add devices."""
if user_input is None and await async_setup_from_flow(self.hass):
return self.async_abort(reason="legacy_migration_started")

errors: dict[str, str] = {}
if user_input is not None:
self._selected_setup_methods = user_input[CONF_DISCOVERY]
Expand Down
Loading
Loading