Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4e3664b

Browse files
andrew-codechimpCopilot
andauthored
Move Transmission services into separate module (home-assistant#155490)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 76f5cc3 commit 4e3664b

File tree

2 files changed

+171
-156
lines changed

2 files changed

+171
-156
lines changed

‎homeassistant/components/transmission/__init__.py‎

Lines changed: 6 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@
1313
TransmissionConnectError,
1414
TransmissionError,
1515
)
16-
import voluptuous as vol
1716

18-
from homeassistant.config_entries import ConfigEntryState
1917
from homeassistant.const import (
2018
CONF_HOST,
21-
CONF_ID,
2219
CONF_NAME,
2320
CONF_PASSWORD,
2421
CONF_PATH,
@@ -27,35 +24,15 @@
2724
CONF_USERNAME,
2825
Platform,
2926
)
30-
from homeassistant.core import HomeAssistant, ServiceCall, callback
31-
from homeassistant.exceptions import (
32-
ConfigEntryAuthFailed,
33-
ConfigEntryNotReady,
34-
HomeAssistantError,
35-
)
36-
from homeassistant.helpers import (
37-
config_validation as cv,
38-
entity_registry as er,
39-
selector,
40-
)
27+
from homeassistant.core import HomeAssistant, callback
28+
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
29+
from homeassistant.helpers import config_validation as cv, entity_registry as er
4130
from homeassistant.helpers.typing import ConfigType
4231

43-
from .const import (
44-
ATTR_DELETE_DATA,
45-
ATTR_DOWNLOAD_PATH,
46-
ATTR_TORRENT,
47-
CONF_ENTRY_ID,
48-
DEFAULT_DELETE_DATA,
49-
DEFAULT_PATH,
50-
DEFAULT_SSL,
51-
DOMAIN,
52-
SERVICE_ADD_TORRENT,
53-
SERVICE_REMOVE_TORRENT,
54-
SERVICE_START_TORRENT,
55-
SERVICE_STOP_TORRENT,
56-
)
32+
from .const import DEFAULT_PATH, DEFAULT_SSL, DOMAIN
5733
from .coordinator import TransmissionConfigEntry, TransmissionDataUpdateCoordinator
5834
from .errors import AuthenticationError, CannotConnect, UnknownError
35+
from .services import async_setup_services
5936

6037
_LOGGER = logging.getLogger(__name__)
6138

@@ -76,51 +53,13 @@
7653
"Turtle Mode": "turtle_mode",
7754
}
7855

79-
SERVICE_BASE_SCHEMA = vol.Schema(
80-
{
81-
vol.Required(CONF_ENTRY_ID): selector.ConfigEntrySelector(
82-
{"integration": DOMAIN}
83-
),
84-
}
85-
)
86-
87-
SERVICE_ADD_TORRENT_SCHEMA = vol.All(
88-
SERVICE_BASE_SCHEMA.extend(
89-
{
90-
vol.Required(ATTR_TORRENT): cv.string,
91-
vol.Optional(ATTR_DOWNLOAD_PATH): cv.string,
92-
}
93-
),
94-
)
95-
96-
97-
SERVICE_REMOVE_TORRENT_SCHEMA = vol.All(
98-
SERVICE_BASE_SCHEMA.extend(
99-
{
100-
vol.Required(CONF_ID): cv.positive_int,
101-
vol.Optional(ATTR_DELETE_DATA, default=DEFAULT_DELETE_DATA): cv.boolean,
102-
}
103-
)
104-
)
105-
106-
SERVICE_START_TORRENT_SCHEMA = vol.All(
107-
SERVICE_BASE_SCHEMA.extend({vol.Required(CONF_ID): cv.positive_int}),
108-
)
109-
110-
SERVICE_STOP_TORRENT_SCHEMA = vol.All(
111-
SERVICE_BASE_SCHEMA.extend(
112-
{
113-
vol.Required(CONF_ID): cv.positive_int,
114-
}
115-
)
116-
)
11756

11857
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
11958

12059

12160
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
12261
"""Set up the Transmission component."""
123-
setup_hass_services(hass)
62+
async_setup_services(hass)
12463
return True
12564

12665

@@ -203,95 +142,6 @@ async def async_migrate_entry(
203142
return True
204143

205144

206-
def _get_coordinator_from_service_data(
207-
hass: HomeAssistant, entry_id: str
208-
) -> TransmissionDataUpdateCoordinator:
209-
"""Return coordinator for entry id."""
210-
entry: TransmissionConfigEntry | None = hass.config_entries.async_get_entry(
211-
entry_id
212-
)
213-
if entry is None or entry.state is not ConfigEntryState.LOADED:
214-
raise HomeAssistantError(f"Config entry {entry_id} is not found or not loaded")
215-
return entry.runtime_data
216-
217-
218-
def setup_hass_services(hass: HomeAssistant) -> None:
219-
"""Home Assistant services."""
220-
221-
async def add_torrent(service: ServiceCall) -> None:
222-
"""Add new torrent to download."""
223-
entry_id: str = service.data[CONF_ENTRY_ID]
224-
coordinator = _get_coordinator_from_service_data(hass, entry_id)
225-
torrent: str = service.data[ATTR_TORRENT]
226-
download_path: str | None = service.data.get(ATTR_DOWNLOAD_PATH)
227-
if torrent.startswith(
228-
("http", "ftp:", "magnet:")
229-
) or hass.config.is_allowed_path(torrent):
230-
if download_path:
231-
await hass.async_add_executor_job(
232-
partial(
233-
coordinator.api.add_torrent, torrent, download_dir=download_path
234-
)
235-
)
236-
else:
237-
await hass.async_add_executor_job(coordinator.api.add_torrent, torrent)
238-
await coordinator.async_request_refresh()
239-
else:
240-
_LOGGER.warning("Could not add torrent: unsupported type or no permission")
241-
242-
async def start_torrent(service: ServiceCall) -> None:
243-
"""Start torrent."""
244-
entry_id: str = service.data[CONF_ENTRY_ID]
245-
coordinator = _get_coordinator_from_service_data(hass, entry_id)
246-
torrent_id = service.data[CONF_ID]
247-
await hass.async_add_executor_job(coordinator.api.start_torrent, torrent_id)
248-
await coordinator.async_request_refresh()
249-
250-
async def stop_torrent(service: ServiceCall) -> None:
251-
"""Stop torrent."""
252-
entry_id: str = service.data[CONF_ENTRY_ID]
253-
coordinator = _get_coordinator_from_service_data(hass, entry_id)
254-
torrent_id = service.data[CONF_ID]
255-
await hass.async_add_executor_job(coordinator.api.stop_torrent, torrent_id)
256-
await coordinator.async_request_refresh()
257-
258-
async def remove_torrent(service: ServiceCall) -> None:
259-
"""Remove torrent."""
260-
entry_id: str = service.data[CONF_ENTRY_ID]
261-
coordinator = _get_coordinator_from_service_data(hass, entry_id)
262-
torrent_id = service.data[CONF_ID]
263-
delete_data = service.data[ATTR_DELETE_DATA]
264-
await hass.async_add_executor_job(
265-
partial(coordinator.api.remove_torrent, torrent_id, delete_data=delete_data)
266-
)
267-
await coordinator.async_request_refresh()
268-
269-
hass.services.async_register(
270-
DOMAIN, SERVICE_ADD_TORRENT, add_torrent, schema=SERVICE_ADD_TORRENT_SCHEMA
271-
)
272-
273-
hass.services.async_register(
274-
DOMAIN,
275-
SERVICE_REMOVE_TORRENT,
276-
remove_torrent,
277-
schema=SERVICE_REMOVE_TORRENT_SCHEMA,
278-
)
279-
280-
hass.services.async_register(
281-
DOMAIN,
282-
SERVICE_START_TORRENT,
283-
start_torrent,
284-
schema=SERVICE_START_TORRENT_SCHEMA,
285-
)
286-
287-
hass.services.async_register(
288-
DOMAIN,
289-
SERVICE_STOP_TORRENT,
290-
stop_torrent,
291-
schema=SERVICE_STOP_TORRENT_SCHEMA,
292-
)
293-
294-
295145
async def get_api(
296146
hass: HomeAssistant, entry: dict[str, Any]
297147
) -> transmission_rpc.Client:
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
"""Define services for the Transmission integration."""
2+
3+
from functools import partial
4+
import logging
5+
6+
import voluptuous as vol
7+
8+
from homeassistant.config_entries import ConfigEntryState
9+
from homeassistant.const import CONF_ID
10+
from homeassistant.core import HomeAssistant, ServiceCall, callback
11+
from homeassistant.exceptions import HomeAssistantError
12+
from homeassistant.helpers import config_validation as cv, selector
13+
14+
from .const import (
15+
ATTR_DELETE_DATA,
16+
ATTR_DOWNLOAD_PATH,
17+
ATTR_TORRENT,
18+
CONF_ENTRY_ID,
19+
DEFAULT_DELETE_DATA,
20+
DOMAIN,
21+
SERVICE_ADD_TORRENT,
22+
SERVICE_REMOVE_TORRENT,
23+
SERVICE_START_TORRENT,
24+
SERVICE_STOP_TORRENT,
25+
)
26+
from .coordinator import TransmissionConfigEntry, TransmissionDataUpdateCoordinator
27+
28+
_LOGGER = logging.getLogger(__name__)
29+
30+
SERVICE_BASE_SCHEMA = vol.Schema(
31+
{
32+
vol.Required(CONF_ENTRY_ID): selector.ConfigEntrySelector(
33+
{"integration": DOMAIN}
34+
),
35+
}
36+
)
37+
38+
SERVICE_ADD_TORRENT_SCHEMA = vol.All(
39+
SERVICE_BASE_SCHEMA.extend(
40+
{
41+
vol.Required(ATTR_TORRENT): cv.string,
42+
vol.Optional(ATTR_DOWNLOAD_PATH): cv.string,
43+
}
44+
),
45+
)
46+
47+
SERVICE_REMOVE_TORRENT_SCHEMA = vol.All(
48+
SERVICE_BASE_SCHEMA.extend(
49+
{
50+
vol.Required(CONF_ID): cv.positive_int,
51+
vol.Optional(ATTR_DELETE_DATA, default=DEFAULT_DELETE_DATA): cv.boolean,
52+
}
53+
)
54+
)
55+
56+
SERVICE_START_TORRENT_SCHEMA = vol.All(
57+
SERVICE_BASE_SCHEMA.extend({vol.Required(CONF_ID): cv.positive_int}),
58+
)
59+
60+
SERVICE_STOP_TORRENT_SCHEMA = vol.All(
61+
SERVICE_BASE_SCHEMA.extend(
62+
{
63+
vol.Required(CONF_ID): cv.positive_int,
64+
}
65+
)
66+
)
67+
68+
69+
def _get_coordinator_from_service_data(
70+
hass: HomeAssistant, entry_id: str
71+
) -> TransmissionDataUpdateCoordinator:
72+
"""Return coordinator for entry id."""
73+
entry: TransmissionConfigEntry | None = hass.config_entries.async_get_entry(
74+
entry_id
75+
)
76+
if entry is None or entry.state is not ConfigEntryState.LOADED:
77+
raise HomeAssistantError(f"Config entry {entry_id} is not found or not loaded")
78+
return entry.runtime_data
79+
80+
81+
async def _async_add_torrent(service: ServiceCall) -> None:
82+
"""Add new torrent to download."""
83+
entry_id: str = service.data[CONF_ENTRY_ID]
84+
coordinator = _get_coordinator_from_service_data(service.hass, entry_id)
85+
torrent: str = service.data[ATTR_TORRENT]
86+
download_path: str | None = service.data.get(ATTR_DOWNLOAD_PATH)
87+
if torrent.startswith(
88+
("http", "ftp:", "magnet:")
89+
) or service.hass.config.is_allowed_path(torrent):
90+
if download_path:
91+
await service.hass.async_add_executor_job(
92+
partial(
93+
coordinator.api.add_torrent, torrent, download_dir=download_path
94+
)
95+
)
96+
else:
97+
await service.hass.async_add_executor_job(
98+
coordinator.api.add_torrent, torrent
99+
)
100+
await coordinator.async_request_refresh()
101+
else:
102+
_LOGGER.warning("Could not add torrent: unsupported type or no permission")
103+
104+
105+
async def _async_start_torrent(service: ServiceCall) -> None:
106+
"""Start torrent."""
107+
entry_id: str = service.data[CONF_ENTRY_ID]
108+
coordinator = _get_coordinator_from_service_data(service.hass, entry_id)
109+
torrent_id = service.data[CONF_ID]
110+
await service.hass.async_add_executor_job(coordinator.api.start_torrent, torrent_id)
111+
await coordinator.async_request_refresh()
112+
113+
114+
async def _async_stop_torrent(service: ServiceCall) -> None:
115+
"""Stop torrent."""
116+
entry_id: str = service.data[CONF_ENTRY_ID]
117+
coordinator = _get_coordinator_from_service_data(service.hass, entry_id)
118+
torrent_id = service.data[CONF_ID]
119+
await service.hass.async_add_executor_job(coordinator.api.stop_torrent, torrent_id)
120+
await coordinator.async_request_refresh()
121+
122+
123+
async def _async_remove_torrent(service: ServiceCall) -> None:
124+
"""Remove torrent."""
125+
entry_id: str = service.data[CONF_ENTRY_ID]
126+
coordinator = _get_coordinator_from_service_data(service.hass, entry_id)
127+
torrent_id = service.data[CONF_ID]
128+
delete_data = service.data[ATTR_DELETE_DATA]
129+
await service.hass.async_add_executor_job(
130+
partial(coordinator.api.remove_torrent, torrent_id, delete_data=delete_data)
131+
)
132+
await coordinator.async_request_refresh()
133+
134+
135+
@callback
136+
def async_setup_services(hass: HomeAssistant) -> None:
137+
"""Register services for the Transmission integration."""
138+
139+
hass.services.async_register(
140+
DOMAIN,
141+
SERVICE_ADD_TORRENT,
142+
_async_add_torrent,
143+
schema=SERVICE_ADD_TORRENT_SCHEMA,
144+
)
145+
146+
hass.services.async_register(
147+
DOMAIN,
148+
SERVICE_REMOVE_TORRENT,
149+
_async_remove_torrent,
150+
schema=SERVICE_REMOVE_TORRENT_SCHEMA,
151+
)
152+
153+
hass.services.async_register(
154+
DOMAIN,
155+
SERVICE_START_TORRENT,
156+
_async_start_torrent,
157+
schema=SERVICE_START_TORRENT_SCHEMA,
158+
)
159+
160+
hass.services.async_register(
161+
DOMAIN,
162+
SERVICE_STOP_TORRENT,
163+
_async_stop_torrent,
164+
schema=SERVICE_STOP_TORRENT_SCHEMA,
165+
)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /