From 3dab2915dc86cb16ba3aecf9957a1b173fbee420 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 6 Nov 2025 19:51:53 +0000 Subject: [PATCH] Fix Growatt integration authentication error for legacy config entries Adds migration logic to handle config entries created before the CONF_AUTH_TYPE field was introduced in PR #149783. The migration automatically detects the authentication type based on the presence of either CONF_TOKEN (for API token auth) or CONF_USERNAME/CONF_PASSWORD (for password auth) and updates the config entry accordingly. This resolves the "Unknown authentication type in config entry" error that users experienced after upgrading to Home Assistant Core 2025.11.0. Fixes #155906 --- .../components/growatt_server/__init__.py | 16 +++++ tests/components/growatt_server/test_init.py | 71 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/homeassistant/components/growatt_server/__init__.py b/homeassistant/components/growatt_server/__init__.py index 6483e7a543c672..41942b8559a34e 100644 --- a/homeassistant/components/growatt_server/__init__.py +++ b/homeassistant/components/growatt_server/__init__.py @@ -136,6 +136,22 @@ async def async_setup_entry( new_data[CONF_URL] = url hass.config_entries.async_update_entry(config_entry, data=new_data) + # Migration logic for config entries created before CONF_AUTH_TYPE was added + if CONF_AUTH_TYPE not in config: + # Determine auth type based on what fields are present + if CONF_TOKEN in config: + auth_type = AUTH_API_TOKEN + elif CONF_USERNAME in config and CONF_PASSWORD in config: + auth_type = AUTH_PASSWORD + else: + raise ConfigEntryError("Cannot determine authentication type from config entry.") + + # Update the config entry with the determined auth type + new_data = dict(config_entry.data) + new_data[CONF_AUTH_TYPE] = auth_type + hass.config_entries.async_update_entry(config_entry, data=new_data) + config = config_entry.data + # Determine API version if config.get(CONF_AUTH_TYPE) == AUTH_API_TOKEN: api_version = "v1" diff --git a/tests/components/growatt_server/test_init.py b/tests/components/growatt_server/test_init.py index eddbf81d770f3e..5c9e4478f02c2d 100644 --- a/tests/components/growatt_server/test_init.py +++ b/tests/components/growatt_server/test_init.py @@ -174,3 +174,74 @@ async def test_multiple_devices_discovered( assert device1 == snapshot(name="device_min123456") assert device2 is not None assert device2 == snapshot(name="device_min789012") + + +async def test_migration_legacy_token_config_entry( + hass: HomeAssistant, + mock_growatt_v1_api, +) -> None: + """Test migration of legacy config entry without CONF_AUTH_TYPE (token auth).""" + from homeassistant.components.growatt_server.const import ( + AUTH_API_TOKEN, + CONF_AUTH_TYPE, + CONF_PLANT_ID, + ) + from homeassistant.const import CONF_TOKEN, CONF_URL + + # Create a legacy config entry without CONF_AUTH_TYPE (but has CONF_TOKEN) + legacy_config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_TOKEN: "test_token_123", + CONF_URL: "https://openapi.growatt.com/", + "user_id": "12345", + CONF_PLANT_ID: "plant_123", + "name": "Test Plant", + }, + unique_id="plant_123", + ) + + await setup_integration(hass, legacy_config_entry) + + # Verify the config entry was migrated with CONF_AUTH_TYPE + assert legacy_config_entry.state is ConfigEntryState.LOADED + assert CONF_AUTH_TYPE in legacy_config_entry.data + assert legacy_config_entry.data[CONF_AUTH_TYPE] == AUTH_API_TOKEN + + +async def test_migration_legacy_password_config_entry( + hass: HomeAssistant, + mock_growatt_classic_api, +) -> None: + """Test migration of legacy config entry without CONF_AUTH_TYPE (password auth).""" + from homeassistant.components.growatt_server.const import ( + AUTH_PASSWORD, + CONF_AUTH_TYPE, + CONF_PLANT_ID, + ) + from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME + + # Create a legacy config entry without CONF_AUTH_TYPE (but has username/password) + legacy_config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_USERNAME: "test_user", + CONF_PASSWORD: "test_password", + CONF_URL: "https://openapi.growatt.com/", + CONF_PLANT_ID: "12345", + "name": "Test Plant", + }, + unique_id="12345", + ) + + # Classic API needs TLX device type for test + mock_growatt_classic_api.device_list.return_value = [ + {"deviceSn": "TLX123456", "deviceType": "tlx"} + ] + + await setup_integration(hass, legacy_config_entry) + + # Verify the config entry was migrated with CONF_AUTH_TYPE + assert legacy_config_entry.state is ConfigEntryState.LOADED + assert CONF_AUTH_TYPE in legacy_config_entry.data + assert legacy_config_entry.data[CONF_AUTH_TYPE] == AUTH_PASSWORD