mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
Add host field to add_torrent service (#28653)
* Add host field to add_torrent service * Code cleanup * use name instead of host for service * update add_torrent
This commit is contained in:
parent
f8a36499c1
commit
a5960830d7
@ -1,42 +1,34 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"title": "Transmission",
|
||||||
"already_configured": "Host is already configured.",
|
|
||||||
"one_instance_allowed": "Only a single instance is necessary."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"cannot_connect": "Unable to Connect to host",
|
|
||||||
"name_exists": "Name already exists",
|
|
||||||
"wrong_credentials": "Wrong username or password"
|
|
||||||
},
|
|
||||||
"step": {
|
"step": {
|
||||||
"options": {
|
|
||||||
"data": {
|
|
||||||
"scan_interval": "Update frequency"
|
|
||||||
},
|
|
||||||
"title": "Configure Options"
|
|
||||||
},
|
|
||||||
"user": {
|
"user": {
|
||||||
|
"title": "Setup Transmission Client",
|
||||||
"data": {
|
"data": {
|
||||||
"host": "Host",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
"host": "Host",
|
||||||
|
"username": "Username",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"port": "Port",
|
"port": "Port"
|
||||||
"username": "Username"
|
}
|
||||||
},
|
|
||||||
"title": "Setup Transmission Client"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "Transmission"
|
"error": {
|
||||||
|
"name_exists": "Name already exists",
|
||||||
|
"wrong_credentials": "Wrong username or password",
|
||||||
|
"cannot_connect": "Unable to Connect to host"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Host is already configured."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
|
"title": "Configure options for Transmission",
|
||||||
"data": {
|
"data": {
|
||||||
"scan_interval": "Update frequency"
|
"scan_interval": "Update frequency"
|
||||||
},
|
}
|
||||||
"description": "Configure options for Transmission",
|
|
||||||
"title": "Configure options for Transmission"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ from homeassistant.exceptions import ConfigEntryNotReady
|
|||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
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.util import slugify
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_TORRENT,
|
ATTR_TORRENT,
|
||||||
@ -28,13 +27,16 @@ from .const import (
|
|||||||
DEFAULT_SCAN_INTERVAL,
|
DEFAULT_SCAN_INTERVAL,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_ADD_TORRENT,
|
SERVICE_ADD_TORRENT,
|
||||||
|
DATA_UPDATED,
|
||||||
)
|
)
|
||||||
from .errors import AuthenticationError, CannotConnect, UnknownError
|
from .errors import AuthenticationError, CannotConnect, UnknownError
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
SERVICE_ADD_TORRENT_SCHEMA = vol.Schema({vol.Required(ATTR_TORRENT): cv.string})
|
SERVICE_ADD_TORRENT_SCHEMA = vol.Schema(
|
||||||
|
{vol.Required(ATTR_TORRENT): cv.string, vol.Required(CONF_NAME): cv.string}
|
||||||
|
)
|
||||||
|
|
||||||
TRANS_SCHEMA = vol.All(
|
TRANS_SCHEMA = vol.All(
|
||||||
vol.Schema(
|
vol.Schema(
|
||||||
@ -55,6 +57,8 @@ CONFIG_SCHEMA = vol.Schema(
|
|||||||
{DOMAIN: vol.All(cv.ensure_list, [TRANS_SCHEMA])}, extra=vol.ALLOW_EXTRA
|
{DOMAIN: vol.All(cv.ensure_list, [TRANS_SCHEMA])}, extra=vol.ALLOW_EXTRA
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PLATFORMS = ["sensor", "switch"]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Import the Transmission Component from config."""
|
"""Import the Transmission Component from config."""
|
||||||
@ -82,15 +86,15 @@ async def async_setup_entry(hass, config_entry):
|
|||||||
|
|
||||||
async def async_unload_entry(hass, config_entry):
|
async def async_unload_entry(hass, config_entry):
|
||||||
"""Unload Transmission Entry from config_entry."""
|
"""Unload Transmission Entry from config_entry."""
|
||||||
client = hass.data[DOMAIN][config_entry.entry_id]
|
client = hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||||
hass.services.async_remove(DOMAIN, client.service_name)
|
|
||||||
if client.unsub_timer:
|
if client.unsub_timer:
|
||||||
client.unsub_timer()
|
client.unsub_timer()
|
||||||
|
|
||||||
for component in "sensor", "switch":
|
for platform in PLATFORMS:
|
||||||
await hass.config_entries.async_forward_entry_unload(config_entry, component)
|
await hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||||
|
|
||||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
if not hass.data[DOMAIN]:
|
||||||
|
hass.services.async_remove(DOMAIN, SERVICE_ADD_TORRENT)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -128,14 +132,10 @@ class TransmissionClient:
|
|||||||
"""Initialize the Transmission RPC API."""
|
"""Initialize the Transmission RPC API."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.config_entry = config_entry
|
self.config_entry = config_entry
|
||||||
|
self.tm_api = None
|
||||||
self._tm_data = None
|
self._tm_data = None
|
||||||
self.unsub_timer = None
|
self.unsub_timer = None
|
||||||
|
|
||||||
@property
|
|
||||||
def service_name(self):
|
|
||||||
"""Return the service name."""
|
|
||||||
return slugify(f"{SERVICE_ADD_TORRENT}_{self.config_entry.data[CONF_NAME]}")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api(self):
|
def api(self):
|
||||||
"""Return the tm_data object."""
|
"""Return the tm_data object."""
|
||||||
@ -145,20 +145,20 @@ class TransmissionClient:
|
|||||||
"""Set up the Transmission client."""
|
"""Set up the Transmission client."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
api = await get_api(self.hass, self.config_entry.data)
|
self.tm_api = await get_api(self.hass, self.config_entry.data)
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
raise ConfigEntryNotReady
|
raise ConfigEntryNotReady
|
||||||
except (AuthenticationError, UnknownError):
|
except (AuthenticationError, UnknownError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._tm_data = TransmissionData(self.hass, self.config_entry, api)
|
self._tm_data = TransmissionData(self.hass, self.config_entry, self.tm_api)
|
||||||
|
|
||||||
await self.hass.async_add_executor_job(self._tm_data.init_torrent_list)
|
await self.hass.async_add_executor_job(self._tm_data.init_torrent_list)
|
||||||
await self.hass.async_add_executor_job(self._tm_data.update)
|
await self.hass.async_add_executor_job(self._tm_data.update)
|
||||||
self.add_options()
|
self.add_options()
|
||||||
self.set_scan_interval(self.config_entry.options[CONF_SCAN_INTERVAL])
|
self.set_scan_interval(self.config_entry.options[CONF_SCAN_INTERVAL])
|
||||||
|
|
||||||
for platform in ["sensor", "switch"]:
|
for platform in PLATFORMS:
|
||||||
self.hass.async_create_task(
|
self.hass.async_create_task(
|
||||||
self.hass.config_entries.async_forward_entry_setup(
|
self.hass.config_entries.async_forward_entry_setup(
|
||||||
self.config_entry, platform
|
self.config_entry, platform
|
||||||
@ -167,18 +167,26 @@ class TransmissionClient:
|
|||||||
|
|
||||||
def add_torrent(service):
|
def add_torrent(service):
|
||||||
"""Add new torrent to download."""
|
"""Add new torrent to download."""
|
||||||
|
tm_client = None
|
||||||
|
for entry in self.hass.config_entries.async_entries(DOMAIN):
|
||||||
|
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:")
|
||||||
) or self.hass.config.is_allowed_path(torrent):
|
) or self.hass.config.is_allowed_path(torrent):
|
||||||
api.add_torrent(torrent)
|
tm_client.tm_api.add_torrent(torrent)
|
||||||
else:
|
else:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Could not add torrent: unsupported type or no permission"
|
"Could not add torrent: unsupported type or no permission"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.hass.services.async_register(
|
self.hass.services.async_register(
|
||||||
DOMAIN, self.service_name, add_torrent, schema=SERVICE_ADD_TORRENT_SCHEMA
|
DOMAIN, SERVICE_ADD_TORRENT, add_torrent, schema=SERVICE_ADD_TORRENT_SCHEMA
|
||||||
)
|
)
|
||||||
|
|
||||||
self.config_entry.add_update_listener(self.async_options_updated)
|
self.config_entry.add_update_listener(self.async_options_updated)
|
||||||
@ -200,7 +208,7 @@ class TransmissionClient:
|
|||||||
def set_scan_interval(self, scan_interval):
|
def set_scan_interval(self, scan_interval):
|
||||||
"""Update scan interval."""
|
"""Update scan interval."""
|
||||||
|
|
||||||
async def refresh(event_time):
|
def refresh(event_time):
|
||||||
"""Get the latest data from Transmission."""
|
"""Get the latest data from Transmission."""
|
||||||
self._tm_data.update()
|
self._tm_data.update()
|
||||||
|
|
||||||
@ -240,9 +248,9 @@ class TransmissionData:
|
|||||||
return self.config.data[CONF_HOST]
|
return self.config.data[CONF_HOST]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def signal_options_update(self):
|
def signal_update(self):
|
||||||
"""Option update signal per transmission entry."""
|
"""Update signal per transmission entry."""
|
||||||
return f"tm-options-{self.host}"
|
return f"{DATA_UPDATED}-{self.host}"
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Get the latest data from Transmission instance."""
|
"""Get the latest data from Transmission instance."""
|
||||||
@ -260,7 +268,7 @@ class TransmissionData:
|
|||||||
except TransmissionError:
|
except TransmissionError:
|
||||||
self.available = False
|
self.available = False
|
||||||
_LOGGER.error("Unable to connect to Transmission client %s", self.host)
|
_LOGGER.error("Unable to connect to Transmission client %s", self.host)
|
||||||
dispatcher_send(self.hass, self.signal_options_update)
|
dispatcher_send(self.hass, self.signal_update)
|
||||||
|
|
||||||
def init_torrent_list(self):
|
def init_torrent_list(self):
|
||||||
"""Initialize torrent lists."""
|
"""Initialize torrent lists."""
|
||||||
|
@ -16,9 +16,19 @@ from . import get_api
|
|||||||
from .const import DEFAULT_NAME, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DOMAIN
|
from .const import DEFAULT_NAME, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DOMAIN
|
||||||
from .errors import AuthenticationError, CannotConnect, UnknownError
|
from .errors import AuthenticationError, CannotConnect, UnknownError
|
||||||
|
|
||||||
|
DATA_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_NAME, default=DEFAULT_NAME): str,
|
||||||
|
vol.Required(CONF_HOST): str,
|
||||||
|
vol.Optional(CONF_USERNAME): str,
|
||||||
|
vol.Optional(CONF_PASSWORD): str,
|
||||||
|
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TransmissionFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
class TransmissionFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a UniFi config flow."""
|
"""Handle Tansmission config flow."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
||||||
@ -57,17 +67,7 @@ class TransmissionFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user", data_schema=DATA_SCHEMA, errors=errors,
|
||||||
data_schema=vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Required(CONF_NAME, default=DEFAULT_NAME): str,
|
|
||||||
vol.Required(CONF_HOST): str,
|
|
||||||
vol.Optional(CONF_USERNAME): str,
|
|
||||||
vol.Optional(CONF_PASSWORD): str,
|
|
||||||
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
errors=errors,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_import(self, import_config):
|
async def async_step_import(self, import_config):
|
||||||
|
@ -52,6 +52,7 @@ class TransmissionSensor(Entity):
|
|||||||
self._data = None
|
self._data = None
|
||||||
self.client_name = client_name
|
self.client_name = client_name
|
||||||
self.type = sensor_type
|
self.type = sensor_type
|
||||||
|
self.unsub_update = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -92,9 +93,9 @@ class TransmissionSensor(Entity):
|
|||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Handle entity which will be added."""
|
"""Handle entity which will be added."""
|
||||||
async_dispatcher_connect(
|
self.unsub_update = async_dispatcher_connect(
|
||||||
self.hass,
|
self.hass,
|
||||||
self._tm_client.api.signal_options_update,
|
self._tm_client.api.signal_update,
|
||||||
self._schedule_immediate_update,
|
self._schedule_immediate_update,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -102,6 +103,12 @@ class TransmissionSensor(Entity):
|
|||||||
def _schedule_immediate_update(self):
|
def _schedule_immediate_update(self):
|
||||||
self.async_schedule_update_ha_state(True)
|
self.async_schedule_update_ha_state(True)
|
||||||
|
|
||||||
|
async def will_remove_from_hass(self):
|
||||||
|
"""Unsubscribe from update dispatcher."""
|
||||||
|
if self.unsub_update:
|
||||||
|
self.unsub_update()
|
||||||
|
self.unsub_update = None
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Get the latest data from Transmission and updates the state."""
|
"""Get the latest data from Transmission and updates the state."""
|
||||||
self._data = self._tm_client.api.data
|
self._data = self._tm_client.api.data
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
add_torrent:
|
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:
|
||||||
|
name:
|
||||||
|
description: Instance name as entered during entry config
|
||||||
|
example: Transmission
|
||||||
torrent:
|
torrent:
|
||||||
description: URL, magnet link or Base64 encoded file.
|
description: URL, magnet link or Base64 encoded file.
|
||||||
example: http://releases.ubuntu.com/19.04/ubuntu-19.04-desktop-amd64.iso.torrent
|
example: http://releases.ubuntu.com/19.04/ubuntu-19.04-desktop-amd64.iso.torrent
|
||||||
|
@ -40,6 +40,7 @@ class TransmissionSwitch(ToggleEntity):
|
|||||||
self._tm_client = tm_client
|
self._tm_client = tm_client
|
||||||
self._state = STATE_OFF
|
self._state = STATE_OFF
|
||||||
self._data = None
|
self._data = None
|
||||||
|
self.unsub_update = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -93,9 +94,9 @@ class TransmissionSwitch(ToggleEntity):
|
|||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Handle entity which will be added."""
|
"""Handle entity which will be added."""
|
||||||
async_dispatcher_connect(
|
self.unsub_update = async_dispatcher_connect(
|
||||||
self.hass,
|
self.hass,
|
||||||
self._tm_client.api.signal_options_update,
|
self._tm_client.api.signal_update,
|
||||||
self._schedule_immediate_update,
|
self._schedule_immediate_update,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -103,6 +104,12 @@ class TransmissionSwitch(ToggleEntity):
|
|||||||
def _schedule_immediate_update(self):
|
def _schedule_immediate_update(self):
|
||||||
self.async_schedule_update_ha_state(True)
|
self.async_schedule_update_ha_state(True)
|
||||||
|
|
||||||
|
async def will_remove_from_hass(self):
|
||||||
|
"""Unsubscribe from update dispatcher."""
|
||||||
|
if self.unsub_update:
|
||||||
|
self.unsub_update()
|
||||||
|
self.unsub_update = None
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Get the latest data from Transmission and updates the state."""
|
"""Get the latest data from Transmission and updates the state."""
|
||||||
active = None
|
active = None
|
||||||
|
@ -98,7 +98,6 @@ async def test_flow_works(hass, api):
|
|||||||
assert result["data"][CONF_NAME] == NAME
|
assert result["data"][CONF_NAME] == NAME
|
||||||
assert result["data"][CONF_HOST] == HOST
|
assert result["data"][CONF_HOST] == HOST
|
||||||
assert result["data"][CONF_PORT] == PORT
|
assert result["data"][CONF_PORT] == PORT
|
||||||
# assert result["data"]["options"][CONF_SCAN_INTERVAL] == DEFAULT_SCAN_INTERVAL
|
|
||||||
|
|
||||||
# test with all provided
|
# test with all provided
|
||||||
result = await flow.async_step_user(MOCK_ENTRY)
|
result = await flow.async_step_user(MOCK_ENTRY)
|
||||||
@ -110,7 +109,6 @@ async def test_flow_works(hass, api):
|
|||||||
assert result["data"][CONF_USERNAME] == USERNAME
|
assert result["data"][CONF_USERNAME] == USERNAME
|
||||||
assert result["data"][CONF_PASSWORD] == PASSWORD
|
assert result["data"][CONF_PASSWORD] == PASSWORD
|
||||||
assert result["data"][CONF_PORT] == PORT
|
assert result["data"][CONF_PORT] == PORT
|
||||||
# assert result["data"]["options"][CONF_SCAN_INTERVAL] == DEFAULT_SCAN_INTERVAL
|
|
||||||
|
|
||||||
|
|
||||||
async def test_options(hass):
|
async def test_options(hass):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user