From 4a374f037864aad9330caaf39c48656058acb654 Mon Sep 17 00:00:00 2001 From: Gleb Sinyavskiy Date: Sun, 28 Jun 2020 13:56:54 +0200 Subject: [PATCH] Limit and sort transmission torrents_info attribute (#35411) --- .../components/transmission/__init__.py | 18 ++++-- .../components/transmission/config_flow.py | 22 ++++++- .../components/transmission/const.py | 22 ++++++- .../components/transmission/sensor.py | 57 +++++++++++++------ .../components/transmission/strings.json | 4 +- .../transmission/translations/en.json | 6 +- .../transmission/test_config_flow.py | 8 +++ tests/components/transmission/test_init.py | 4 +- 8 files changed, 114 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/transmission/__init__.py b/homeassistant/components/transmission/__init__.py index a09fb6a7456..2fd7e60cf31 100644 --- a/homeassistant/components/transmission/__init__.py +++ b/homeassistant/components/transmission/__init__.py @@ -24,9 +24,13 @@ from homeassistant.helpers.event import async_track_time_interval from .const import ( ATTR_DELETE_DATA, ATTR_TORRENT, + CONF_LIMIT, + CONF_ORDER, DATA_UPDATED, DEFAULT_DELETE_DATA, + DEFAULT_LIMIT, DEFAULT_NAME, + DEFAULT_ORDER, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DOMAIN, @@ -238,7 +242,13 @@ class TransmissionClient: scan_interval = self.config_entry.data.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL ) - options = {CONF_SCAN_INTERVAL: scan_interval} + limit = self.config_entry.data.get(CONF_LIMIT, DEFAULT_LIMIT) + order = self.config_entry.data.get(CONF_ORDER, DEFAULT_ORDER) + options = { + CONF_SCAN_INTERVAL: scan_interval, + CONF_LIMIT: limit, + CONF_ORDER: order, + } self.hass.config_entries.async_update_entry( self.config_entry, options=options @@ -260,9 +270,9 @@ class TransmissionClient: @staticmethod async def async_options_updated(hass, entry): """Triggered by config entry options updates.""" - hass.data[DOMAIN][entry.entry_id].set_scan_interval( - entry.options[CONF_SCAN_INTERVAL] - ) + tm_client = hass.data[DOMAIN][entry.entry_id] + tm_client.set_scan_interval(entry.options[CONF_SCAN_INTERVAL]) + await hass.async_add_executor_job(tm_client.api.update) class TransmissionData: diff --git a/homeassistant/components/transmission/config_flow.py b/homeassistant/components/transmission/config_flow.py index 193c152d7c1..c457306310d 100644 --- a/homeassistant/components/transmission/config_flow.py +++ b/homeassistant/components/transmission/config_flow.py @@ -13,7 +13,17 @@ from homeassistant.const import ( from homeassistant.core import callback from . import get_api -from .const import DEFAULT_NAME, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DOMAIN +from .const import ( + CONF_LIMIT, + CONF_ORDER, + DEFAULT_LIMIT, + DEFAULT_NAME, + DEFAULT_ORDER, + DEFAULT_PORT, + DEFAULT_SCAN_INTERVAL, + DOMAIN, + SUPPORTED_ORDER_MODES, +) from .errors import AuthenticationError, CannotConnect, UnknownError DATA_SCHEMA = vol.Schema( @@ -94,7 +104,15 @@ class TransmissionOptionsFlowHandler(config_entries.OptionsFlow): default=self.config_entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL ), - ): int + ): int, + vol.Optional( + CONF_LIMIT, + default=self.config_entry.options.get(CONF_LIMIT, DEFAULT_LIMIT), + ): vol.All(vol.Coerce(int), vol.Range(min=1, max=500)), + vol.Optional( + CONF_ORDER, + default=self.config_entry.options.get(CONF_ORDER, DEFAULT_ORDER), + ): vol.All(vol.Coerce(str), vol.In(SUPPORTED_ORDER_MODES.keys())), } return self.async_show_form(step_id="init", data_schema=vol.Schema(options)) diff --git a/homeassistant/components/transmission/const.py b/homeassistant/components/transmission/const.py index 8edbf944890..960fd7a65b4 100644 --- a/homeassistant/components/transmission/const.py +++ b/homeassistant/components/transmission/const.py @@ -1,10 +1,30 @@ """Constants for the Transmission Bittorent Client component.""" - DOMAIN = "transmission" SWITCH_TYPES = {"on_off": "Switch", "turtle_mode": "Turtle Mode"} +ORDER_NEWEST_FIRST = "newest_first" +ORDER_OLDEST_FIRST = "oldest_first" +ORDER_BEST_RATIO_FIRST = "best_ratio_first" +ORDER_WORST_RATIO_FIRST = "worst_ratio_first" + +SUPPORTED_ORDER_MODES = { + ORDER_NEWEST_FIRST: lambda torrents: sorted( + torrents, key=lambda t: t.addedDate, reverse=True + ), + ORDER_OLDEST_FIRST: lambda torrents: sorted(torrents, key=lambda t: t.addedDate), + ORDER_WORST_RATIO_FIRST: lambda torrents: sorted(torrents, key=lambda t: t.ratio), + ORDER_BEST_RATIO_FIRST: lambda torrents: sorted( + torrents, key=lambda t: t.ratio, reverse=True + ), +} + +CONF_LIMIT = "limit" +CONF_ORDER = "order" + DEFAULT_DELETE_DATA = False +DEFAULT_LIMIT = 10 +DEFAULT_ORDER = ORDER_OLDEST_FIRST DEFAULT_NAME = "Transmission" DEFAULT_PORT = 9091 DEFAULT_SCAN_INTERVAL = 120 diff --git a/homeassistant/components/transmission/sensor.py b/homeassistant/components/transmission/sensor.py index 812d63f24d8..baa70bf0b19 100644 --- a/homeassistant/components/transmission/sensor.py +++ b/homeassistant/components/transmission/sensor.py @@ -6,7 +6,13 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -from .const import DOMAIN, STATE_ATTR_TORRENT_INFO +from .const import ( + CONF_LIMIT, + CONF_ORDER, + DOMAIN, + STATE_ATTR_TORRENT_INFO, + SUPPORTED_ORDER_MODES, +) _LOGGER = logging.getLogger(__name__) @@ -143,28 +149,45 @@ class TransmissionTorrentsSensor(TransmissionSensor): @property def device_state_attributes(self): """Return the state attributes, if any.""" + limit = self._tm_client.config_entry.options[CONF_LIMIT] + order = self._tm_client.config_entry.options[CONF_ORDER] + torrents = self._tm_client.api.torrents[0:limit] info = _torrents_info( - self._tm_client.api.torrents, self.SUBTYPE_MODES[self._sub_type] + torrents, order=order, statuses=self.SUBTYPE_MODES[self._sub_type], ) - return {STATE_ATTR_TORRENT_INFO: info} + return { + STATE_ATTR_TORRENT_INFO: info, + } def update(self): """Get the latest data from Transmission and updates the state.""" - self._state = len(self.device_state_attributes[STATE_ATTR_TORRENT_INFO]) + torrents = _filter_torrents( + self._tm_client.api.torrents, statuses=self.SUBTYPE_MODES[self._sub_type] + ) + self._state = len(torrents) -def _torrents_info(torrents, statuses=None): +def _filter_torrents(torrents, statuses=None): + return [ + torrent + for torrent in torrents + if statuses is None or torrent.status in statuses + ] + + +def _torrents_info(torrents, order, statuses=None): infos = {} - for torrent in torrents: - if statuses is None or torrent.status in statuses: - info = infos[torrent.name] = { - "added_date": torrent.addedDate, - "percent_done": f"{torrent.percentDone * 100:.2f}", - "status": torrent.status, - "id": torrent.id, - } - try: - info["eta"] = str(torrent.eta) - except ValueError: - pass + torrents = _filter_torrents(torrents, statuses) + torrents = SUPPORTED_ORDER_MODES[order](torrents) + for torrent in _filter_torrents(torrents, statuses): + info = infos[torrent.name] = { + "added_date": torrent.addedDate, + "percent_done": f"{torrent.percentDone * 100:.2f}", + "status": torrent.status, + "id": torrent.id, + } + try: + info["eta"] = str(torrent.eta) + except ValueError: + pass return infos diff --git a/homeassistant/components/transmission/strings.json b/homeassistant/components/transmission/strings.json index e16ef43a48e..6d12258aff4 100644 --- a/homeassistant/components/transmission/strings.json +++ b/homeassistant/components/transmission/strings.json @@ -8,7 +8,9 @@ "host": "[%key:common::config_flow::data::host%]", "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", - "port": "[%key:common::config_flow::data::port%]" + "port": "[%key:common::config_flow::data::port%]", + "limit": "Limit", + "order": "Order" } } }, diff --git a/homeassistant/components/transmission/translations/en.json b/homeassistant/components/transmission/translations/en.json index 702fda2dcd5..11ebfd61e1c 100644 --- a/homeassistant/components/transmission/translations/en.json +++ b/homeassistant/components/transmission/translations/en.json @@ -12,6 +12,8 @@ "user": { "data": { "host": "Host", + "limit": "Limit", + "order": "Order", "name": "Name", "password": "Password", "port": "Port", @@ -25,7 +27,9 @@ "step": { "init": { "data": { - "scan_interval": "Update frequency" + "scan_interval": "Update frequency", + "limit": "Limit", + "order": "Order" }, "title": "Configure options for Transmission" } diff --git a/tests/components/transmission/test_config_flow.py b/tests/components/transmission/test_config_flow.py index 4436a6adf21..6b9917d17f4 100644 --- a/tests/components/transmission/test_config_flow.py +++ b/tests/components/transmission/test_config_flow.py @@ -8,7 +8,11 @@ from homeassistant import data_entry_flow from homeassistant.components import transmission from homeassistant.components.transmission import config_flow from homeassistant.components.transmission.const import ( + CONF_LIMIT, + CONF_ORDER, + DEFAULT_LIMIT, DEFAULT_NAME, + DEFAULT_ORDER, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, ) @@ -158,6 +162,8 @@ async def test_import(hass, api): CONF_HOST: HOST, CONF_PORT: DEFAULT_PORT, CONF_SCAN_INTERVAL: timedelta(seconds=DEFAULT_SCAN_INTERVAL), + CONF_LIMIT: DEFAULT_LIMIT, + CONF_ORDER: DEFAULT_ORDER, } ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY @@ -176,6 +182,8 @@ async def test_import(hass, api): CONF_PASSWORD: PASSWORD, CONF_PORT: PORT, CONF_SCAN_INTERVAL: timedelta(seconds=SCAN_INTERVAL), + CONF_LIMIT: DEFAULT_LIMIT, + CONF_ORDER: DEFAULT_ORDER, } ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY diff --git a/tests/components/transmission/test_init.py b/tests/components/transmission/test_init.py index 4baa00de7a7..78fddc5be86 100644 --- a/tests/components/transmission/test_init.py +++ b/tests/components/transmission/test_init.py @@ -81,7 +81,9 @@ async def test_successful_config_entry(hass, api): assert await transmission.async_setup_entry(hass, entry) is True assert entry.options == { - transmission.CONF_SCAN_INTERVAL: transmission.DEFAULT_SCAN_INTERVAL + transmission.CONF_SCAN_INTERVAL: transmission.DEFAULT_SCAN_INTERVAL, + transmission.CONF_LIMIT: transmission.DEFAULT_LIMIT, + transmission.CONF_ORDER: transmission.DEFAULT_ORDER, }