diff --git a/homeassistant/components/vicare/__init__.py b/homeassistant/components/vicare/__init__.py index eedeffea9378..295e8ab734f1 100644 --- a/homeassistant/components/vicare/__init__.py +++ b/homeassistant/components/vicare/__init__.py @@ -11,6 +11,7 @@ from PyViCare.PyViCareOAuthManager import obtain_token_via_basic_auth_pkce from PyViCare.PyViCareUtils import ( PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError, + PyViCareRateLimitError, ) from homeassistant.components.application_credentials import ( @@ -169,6 +170,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ViCareConfigEntry) -> bo PyViCareInvalidCredentialsError, ) as err: raise ConfigEntryAuthFailed("Authentication failed") from err + except PyViCareRateLimitError as err: + # The quota recovers on its own. + raise ConfigEntryNotReady( + f"ViCare API rate limit exceeded, resets at {err.limitResetDate}" + ) from err device_count = len(entry.runtime_data.devices) coordinators: list[ViCareCoordinator] = [] diff --git a/tests/components/vicare/test_init.py b/tests/components/vicare/test_init.py index 10f370691e50..e938a5906352 100644 --- a/tests/components/vicare/test_init.py +++ b/tests/components/vicare/test_init.py @@ -11,6 +11,7 @@ from PyViCare.PyViCareUtils import ( PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError, PyViCareInvalidDataError, + PyViCareRateLimitError, ) from homeassistant.components.vicare.const import DOMAIN @@ -329,6 +330,42 @@ async def test_setup_entry_invalid_credentials( assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR +async def test_setup_entry_rate_limited( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, +) -> None: + """Test setup retries when the daily API quota is spent.""" + mock_config_entry.add_to_hass(hass) + + rate_limit_error = PyViCareRateLimitError( + { + "extendedPayload": { + "name": "development portal", + "requestCountLimit": 1450, + "limitReset": 1757376004000, + } + } + ) + + with ( + patch( + "homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid", + ), + patch( + f"{MODULE}._setup_vicare_api", + side_effect=rate_limit_error, + ) as setup_api, + ): + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + setup_api.assert_called_once() + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + # SETUP_RETRY alone would also match an unrelated ConfigEntryNotReady. + assert "rate limit" in mock_config_entry.reason + assert str(rate_limit_error.limitResetDate) in mock_config_entry.reason + + async def test_setup_entry_invalid_configuration( hass: HomeAssistant, mock_config_entry: MockConfigEntry,