mirror of
https://github.com/home-assistant/core.git
synced 2026-09-07 22:20:07 +01:00
Use growattServer library error code constants in growatt_server (#172771)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)}"
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user