Improve the transmission integration (#34223)
* Update state after adding a new torrent * Use cached torrents list in check_started_torrent_info * Add torrent_info to all sensors * Add torrent_info for active torrents * Fix typo * Update codeowners * Do not set eta if it's unknown * Fix codeowners * Extract TransmissionSpeedSensor * Extract TransmissionStatusSensor * Extract TransmissionTorrentsSensor * Refactor device_state_attributes() and update() * Remove unused methods * Use async_on_remove * Fix sensor update * Add transmission.remove_torrent service * Add transmission_removed_torrent event * Fix naming * Fix typo in services.yaml
This commit is contained in:
parent
c87ecf0ff6
commit
9062d6e5e6
4 changed files with 182 additions and 130 deletions
|
@ -1,12 +1,12 @@
|
|||
"""Support for monitoring the Transmission BitTorrent client API."""
|
||||
import logging
|
||||
|
||||
from homeassistant.const import CONF_NAME, STATE_IDLE
|
||||
from homeassistant.const import CONF_NAME, DATA_RATE_MEGABYTES_PER_SECOND, STATE_IDLE
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import DOMAIN, SENSOR_TYPES, STATE_ATTR_TORRENT_INFO
|
||||
from .const import DOMAIN, STATE_ATTR_TORRENT_INFO
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -17,41 +17,35 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
tm_client = hass.data[DOMAIN][config_entry.entry_id]
|
||||
name = config_entry.data[CONF_NAME]
|
||||
|
||||
dev = []
|
||||
for sensor_type in SENSOR_TYPES:
|
||||
dev.append(
|
||||
TransmissionSensor(
|
||||
sensor_type,
|
||||
tm_client,
|
||||
name,
|
||||
SENSOR_TYPES[sensor_type][0],
|
||||
SENSOR_TYPES[sensor_type][1],
|
||||
)
|
||||
)
|
||||
dev = [
|
||||
TransmissionSpeedSensor(tm_client, name, "Down Speed", "download"),
|
||||
TransmissionSpeedSensor(tm_client, name, "Up Speed", "upload"),
|
||||
TransmissionStatusSensor(tm_client, name, "Status"),
|
||||
TransmissionTorrentsSensor(tm_client, name, "Active Torrents", "active"),
|
||||
TransmissionTorrentsSensor(tm_client, name, "Paused Torrents", "paused"),
|
||||
TransmissionTorrentsSensor(tm_client, name, "Total Torrents", "total"),
|
||||
TransmissionTorrentsSensor(tm_client, name, "Completed Torrents", "completed"),
|
||||
TransmissionTorrentsSensor(tm_client, name, "Started Torrents", "started"),
|
||||
]
|
||||
|
||||
async_add_entities(dev, True)
|
||||
|
||||
|
||||
class TransmissionSensor(Entity):
|
||||
"""Representation of a Transmission sensor."""
|
||||
"""A base class for all Transmission sensors."""
|
||||
|
||||
def __init__(
|
||||
self, sensor_type, tm_client, client_name, sensor_name, unit_of_measurement
|
||||
):
|
||||
def __init__(self, tm_client, client_name, sensor_name, sub_type=None):
|
||||
"""Initialize the sensor."""
|
||||
self._name = sensor_name
|
||||
self._state = None
|
||||
self._tm_client = tm_client
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
self._data = None
|
||||
self.client_name = client_name
|
||||
self.type = sensor_type
|
||||
self.unsub_update = None
|
||||
self._client_name = client_name
|
||||
self._name = sensor_name
|
||||
self._sub_type = sub_type
|
||||
self._state = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return f"{self.client_name} {self._name}"
|
||||
return f"{self._client_name} {self._name}"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
|
@ -68,77 +62,109 @@ class TransmissionSensor(Entity):
|
|||
"""Return the polling requirement for this sensor."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement of this entity, if any."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Could the device be accessed during the last update call."""
|
||||
return self._tm_client.api.available
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes, if any."""
|
||||
if self._tm_client.api.started_torrent_dict and self.type == "started_torrents":
|
||||
return {STATE_ATTR_TORRENT_INFO: self._tm_client.api.started_torrent_dict}
|
||||
return None
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Handle entity which will be added."""
|
||||
self.unsub_update = async_dispatcher_connect(
|
||||
self.hass,
|
||||
self._tm_client.api.signal_update,
|
||||
self._schedule_immediate_update,
|
||||
|
||||
@callback
|
||||
def update():
|
||||
"""Update the state."""
|
||||
self.async_schedule_update_ha_state(True)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, self._tm_client.api.signal_update, update
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def _schedule_immediate_update(self):
|
||||
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
|
||||
class TransmissionSpeedSensor(TransmissionSensor):
|
||||
"""Representation of a Transmission speed sensor."""
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement of this entity, if any."""
|
||||
return DATA_RATE_MEGABYTES_PER_SECOND
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data from Transmission and updates the state."""
|
||||
self._data = self._tm_client.api.data
|
||||
data = self._tm_client.api.data
|
||||
if data:
|
||||
mb_spd = (
|
||||
float(data.downloadSpeed)
|
||||
if self._sub_type == "download"
|
||||
else float(data.uploadSpeed)
|
||||
)
|
||||
mb_spd = mb_spd / 1024 / 1024
|
||||
self._state = round(mb_spd, 2 if mb_spd < 0.1 else 1)
|
||||
|
||||
if self.type == "completed_torrents":
|
||||
self._state = self._tm_client.api.get_completed_torrent_count()
|
||||
elif self.type == "started_torrents":
|
||||
self._state = self._tm_client.api.get_started_torrent_count()
|
||||
|
||||
if self.type == "current_status":
|
||||
if self._data:
|
||||
upload = self._data.uploadSpeed
|
||||
download = self._data.downloadSpeed
|
||||
if upload > 0 and download > 0:
|
||||
self._state = "Up/Down"
|
||||
elif upload > 0 and download == 0:
|
||||
self._state = "Seeding"
|
||||
elif upload == 0 and download > 0:
|
||||
self._state = "Downloading"
|
||||
else:
|
||||
self._state = STATE_IDLE
|
||||
class TransmissionStatusSensor(TransmissionSensor):
|
||||
"""Representation of a Transmission status sensor."""
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data from Transmission and updates the state."""
|
||||
data = self._tm_client.api.data
|
||||
if data:
|
||||
upload = data.uploadSpeed
|
||||
download = data.downloadSpeed
|
||||
if upload > 0 and download > 0:
|
||||
self._state = "Up/Down"
|
||||
elif upload > 0 and download == 0:
|
||||
self._state = "Seeding"
|
||||
elif upload == 0 and download > 0:
|
||||
self._state = "Downloading"
|
||||
else:
|
||||
self._state = None
|
||||
self._state = STATE_IDLE
|
||||
else:
|
||||
self._state = None
|
||||
|
||||
if self._data:
|
||||
if self.type == "download_speed":
|
||||
mb_spd = float(self._data.downloadSpeed)
|
||||
mb_spd = mb_spd / 1024 / 1024
|
||||
self._state = round(mb_spd, 2 if mb_spd < 0.1 else 1)
|
||||
elif self.type == "upload_speed":
|
||||
mb_spd = float(self._data.uploadSpeed)
|
||||
mb_spd = mb_spd / 1024 / 1024
|
||||
self._state = round(mb_spd, 2 if mb_spd < 0.1 else 1)
|
||||
elif self.type == "active_torrents":
|
||||
self._state = self._data.activeTorrentCount
|
||||
elif self.type == "paused_torrents":
|
||||
self._state = self._data.pausedTorrentCount
|
||||
elif self.type == "total_torrents":
|
||||
self._state = self._data.torrentCount
|
||||
|
||||
class TransmissionTorrentsSensor(TransmissionSensor):
|
||||
"""Representation of a Transmission torrents sensor."""
|
||||
|
||||
SUBTYPE_MODES = {
|
||||
"started": ("downloading"),
|
||||
"completed": ("seeding"),
|
||||
"paused": ("stopped"),
|
||||
"active": ("seeding", "downloading"),
|
||||
"total": None,
|
||||
}
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement of this entity, if any."""
|
||||
return "Torrents"
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes, if any."""
|
||||
info = _torrents_info(
|
||||
self._tm_client.api.torrents, self.SUBTYPE_MODES[self._sub_type]
|
||||
)
|
||||
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])
|
||||
|
||||
|
||||
def _torrents_info(torrents, 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
|
||||
return infos
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue