From c22823ff8d9e09e35c23c8bfc56f05b404c2a34a Mon Sep 17 00:00:00 2001 From: johanzander Date: Tue, 2 Jun 2026 04:54:27 +0200 Subject: [PATCH] Use growattServer library error code constants in growatt_server (#172771) --- homeassistant/components/growatt_server/__init__.py | 13 ++++++------- .../components/growatt_server/config_flow.py | 6 +++--- homeassistant/components/growatt_server/const.py | 7 ------- .../components/growatt_server/coordinator.py | 10 +++++----- tests/components/growatt_server/test_config_flow.py | 7 +++---- tests/components/growatt_server/test_init.py | 7 +++---- 6 files changed, 20 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/growatt_server/__init__.py b/homeassistant/components/growatt_server/__init__.py index c160b253fb93..58601f34e2d0 100644 --- a/homeassistant/components/growatt_server/__init__.py +++ b/homeassistant/components/growatt_server/__init__.py @@ -10,8 +10,8 @@ Classic API (username/password): Open API V1 (API token): - Stateless — no login call, token is sent as a Bearer header on every request. -- Auth failure is signalled by raising GrowattV1ApiError with error_code=10011 - (V1_API_ERROR_NO_PRIVILEGE). The library NEVER returns a failure silently; +- Auth failure is signalled by raising GrowattV1ApiError with + error_code=GrowattV1ApiErrorCode.NO_PRIVILEGE. The library NEVER returns a failure silently; any non-zero error_code raises an exception via _process_response(). - Because the library always raises on error, return-value validation after a successful V1 API call is unnecessary — if it returned, the token was valid. @@ -19,7 +19,7 @@ Open API V1 (API token): Error handling pattern for reauth: - Classic API: check NOT login_response["success"] and msg == LOGIN_INVALID_AUTH_CODE → raise ConfigEntryAuthFailed -- V1 API: catch GrowattV1ApiError with error_code == V1_API_ERROR_NO_PRIVILEGE +- V1 API: catch GrowattV1ApiError with error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE → raise ConfigEntryAuthFailed - All other errors → ConfigEntryError (setup) or UpdateFailed (coordinator) """ @@ -30,6 +30,7 @@ from json import JSONDecodeError import logging import growattServer +from growattServer import GrowattV1ApiErrorCode from requests import RequestException from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_URL, CONF_USERNAME @@ -58,8 +59,6 @@ from .const import ( LOGIN_INVALID_AUTH_CODE, PLATFORMS, SUPPORTED_DEVICE_TYPES, - V1_API_ERROR_NO_PRIVILEGE, - V1_API_ERROR_RATE_LIMITED, V1_DEVICE_TYPES, ) from .coordinator import GrowattConfigEntry, GrowattCoordinator @@ -265,11 +264,11 @@ def get_device_list_v1( try: devices_dict = api.device_list(plant_id) except growattServer.GrowattV1ApiError as e: - if e.error_code == V1_API_ERROR_NO_PRIVILEGE: + if e.error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE: raise ConfigEntryAuthFailed( f"Authentication failed for Growatt API: {e.error_msg or str(e)}" ) from e - if e.error_code == V1_API_ERROR_RATE_LIMITED: + if e.error_code == GrowattV1ApiErrorCode.RATE_LIMITED: raise ConfigEntryNotReady( f"Growatt API rate limited, will retry: {e.error_msg or str(e)}" ) from e diff --git a/homeassistant/components/growatt_server/config_flow.py b/homeassistant/components/growatt_server/config_flow.py index f2f937a20c21..73ac3ea51a57 100644 --- a/homeassistant/components/growatt_server/config_flow.py +++ b/homeassistant/components/growatt_server/config_flow.py @@ -5,6 +5,7 @@ import logging from typing import Any import growattServer +from growattServer import GrowattV1ApiErrorCode import requests import voluptuous as vol @@ -32,7 +33,6 @@ from .const import ( ERROR_INVALID_AUTH, LOGIN_INVALID_AUTH_CODE, SERVER_URLS_NAMES, - V1_API_ERROR_NO_PRIVILEGE, ) _URL_TO_REGION = {v: k for k, v in SERVER_URLS_NAMES.items()} @@ -148,7 +148,7 @@ class GrowattServerConfigFlow(ConfigFlow, domain=DOMAIN): _LOGGER.debug("Network error during credential update: %s", ex) errors["base"] = ERROR_CANNOT_CONNECT except growattServer.GrowattV1ApiError as err: - if err.error_code == V1_API_ERROR_NO_PRIVILEGE: + if err.error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE: errors["base"] = ERROR_INVALID_AUTH else: _LOGGER.debug( @@ -301,7 +301,7 @@ class GrowattServerConfigFlow(ConfigFlow, domain=DOMAIN): e.error_msg or str(e), e.error_code, ) - if e.error_code == V1_API_ERROR_NO_PRIVILEGE: + if e.error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE: return self._async_show_token_form({"base": ERROR_INVALID_AUTH}) return self._async_show_token_form({"base": ERROR_CANNOT_CONNECT}) except (ValueError, KeyError, TypeError, AttributeError) as ex: diff --git a/homeassistant/components/growatt_server/const.py b/homeassistant/components/growatt_server/const.py index 81c400f24076..508c3fdef45c 100644 --- a/homeassistant/components/growatt_server/const.py +++ b/homeassistant/components/growatt_server/const.py @@ -42,13 +42,6 @@ PLATFORMS = [Platform.NUMBER, Platform.SENSOR, Platform.SWITCH] # Growatt Classic API error codes LOGIN_INVALID_AUTH_CODE = "502" -# Growatt Open API V1 error codes -# Reference: https://www.showdoc.com.cn/262556420217021/1494055648380019 -V1_API_ERROR_WRONG_DOMAIN = -1 # Use correct regional domain -V1_API_ERROR_NO_PRIVILEGE = 10011 # No privilege access — invalid or expired token -V1_API_ERROR_RATE_LIMITED = 10012 # Access frequency limit (5 minutes per call) -V1_API_ERROR_PAGE_SIZE = 10013 # Page size cannot exceed 100 -V1_API_ERROR_PAGE_COUNT = 10014 # Page count cannot exceed 250 # Config flow error types (also used as abort reasons) ERROR_CANNOT_CONNECT = "cannot_connect" # Used for both form errors and aborts diff --git a/homeassistant/components/growatt_server/coordinator.py b/homeassistant/components/growatt_server/coordinator.py index 513f17d9ebee..a078eb648e41 100644 --- a/homeassistant/components/growatt_server/coordinator.py +++ b/homeassistant/components/growatt_server/coordinator.py @@ -6,6 +6,7 @@ import logging from typing import TYPE_CHECKING, Any import growattServer +from growattServer import GrowattV1ApiErrorCode from requests import RequestException from homeassistant.components.sensor import SensorStateClass @@ -27,7 +28,6 @@ from .const import ( DEFAULT_URL, DOMAIN, LOGIN_INVALID_AUTH_CODE, - V1_API_ERROR_NO_PRIVILEGE, V1_DEVICE_TYPES, ) from .models import GrowattRuntimeData @@ -113,7 +113,7 @@ class GrowattCoordinator(DataUpdateCoordinator[dict[str, Any]]): if device.get("type") in V1_DEVICE_TYPES ] except growattServer.GrowattV1ApiError as err: - if err.error_code == V1_API_ERROR_NO_PRIVILEGE: + if err.error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE: raise ConfigEntryAuthFailed( f"Authentication failed for Growatt API: {err.error_msg or str(err)}" ) from err @@ -179,7 +179,7 @@ class GrowattCoordinator(DataUpdateCoordinator[dict[str, Any]]): try: total_info = self.api.plant_energy_overview(self.plant_id) except growattServer.GrowattV1ApiError as err: - if err.error_code == V1_API_ERROR_NO_PRIVILEGE: + if err.error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE: raise ConfigEntryAuthFailed( "Authentication failed for Growatt API:" f" {err.error_msg or str(err)}" @@ -212,7 +212,7 @@ class GrowattCoordinator(DataUpdateCoordinator[dict[str, Any]]): min_settings = self.api.min_settings(self.device_id) min_energy = self.api.min_energy(self.device_id) except growattServer.GrowattV1ApiError as err: - if err.error_code == V1_API_ERROR_NO_PRIVILEGE: + if err.error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE: raise ConfigEntryAuthFailed( "Authentication failed for Growatt API:" f" {err.error_msg or str(err)}" @@ -240,7 +240,7 @@ class GrowattCoordinator(DataUpdateCoordinator[dict[str, Any]]): sph_detail = self.api.sph_detail(self.device_id) sph_energy = self.api.sph_energy(self.device_id) except growattServer.GrowattV1ApiError as err: - if err.error_code == V1_API_ERROR_NO_PRIVILEGE: + if err.error_code == GrowattV1ApiErrorCode.NO_PRIVILEGE: raise ConfigEntryAuthFailed( "Authentication failed for Growatt API:" f" {err.error_msg or str(err)}" diff --git a/tests/components/growatt_server/test_config_flow.py b/tests/components/growatt_server/test_config_flow.py index f22655564370..4bbe7067dadf 100644 --- a/tests/components/growatt_server/test_config_flow.py +++ b/tests/components/growatt_server/test_config_flow.py @@ -6,6 +6,7 @@ from typing import Any from unittest.mock import MagicMock import growattServer +from growattServer import GrowattV1ApiErrorCode import pytest import requests import voluptuous as vol @@ -23,8 +24,6 @@ from homeassistant.components.growatt_server.const import ( ERROR_INVALID_AUTH, LOGIN_INVALID_AUTH_CODE, SERVER_URLS_NAMES, - V1_API_ERROR_NO_PRIVILEGE, - V1_API_ERROR_RATE_LIMITED, ) from homeassistant.const import ( CONF_NAME, @@ -355,8 +354,8 @@ async def test_password_auth_multiple_plants( @pytest.mark.parametrize( ("error_code", "expected_error"), [ - (V1_API_ERROR_NO_PRIVILEGE, ERROR_INVALID_AUTH), - (V1_API_ERROR_RATE_LIMITED, ERROR_CANNOT_CONNECT), + (GrowattV1ApiErrorCode.NO_PRIVILEGE, ERROR_INVALID_AUTH), + (GrowattV1ApiErrorCode.RATE_LIMITED, ERROR_CANNOT_CONNECT), ], ) @pytest.mark.usefixtures("mock_setup_entry") diff --git a/tests/components/growatt_server/test_init.py b/tests/components/growatt_server/test_init.py index d81d74d1f5eb..9a30912f84df 100644 --- a/tests/components/growatt_server/test_init.py +++ b/tests/components/growatt_server/test_init.py @@ -5,6 +5,7 @@ import json from freezegun.api import FrozenDateTimeFactory import growattServer +from growattServer import GrowattV1ApiErrorCode import pytest import requests from syrupy.assertion import SnapshotAssertion @@ -19,8 +20,6 @@ from homeassistant.components.growatt_server.const import ( DEVICE_SCAN_INTERVAL, DOMAIN, LOGIN_INVALID_AUTH_CODE, - V1_API_ERROR_RATE_LIMITED, - V1_API_ERROR_WRONG_DOMAIN, ) from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( @@ -70,7 +69,7 @@ async def test_device_info( ( growattServer.GrowattV1ApiError( message="API Error", - error_code=V1_API_ERROR_WRONG_DOMAIN, + error_code=GrowattV1ApiErrorCode.WRONG_DOMAIN, error_msg="Invalid JSON", ), ConfigEntryState.SETUP_ERROR, @@ -82,7 +81,7 @@ async def test_device_info( ( growattServer.GrowattV1ApiError( message="Rate limited", - error_code=V1_API_ERROR_RATE_LIMITED, + error_code=GrowattV1ApiErrorCode.RATE_LIMITED, error_msg="Access frequency limit", ), ConfigEntryState.SETUP_RETRY,