Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add enhancements to qBittorrent sensor #42837

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
127 changes: 122 additions & 5 deletions homeassistant/components/qbittorrent/sensor.py
Expand Up @@ -7,6 +7,7 @@

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_MONITORED_CONDITIONS,
CONF_NAME,
CONF_PASSWORD,
CONF_URL,
Expand All @@ -23,13 +24,31 @@
SENSOR_TYPE_CURRENT_STATUS = "current_status"
SENSOR_TYPE_DOWNLOAD_SPEED = "download_speed"
SENSOR_TYPE_UPLOAD_SPEED = "upload_speed"
SENSOR_TYPE_TOTAL_TORRENTS = "total_torrents"
SENSOR_TYPE_ACTIVE_TORRENTS = "active_torrents"
SENSOR_TYPE_INACTIVE_TORRENTS = "inactive_torrents"
SENSOR_TYPE_DOWNLOADING_TORRENTS = "downloading_torrents"
SENSOR_TYPE_SEEDING_TORRENTS = "seeding_torrents"
SENSOR_TYPE_RESUMED_TORRENTS = "resumed_torrents"
SENSOR_TYPE_PAUSED_TORRENTS = "paused_torrents"
SENSOR_TYPE_COMPLETED_TORRENTS = "completed_torrents"

DEFAULT_NAME = "qBittorrent"
DEFAULT_CONDITIONS = ["current_status", "download_speed", "upload_speed"]
TRIM_SIZE = 35

SENSOR_TYPES = {
SENSOR_TYPE_CURRENT_STATUS: ["Status", None],
SENSOR_TYPE_DOWNLOAD_SPEED: ["Down Speed", DATA_RATE_KILOBYTES_PER_SECOND],
SENSOR_TYPE_UPLOAD_SPEED: ["Up Speed", DATA_RATE_KILOBYTES_PER_SECOND],
SENSOR_TYPE_TOTAL_TORRENTS: ["Total Torrents", None],
SENSOR_TYPE_ACTIVE_TORRENTS: ["Active Torrents", None],
SENSOR_TYPE_INACTIVE_TORRENTS: ["Inactive Torrents", None],
SENSOR_TYPE_DOWNLOADING_TORRENTS: ["Downloading Torrents", None],
SENSOR_TYPE_SEEDING_TORRENTS: ["Seeding Torrents", None],
SENSOR_TYPE_RESUMED_TORRENTS: ["Resumed Torrents", None],
SENSOR_TYPE_PAUSED_TORRENTS: ["Paused Torrents", None],
SENSOR_TYPE_COMPLETED_TORRENTS: ["Completed Torrents", None],
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
Expand All @@ -38,13 +57,15 @@
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_MONITORED_CONDITIONS, default=DEFAULT_CONDITIONS): vol.All(
cv.ensure_list, [vol.In(SENSOR_TYPES)]
),
Comment on lines +60 to +62
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this goes against ADR-0003:

We don't merge PRs with data selectors. We remove existing unneeded selectors until Home Assistant 1.0. We focus on a good view/frontend to select and group available data.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah OK I hadn't seen that, I saw here about the use of MONITORED_CONDITIONS and assumed it was OK as didn't see any mention there of any issues regarding using.

Can you make any suggestions as to what to change? Sorry if it's a basic question, this is already way over my head!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, monitored conditions is still used in some integrations. Ideally you would add a config flow so users can set it up via the frontend and do not have to alter their YAML. With a config flow a user can also easily disable sensors that they do not want to show on the frontend.

However, in this case I think it would be okay to just remove the monitored conditions and just set up all of them automatically.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi springstan, I've added everythingsmarthomes to my PR and extended the qbittorrent integration with flow and added some services #43661

}
)


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the qBittorrent sensors."""

try:
client = Client(config[CONF_URL])
client.login(config[CONF_USERNAME], config[CONF_PASSWORD])
Expand All @@ -56,10 +77,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
raise PlatformNotReady from err

name = config.get(CONF_NAME)
monitored_variables = config.get(CONF_MONITORED_CONDITIONS)

dev = []
for sensor_type in SENSOR_TYPES:
sensor = QBittorrentSensor(sensor_type, client, name, LoginRequired)
for monitored_variable in monitored_variables:
sensor = QBittorrentSensor(monitored_variable, client, name, LoginRequired)
dev.append(sensor)

add_entities(dev, True)
Expand All @@ -80,6 +102,7 @@ def __init__(self, sensor_type, qbittorrent_client, client_name, exception):
self.client = qbittorrent_client
self.type = sensor_type
self.client_name = client_name
self.attrs = {}
self._state = None
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._available = False
Expand All @@ -95,6 +118,11 @@ def state(self):
"""Return the state of the sensor."""
return self._state

@property
def device_state_attributes(self):
"""Return device specific state attributes."""
return self.attrs

@property
def available(self):
"""Return true if device is available."""
Expand All @@ -117,24 +145,113 @@ def update(self):
except self._exception:
_LOGGER.error("Invalid authentication")
return

if data is None:
return

attributes = {}
download = data["server_state"]["dl_info_speed"]
upload = data["server_state"]["up_info_speed"]

if self.type == SENSOR_TYPE_CURRENT_STATUS:
if upload > 0 and download > 0:
self._state = "up_down"
self.attrs = attributes
Copy link
Contributor

@Adminiuga Adminiuga Nov 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you just move this line out side of these if statements and just do it at the end once?

elif upload > 0 and download == 0:
self._state = "seeding"
self.attrs = attributes
elif upload == 0 and download > 0:
self._state = "downloading"
self.attrs = attributes
else:
self._state = STATE_IDLE
self.attrs = attributes
elif self.type == "total_torrents":
data = self.client.torrents()

for torrent in data:
attributes[format_name(torrent, TRIM_SIZE - 5)] = torrent["state"]

self._state = len(data)
self.attrs = attributes
elif self.type == "active_torrents":
data = self.client.torrents(filter="active")

for torrent in data:
attributes[format_name(torrent, TRIM_SIZE - 5)] = torrent["state"]

self._state = len(data)
self.attrs = attributes
elif self.type == "inactive_torrents":
data = self.client.torrents(filter="inactive")

for torrent in data:
attributes[format_name(torrent, TRIM_SIZE - 5)] = torrent["state"]

self._state = len(data)
self.attrs = attributes
elif self.type == "downloading_torrents":
data = self.client.torrents(filter="downloading")
for torrent in data:
attributes[format_name(torrent)] = format_progress(torrent)

self._state = len(data)
self.attrs = attributes
elif self.type == "seeding_torrents":
data = self.client.torrents(filter="seeding")

for torrent in data:
ratio = torrent["ratio"]
ratio = float(ratio)
ratio = f"{ratio:.2f}"

attributes[format_name(torrent)] = ratio

self._state = len(data)
self.attrs = attributes
elif self.type == "resumed_torrents":
data = self.client.torrents(filter="resumed")

for torrent in data:
attributes[format_name(torrent)] = format_progress(torrent)

self._state = len(data)
self.attrs = attributes
elif self.type == "paused_torrents":
data = self.client.torrents(filter="paused")

for torrent in data:
attributes[format_name(torrent)] = format_progress(torrent)

self._state = len(data)
self.attrs = attributes
elif self.type == "completed_torrents":
data = self.client.torrents(filter="completed")

for torrent in data:
attributes[format_name(torrent)] = "100.0%"

self._state = len(data)
self.attrs = attributes

elif self.type == SENSOR_TYPE_DOWNLOAD_SPEED:
self._state = format_speed(download)
self.attrs = attributes

elif self.type == SENSOR_TYPE_UPLOAD_SPEED:
self._state = format_speed(upload)
self.attrs = attributes


def format_name(torrent, trim_size=TRIM_SIZE):
"""Return the formatted name of the torrent to fit better within UI."""
name = torrent["name"]
if len(name) > trim_size:
name = name[0:trim_size] + "..."
return name


def format_progress(torrent):
"""Return the progress percentage of the torrent."""
progress = torrent["progress"]
progress = float(progress) * 100
progress = f"{progress:.1f}%"
return progress