mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Deprecate name key in Transmission
services (#78577)
* Use entry_id instead of name in Transmission services * fix return type for _get_client * address comments * combine conditions
This commit is contained in:
parent
d9d6902803
commit
06773efcbd
@ -3,12 +3,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import transmissionrpc
|
import transmissionrpc
|
||||||
from transmissionrpc.error import TransmissionError
|
from transmissionrpc.error import TransmissionError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
@ -21,13 +22,15 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall
|
from homeassistant.core import HomeAssistant, ServiceCall
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv, selector
|
||||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_DELETE_DATA,
|
ATTR_DELETE_DATA,
|
||||||
ATTR_TORRENT,
|
ATTR_TORRENT,
|
||||||
|
CONF_ENTRY_ID,
|
||||||
CONF_LIMIT,
|
CONF_LIMIT,
|
||||||
CONF_ORDER,
|
CONF_ORDER,
|
||||||
DATA_UPDATED,
|
DATA_UPDATED,
|
||||||
@ -49,30 +52,41 @@ from .errors import AuthenticationError, CannotConnect, UnknownError
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
SERVICE_ADD_TORRENT_SCHEMA = vol.Schema(
|
SERVICE_BASE_SCHEMA = vol.Schema(
|
||||||
{vol.Required(ATTR_TORRENT): cv.string, vol.Required(CONF_NAME): cv.string}
|
{
|
||||||
|
vol.Exclusive(CONF_ENTRY_ID, "identifier"): selector.ConfigEntrySelector(),
|
||||||
|
vol.Exclusive(CONF_NAME, "identifier"): selector.TextSelector(),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
SERVICE_REMOVE_TORRENT_SCHEMA = vol.Schema(
|
SERVICE_ADD_TORRENT_SCHEMA = vol.All(
|
||||||
|
SERVICE_BASE_SCHEMA.extend({vol.Required(ATTR_TORRENT): cv.string}),
|
||||||
|
cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
SERVICE_REMOVE_TORRENT_SCHEMA = vol.All(
|
||||||
|
SERVICE_BASE_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_NAME): cv.string,
|
|
||||||
vol.Required(CONF_ID): cv.positive_int,
|
vol.Required(CONF_ID): cv.positive_int,
|
||||||
vol.Optional(ATTR_DELETE_DATA, default=DEFAULT_DELETE_DATA): cv.boolean,
|
vol.Optional(ATTR_DELETE_DATA, default=DEFAULT_DELETE_DATA): cv.boolean,
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME),
|
||||||
)
|
)
|
||||||
|
|
||||||
SERVICE_START_TORRENT_SCHEMA = vol.Schema(
|
SERVICE_START_TORRENT_SCHEMA = vol.All(
|
||||||
{
|
SERVICE_BASE_SCHEMA.extend({vol.Required(CONF_ID): cv.positive_int}),
|
||||||
vol.Required(CONF_NAME): cv.string,
|
cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME),
|
||||||
vol.Required(CONF_ID): cv.positive_int,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SERVICE_STOP_TORRENT_SCHEMA = vol.Schema(
|
SERVICE_STOP_TORRENT_SCHEMA = vol.All(
|
||||||
|
SERVICE_BASE_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_NAME): cv.string,
|
|
||||||
vol.Required(CONF_ID): cv.positive_int,
|
vol.Required(CONF_ID): cv.positive_int,
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME),
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||||
@ -135,6 +149,39 @@ async def get_api(hass, entry):
|
|||||||
raise UnknownError from error
|
raise UnknownError from error
|
||||||
|
|
||||||
|
|
||||||
|
def _get_client(hass: HomeAssistant, data: dict[str, Any]) -> TransmissionClient | None:
|
||||||
|
"""Return client from integration name or entry_id."""
|
||||||
|
if (
|
||||||
|
(entry_id := data.get(CONF_ENTRY_ID))
|
||||||
|
and (entry := hass.config_entries.async_get_entry(entry_id))
|
||||||
|
and entry.state == ConfigEntryState.LOADED
|
||||||
|
):
|
||||||
|
return hass.data[DOMAIN][entry_id]
|
||||||
|
|
||||||
|
# to be removed once name key is removed
|
||||||
|
if CONF_NAME in data:
|
||||||
|
create_issue(
|
||||||
|
hass,
|
||||||
|
DOMAIN,
|
||||||
|
"deprecated_key",
|
||||||
|
breaks_in_ha_version="2023.1.0",
|
||||||
|
is_fixable=True,
|
||||||
|
is_persistent=True,
|
||||||
|
severity=IssueSeverity.WARNING,
|
||||||
|
translation_key="deprecated_key",
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER.warning(
|
||||||
|
'The "name" key in the Transmission services is deprecated and will be removed in "2023.1.0"; '
|
||||||
|
'use the "entry_id" key instead to identity which entry to call'
|
||||||
|
)
|
||||||
|
for entry in hass.config_entries.async_entries(DOMAIN):
|
||||||
|
if entry.data[CONF_NAME] == data[CONF_NAME]:
|
||||||
|
return hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class TransmissionClient:
|
class TransmissionClient:
|
||||||
"""Transmission Client Object."""
|
"""Transmission Client Object."""
|
||||||
|
|
||||||
@ -174,14 +221,9 @@ class TransmissionClient:
|
|||||||
|
|
||||||
def add_torrent(service: ServiceCall) -> None:
|
def add_torrent(service: ServiceCall) -> None:
|
||||||
"""Add new torrent to download."""
|
"""Add new torrent to download."""
|
||||||
tm_client = None
|
if not (tm_client := _get_client(self.hass, service.data)):
|
||||||
for entry in self.hass.config_entries.async_entries(DOMAIN):
|
raise ValueError("Transmission instance is not found")
|
||||||
if entry.data[CONF_NAME] == service.data[CONF_NAME]:
|
|
||||||
tm_client = self.hass.data[DOMAIN][entry.entry_id]
|
|
||||||
break
|
|
||||||
if tm_client is None:
|
|
||||||
_LOGGER.error("Transmission instance is not found")
|
|
||||||
return
|
|
||||||
torrent = service.data[ATTR_TORRENT]
|
torrent = service.data[ATTR_TORRENT]
|
||||||
if torrent.startswith(
|
if torrent.startswith(
|
||||||
("http", "ftp:", "magnet:")
|
("http", "ftp:", "magnet:")
|
||||||
@ -195,42 +237,27 @@ class TransmissionClient:
|
|||||||
|
|
||||||
def start_torrent(service: ServiceCall) -> None:
|
def start_torrent(service: ServiceCall) -> None:
|
||||||
"""Start torrent."""
|
"""Start torrent."""
|
||||||
tm_client = None
|
if not (tm_client := _get_client(self.hass, service.data)):
|
||||||
for entry in self.hass.config_entries.async_entries(DOMAIN):
|
raise ValueError("Transmission instance is not found")
|
||||||
if entry.data[CONF_NAME] == service.data[CONF_NAME]:
|
|
||||||
tm_client = self.hass.data[DOMAIN][entry.entry_id]
|
|
||||||
break
|
|
||||||
if tm_client is None:
|
|
||||||
_LOGGER.error("Transmission instance is not found")
|
|
||||||
return
|
|
||||||
torrent_id = service.data[CONF_ID]
|
torrent_id = service.data[CONF_ID]
|
||||||
tm_client.tm_api.start_torrent(torrent_id)
|
tm_client.tm_api.start_torrent(torrent_id)
|
||||||
tm_client.api.update()
|
tm_client.api.update()
|
||||||
|
|
||||||
def stop_torrent(service: ServiceCall) -> None:
|
def stop_torrent(service: ServiceCall) -> None:
|
||||||
"""Stop torrent."""
|
"""Stop torrent."""
|
||||||
tm_client = None
|
if not (tm_client := _get_client(self.hass, service.data)):
|
||||||
for entry in self.hass.config_entries.async_entries(DOMAIN):
|
raise ValueError("Transmission instance is not found")
|
||||||
if entry.data[CONF_NAME] == service.data[CONF_NAME]:
|
|
||||||
tm_client = self.hass.data[DOMAIN][entry.entry_id]
|
|
||||||
break
|
|
||||||
if tm_client is None:
|
|
||||||
_LOGGER.error("Transmission instance is not found")
|
|
||||||
return
|
|
||||||
torrent_id = service.data[CONF_ID]
|
torrent_id = service.data[CONF_ID]
|
||||||
tm_client.tm_api.stop_torrent(torrent_id)
|
tm_client.tm_api.stop_torrent(torrent_id)
|
||||||
tm_client.api.update()
|
tm_client.api.update()
|
||||||
|
|
||||||
def remove_torrent(service: ServiceCall) -> None:
|
def remove_torrent(service: ServiceCall) -> None:
|
||||||
"""Remove torrent."""
|
"""Remove torrent."""
|
||||||
tm_client = None
|
if not (tm_client := _get_client(self.hass, service.data)):
|
||||||
for entry in self.hass.config_entries.async_entries(DOMAIN):
|
raise ValueError("Transmission instance is not found")
|
||||||
if entry.data[CONF_NAME] == service.data[CONF_NAME]:
|
|
||||||
tm_client = self.hass.data[DOMAIN][entry.entry_id]
|
|
||||||
break
|
|
||||||
if tm_client is None:
|
|
||||||
_LOGGER.error("Transmission instance is not found")
|
|
||||||
return
|
|
||||||
torrent_id = service.data[CONF_ID]
|
torrent_id = service.data[CONF_ID]
|
||||||
delete_data = service.data[ATTR_DELETE_DATA]
|
delete_data = service.data[ATTR_DELETE_DATA]
|
||||||
tm_client.tm_api.remove_torrent(torrent_id, delete_data=delete_data)
|
tm_client.tm_api.remove_torrent(torrent_id, delete_data=delete_data)
|
||||||
|
@ -18,7 +18,7 @@ SUPPORTED_ORDER_MODES = {
|
|||||||
torrents, key=lambda t: t.ratio, reverse=True
|
torrents, key=lambda t: t.ratio, reverse=True
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
CONF_ENTRY_ID = "entry_id"
|
||||||
CONF_LIMIT = "limit"
|
CONF_LIMIT = "limit"
|
||||||
CONF_ORDER = "order"
|
CONF_ORDER = "order"
|
||||||
|
|
||||||
|
@ -2,10 +2,15 @@ add_torrent:
|
|||||||
name: Add torrent
|
name: Add torrent
|
||||||
description: Add a new torrent to download (URL, magnet link or Base64 encoded).
|
description: Add a new torrent to download (URL, magnet link or Base64 encoded).
|
||||||
fields:
|
fields:
|
||||||
|
entry_id:
|
||||||
|
name: Transmission entry
|
||||||
|
description: Config entry id
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: transmission
|
||||||
name:
|
name:
|
||||||
name: Name
|
name: Name
|
||||||
description: Instance name as entered during entry config
|
description: Instance name as entered during entry config
|
||||||
required: true
|
|
||||||
example: Transmission
|
example: Transmission
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
@ -21,10 +26,15 @@ remove_torrent:
|
|||||||
name: Remove torrent
|
name: Remove torrent
|
||||||
description: Remove a torrent
|
description: Remove a torrent
|
||||||
fields:
|
fields:
|
||||||
|
entry_id:
|
||||||
|
name: Transmission entry
|
||||||
|
description: Config entry id
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: transmission
|
||||||
name:
|
name:
|
||||||
name: Name
|
name: Name
|
||||||
description: Instance name as entered during entry config
|
description: Instance name as entered during entry config
|
||||||
required: true
|
|
||||||
example: Transmission
|
example: Transmission
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
@ -46,6 +56,12 @@ start_torrent:
|
|||||||
name: Start torrent
|
name: Start torrent
|
||||||
description: Start a torrent
|
description: Start a torrent
|
||||||
fields:
|
fields:
|
||||||
|
entry_id:
|
||||||
|
name: Transmission entry
|
||||||
|
description: Config entry id
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: transmission
|
||||||
name:
|
name:
|
||||||
name: Name
|
name: Name
|
||||||
description: Instance name as entered during entry config
|
description: Instance name as entered during entry config
|
||||||
@ -63,10 +79,15 @@ stop_torrent:
|
|||||||
name: Stop torrent
|
name: Stop torrent
|
||||||
description: Stop a torrent
|
description: Stop a torrent
|
||||||
fields:
|
fields:
|
||||||
|
entry_id:
|
||||||
|
name: Transmission entry
|
||||||
|
description: Config entry id
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: transmission
|
||||||
name:
|
name:
|
||||||
name: Name
|
name: Name
|
||||||
description: Instance name as entered during entry config
|
description: Instance name as entered during entry config
|
||||||
required: true
|
|
||||||
example: Transmission
|
example: Transmission
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
@ -40,5 +40,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"deprecated_key": {
|
||||||
|
"title": "The name key in Transmission services is being removed",
|
||||||
|
"fix_flow": {
|
||||||
|
"step": {
|
||||||
|
"confirm": {
|
||||||
|
"title": "The name key in Transmission services is being removed",
|
||||||
|
"description": "Update any automations or scripts that use this service and replace the name key with the entry_id key."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"issues": {
|
||||||
|
"deprecated_key": {
|
||||||
|
"fix_flow": {
|
||||||
|
"step": {
|
||||||
|
"confirm": {
|
||||||
|
"description": "Update any automations or scripts that use this service and replace the name key with the entry_id key.",
|
||||||
|
"title": "The name key in Transmission services is being removed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "The name key in Transmission services is being removed"
|
||||||
|
}
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user