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:
Rami Mosleh 2022-10-30 13:00:47 +02:00 committed by GitHub
parent d9d6902803
commit 06773efcbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 56 deletions

View File

@ -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)

View File

@ -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"

View File

@ -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:

View File

@ -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."
}
}
}
}
} }
} }

View File

@ -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": {