diff --git a/homeassistant/components/sonarr/services.py b/homeassistant/components/sonarr/services.py index acd0bd11e479..49e6f259f467 100644 --- a/homeassistant/components/sonarr/services.py +++ b/homeassistant/components/sonarr/services.py @@ -7,11 +7,10 @@ from typing import Any, cast from aiopyarr import exceptions import voluptuous as vol -from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_URL from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse, callback -from homeassistant.exceptions import HomeAssistantError, ServiceValidationError -from homeassistant.helpers import selector +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import selector, service from homeassistant.util import dt as dt_util from .const import ( @@ -100,24 +99,6 @@ SERVICE_GET_WANTED_SCHEMA = SERVICE_BASE_SCHEMA.extend( ) -def _get_config_entry_from_service_data(call: ServiceCall) -> SonarrConfigEntry: - """Return config entry for entry id.""" - config_entry_id: str = call.data[ATTR_ENTRY_ID] - if not (entry := call.hass.config_entries.async_get_entry(config_entry_id)): - raise ServiceValidationError( - translation_domain=DOMAIN, - translation_key="integration_not_found", - translation_placeholders={"target": config_entry_id}, - ) - if entry.state is not ConfigEntryState.LOADED: - raise ServiceValidationError( - translation_domain=DOMAIN, - translation_key="not_loaded", - translation_placeholders={"target": entry.title}, - ) - return cast(SonarrConfigEntry, entry) - - async def _handle_api_errors[_T](func: Callable[[], Awaitable[_T]]) -> _T: """Handle API errors and raise HomeAssistantError with user-friendly messages.""" try: @@ -130,9 +111,11 @@ async def _handle_api_errors[_T](func: Callable[[], Awaitable[_T]]) -> _T: raise HomeAssistantError(f"Sonarr API error: {ex}") from ex -async def _async_get_series(service: ServiceCall) -> dict[str, Any]: +async def _async_get_series(call: ServiceCall) -> dict[str, Any]: """Get all Sonarr series.""" - entry = _get_config_entry_from_service_data(service) + entry: SonarrConfigEntry = service.async_get_config_entry( + call.hass, DOMAIN, call.data[ATTR_ENTRY_ID] + ) api_client = entry.runtime_data.status.api_client series_list = await _handle_api_errors(api_client.async_get_series) @@ -143,11 +126,13 @@ async def _async_get_series(service: ServiceCall) -> dict[str, Any]: return {ATTR_SHOWS: shows} -async def _async_get_episodes(service: ServiceCall) -> dict[str, Any]: +async def _async_get_episodes(call: ServiceCall) -> dict[str, Any]: """Get episodes for a specific series.""" - entry = _get_config_entry_from_service_data(service) - series_id: int = service.data[CONF_SERIES_ID] - season_number: int | None = service.data.get(CONF_SEASON_NUMBER) + entry: SonarrConfigEntry = service.async_get_config_entry( + call.hass, DOMAIN, call.data[ATTR_ENTRY_ID] + ) + series_id: int = call.data[CONF_SERIES_ID] + season_number: int | None = call.data.get(CONF_SEASON_NUMBER) api_client = entry.runtime_data.status.api_client episodes = await _handle_api_errors( @@ -159,10 +144,12 @@ async def _async_get_episodes(service: ServiceCall) -> dict[str, Any]: return {ATTR_EPISODES: formatted_episodes} -async def _async_get_queue(service: ServiceCall) -> dict[str, Any]: +async def _async_get_queue(call: ServiceCall) -> dict[str, Any]: """Get Sonarr queue.""" - entry = _get_config_entry_from_service_data(service) - max_items: int = service.data[CONF_MAX_ITEMS] + entry: SonarrConfigEntry = service.async_get_config_entry( + call.hass, DOMAIN, call.data[ATTR_ENTRY_ID] + ) + max_items: int = call.data[CONF_MAX_ITEMS] api_client = entry.runtime_data.status.api_client # 0 means no limit - use a large page size to get all items @@ -179,10 +166,12 @@ async def _async_get_queue(service: ServiceCall) -> dict[str, Any]: return {ATTR_SHOWS: shows} -async def _async_get_diskspace(service: ServiceCall) -> dict[str, Any]: +async def _async_get_diskspace(call: ServiceCall) -> dict[str, Any]: """Get Sonarr diskspace information.""" - entry = _get_config_entry_from_service_data(service) - space_unit: str = service.data[CONF_SPACE_UNIT] + entry: SonarrConfigEntry = service.async_get_config_entry( + call.hass, DOMAIN, call.data[ATTR_ENTRY_ID] + ) + space_unit: str = call.data[CONF_SPACE_UNIT] api_client = entry.runtime_data.status.api_client disks = await _handle_api_errors(api_client.async_get_diskspace) @@ -190,10 +179,12 @@ async def _async_get_diskspace(service: ServiceCall) -> dict[str, Any]: return {ATTR_DISKS: format_diskspace(disks, space_unit)} -async def _async_get_upcoming(service: ServiceCall) -> dict[str, Any]: +async def _async_get_upcoming(call: ServiceCall) -> dict[str, Any]: """Get Sonarr upcoming episodes.""" - entry = _get_config_entry_from_service_data(service) - days: int = service.data[CONF_DAYS] + entry: SonarrConfigEntry = service.async_get_config_entry( + call.hass, DOMAIN, call.data[ATTR_ENTRY_ID] + ) + days: int = call.data[CONF_DAYS] api_client = entry.runtime_data.status.api_client @@ -213,10 +204,12 @@ async def _async_get_upcoming(service: ServiceCall) -> dict[str, Any]: return {ATTR_EPISODES: episodes} -async def _async_get_wanted(service: ServiceCall) -> dict[str, Any]: +async def _async_get_wanted(call: ServiceCall) -> dict[str, Any]: """Get Sonarr wanted/missing episodes.""" - entry = _get_config_entry_from_service_data(service) - max_items: int = service.data[CONF_MAX_ITEMS] + entry: SonarrConfigEntry = service.async_get_config_entry( + call.hass, DOMAIN, call.data[ATTR_ENTRY_ID] + ) + max_items: int = call.data[CONF_MAX_ITEMS] api_client = entry.runtime_data.status.api_client # 0 means no limit - use a large page size to get all items diff --git a/homeassistant/components/sonarr/strings.json b/homeassistant/components/sonarr/strings.json index ef2c63883543..b7c30ff6899b 100644 --- a/homeassistant/components/sonarr/strings.json +++ b/homeassistant/components/sonarr/strings.json @@ -58,14 +58,6 @@ } } }, - "exceptions": { - "integration_not_found": { - "message": "Config entry for integration \"{target}\" not found." - }, - "not_loaded": { - "message": "Config entry \"{target}\" is not loaded." - } - }, "options": { "step": { "init": { diff --git a/tests/components/sonarr/test_services.py b/tests/components/sonarr/test_services.py index 80fa6f2c6686..c323274ed2fa 100644 --- a/tests/components/sonarr/test_services.py +++ b/tests/components/sonarr/test_services.py @@ -66,7 +66,7 @@ async def test_services_config_entry_not_loaded_state( return_response=True, ) - assert exc_info.value.translation_key == "not_loaded" + assert exc_info.value.translation_key == "service_config_entry_not_loaded" @pytest.mark.parametrize( @@ -94,7 +94,7 @@ async def test_services_integration_not_found( return_response=True, ) - assert exc_info.value.translation_key == "integration_not_found" + assert exc_info.value.translation_key == "service_config_entry_not_found" async def test_service_get_series( @@ -168,7 +168,7 @@ async def test_services_entry_not_loaded( return_response=True, ) - assert exc_info.value.translation_key == "not_loaded" + assert exc_info.value.translation_key == "service_config_entry_not_loaded" async def test_service_get_queue_empty(