Use service helper in Sonarr (#174273)

This commit is contained in:
some-random-climber
2026-06-22 19:37:28 +02:00
committed by GitHub
parent b2aa1c1b8d
commit 28294dfdfc
3 changed files with 35 additions and 50 deletions
+32 -39
View File
@@ -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
@@ -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": {
+3 -3
View File
@@ -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(