Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for monitoring an SABnzbd NZB client."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable, Coroutine
6 import logging
7 from typing import Any
8 
9 import voluptuous as vol
10 
11 from homeassistant.config_entries import ConfigEntry, ConfigEntryState
12 from homeassistant.const import Platform
13 from homeassistant.core import HomeAssistant, ServiceCall, callback
14 from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
15 from homeassistant.helpers import config_validation as cv
17 
18 from .const import (
19  ATTR_API_KEY,
20  ATTR_SPEED,
21  DEFAULT_SPEED_LIMIT,
22  DOMAIN,
23  SERVICE_PAUSE,
24  SERVICE_RESUME,
25  SERVICE_SET_SPEED,
26 )
27 from .coordinator import SabnzbdUpdateCoordinator
28 from .helpers import get_client
29 
30 PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.NUMBER, Platform.SENSOR]
31 _LOGGER = logging.getLogger(__name__)
32 
33 SERVICES = (
34  SERVICE_PAUSE,
35  SERVICE_RESUME,
36  SERVICE_SET_SPEED,
37 )
38 
39 SERVICE_BASE_SCHEMA = vol.Schema(
40  {
41  vol.Required(ATTR_API_KEY): cv.string,
42  }
43 )
44 
45 SERVICE_SPEED_SCHEMA = SERVICE_BASE_SCHEMA.extend(
46  {
47  vol.Optional(ATTR_SPEED, default=DEFAULT_SPEED_LIMIT): cv.string,
48  }
49 )
50 
51 type SabnzbdConfigEntry = ConfigEntry[SabnzbdUpdateCoordinator]
52 
53 
54 @callback
56  hass: HomeAssistant, call: ServiceCall
57 ) -> SabnzbdConfigEntry:
58  """Get the entry ID related to a service call (by device ID)."""
59  call_data_api_key = call.data[ATTR_API_KEY]
60 
61  for entry in hass.config_entries.async_entries(DOMAIN):
62  if entry.data[ATTR_API_KEY] == call_data_api_key:
63  return entry
64 
65  raise ValueError(f"No api for API key: {call_data_api_key}")
66 
67 
68 async def async_setup_entry(hass: HomeAssistant, entry: SabnzbdConfigEntry) -> bool:
69  """Set up the SabNzbd Component."""
70 
71  sab_api = await get_client(hass, entry.data)
72  if not sab_api:
73  raise ConfigEntryNotReady
74 
75  coordinator = SabnzbdUpdateCoordinator(hass, entry, sab_api)
76  await coordinator.async_config_entry_first_refresh()
77  entry.runtime_data = coordinator
78 
79  @callback
80  def extract_api(
81  func: Callable[
82  [ServiceCall, SabnzbdUpdateCoordinator], Coroutine[Any, Any, None]
83  ],
84  ) -> Callable[[ServiceCall], Coroutine[Any, Any, None]]:
85  """Define a decorator to get the correct api for a service call."""
86 
87  async def wrapper(call: ServiceCall) -> None:
88  """Wrap the service function."""
89  config_entry = async_get_entry_for_service_call(hass, call)
90  coordinator = config_entry.runtime_data
91 
92  try:
93  await func(call, coordinator)
94  except Exception as err:
95  raise HomeAssistantError(
96  f"Error while executing {func.__name__}: {err}"
97  ) from err
98 
99  return wrapper
100 
101  @extract_api
102  async def async_pause_queue(
103  call: ServiceCall, coordinator: SabnzbdUpdateCoordinator
104  ) -> None:
105  ir.async_create_issue(
106  hass,
107  DOMAIN,
108  "pause_action_deprecated",
109  is_fixable=False,
110  severity=ir.IssueSeverity.WARNING,
111  breaks_in_ha_version="2025.6",
112  translation_key="pause_action_deprecated",
113  )
114  await coordinator.sab_api.pause_queue()
115 
116  @extract_api
117  async def async_resume_queue(
118  call: ServiceCall, coordinator: SabnzbdUpdateCoordinator
119  ) -> None:
120  ir.async_create_issue(
121  hass,
122  DOMAIN,
123  "resume_action_deprecated",
124  is_fixable=False,
125  severity=ir.IssueSeverity.WARNING,
126  breaks_in_ha_version="2025.6",
127  translation_key="resume_action_deprecated",
128  )
129  await coordinator.sab_api.resume_queue()
130 
131  @extract_api
132  async def async_set_queue_speed(
133  call: ServiceCall, coordinator: SabnzbdUpdateCoordinator
134  ) -> None:
135  ir.async_create_issue(
136  hass,
137  DOMAIN,
138  "set_speed_action_deprecated",
139  is_fixable=False,
140  severity=ir.IssueSeverity.WARNING,
141  breaks_in_ha_version="2025.6",
142  translation_key="set_speed_action_deprecated",
143  )
144  speed = call.data.get(ATTR_SPEED)
145  await coordinator.sab_api.set_speed_limit(speed)
146 
147  for service, method, schema in (
148  (SERVICE_PAUSE, async_pause_queue, SERVICE_BASE_SCHEMA),
149  (SERVICE_RESUME, async_resume_queue, SERVICE_BASE_SCHEMA),
150  (SERVICE_SET_SPEED, async_set_queue_speed, SERVICE_SPEED_SCHEMA),
151  ):
152  if hass.services.has_service(DOMAIN, service):
153  continue
154 
155  hass.services.async_register(DOMAIN, service, method, schema=schema)
156 
157  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
158 
159  return True
160 
161 
162 async def async_unload_entry(hass: HomeAssistant, entry: SabnzbdConfigEntry) -> bool:
163  """Unload a Sabnzbd config entry."""
164  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
165 
166  loaded_entries = [
167  entry
168  for entry in hass.config_entries.async_entries(DOMAIN)
169  if entry.state == ConfigEntryState.LOADED
170  ]
171  if len(loaded_entries) == 1:
172  # If this is the last loaded instance of Sabnzbd, deregister any services
173  # defined during integration setup:
174  for service_name in SERVICES:
175  hass.services.async_remove(DOMAIN, service_name)
176 
177  return unload_ok
def get_client(HomeAssistant hass, data)
Definition: helpers.py:10
bool async_setup_entry(HomeAssistant hass, SabnzbdConfigEntry entry)
Definition: __init__.py:68
SabnzbdConfigEntry async_get_entry_for_service_call(HomeAssistant hass, ServiceCall call)
Definition: __init__.py:57
bool async_unload_entry(HomeAssistant hass, SabnzbdConfigEntry entry)
Definition: __init__.py:162