diff --git a/.coveragerc b/.coveragerc index 2a6446092e56..7c3ee19a3f49 100644 --- a/.coveragerc +++ b/.coveragerc @@ -780,6 +780,7 @@ omit = homeassistant/components/sensor/pushbullet.py homeassistant/components/sensor/pvoutput.py homeassistant/components/sensor/pyload.py + homeassistant/components/sensor/qbittorrent.py homeassistant/components/sensor/qnap.py homeassistant/components/sensor/radarr.py homeassistant/components/sensor/rainbird.py diff --git a/homeassistant/components/sensor/qbittorrent.py b/homeassistant/components/sensor/qbittorrent.py new file mode 100644 index 000000000000..8718f3a9d744 --- /dev/null +++ b/homeassistant/components/sensor/qbittorrent.py @@ -0,0 +1,142 @@ +""" +Support for monitoring the qBittorrent API. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.qbittorrent/ +""" +import logging + +import voluptuous as vol + +from requests.exceptions import RequestException + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_NAME, CONF_PASSWORD, CONF_URL, CONF_USERNAME, STATE_IDLE) +from homeassistant.helpers.entity import Entity +import homeassistant.helpers.config_validation as cv +from homeassistant.exceptions import PlatformNotReady + +REQUIREMENTS = ['python-qbittorrent==0.3.1'] + +_LOGGER = logging.getLogger(__name__) + +SENSOR_TYPE_CURRENT_STATUS = 'current_status' +SENSOR_TYPE_DOWNLOAD_SPEED = 'download_speed' +SENSOR_TYPE_UPLOAD_SPEED = 'upload_speed' + +DEFAULT_NAME = 'qBittorrent' + +SENSOR_TYPES = { + SENSOR_TYPE_CURRENT_STATUS: ['Status', None], + SENSOR_TYPE_DOWNLOAD_SPEED: ['Down Speed', 'kB/s'], + SENSOR_TYPE_UPLOAD_SPEED: ['Up Speed', 'kB/s'], +} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_URL): cv.url, + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string +}) + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the qBittorrent sensors.""" + from qbittorrent.client import Client, LoginRequired + + try: + client = Client(config[CONF_URL]) + client.login(config[CONF_USERNAME], config[CONF_PASSWORD]) + except LoginRequired: + _LOGGER.error("Invalid authentication") + return + except RequestException: + _LOGGER.error("Connection failed") + raise PlatformNotReady + + name = config.get(CONF_NAME) + + dev = [] + for sensor_type in SENSOR_TYPES: + sensor = QBittorrentSensor(sensor_type, client, name, LoginRequired) + dev.append(sensor) + + async_add_entities(dev, True) + + +def format_speed(speed): + """Return a bytes/s measurement as a human readable string.""" + kb_spd = float(speed) / 1024 + return round(kb_spd, 2 if kb_spd < 0.1 else 1) + + +class QBittorrentSensor(Entity): + """Representation of an qBittorrent sensor.""" + + def __init__(self, sensor_type, qbittorrent_client, + client_name, exception): + """Initialize the qBittorrent sensor.""" + self._name = SENSOR_TYPES[sensor_type][0] + self.client = qbittorrent_client + self.type = sensor_type + self.client_name = client_name + self._state = None + self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] + self._available = False + self._exception = exception + + @property + def name(self): + """Return the name of the sensor.""" + return '{} {}'.format(self.client_name, self._name) + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def available(self): + """Return true if device is available.""" + return self._available + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity, if any.""" + return self._unit_of_measurement + + async def async_update(self): + """Get the latest data from qBittorrent and updates the state.""" + try: + data = self.client.sync() + self._available = True + except RequestException: + _LOGGER.error("Connection lost") + self._available = False + return + except self._exception: + _LOGGER.error("Invalid authentication") + return + + if data is None: + return + + 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' + elif upload > 0 and download == 0: + self._state = 'seeding' + elif upload == 0 and download > 0: + self._state = 'downloading' + else: + self._state = STATE_IDLE + + elif self.type == SENSOR_TYPE_DOWNLOAD_SPEED: + self._state = format_speed(download) + elif self.type == SENSOR_TYPE_UPLOAD_SPEED: + self._state = format_speed(upload) diff --git a/requirements_all.txt b/requirements_all.txt index 0672b553eed9..20ee4b368373 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1233,6 +1233,9 @@ python-nmap==0.6.1 # homeassistant.components.notify.pushover python-pushover==0.3 +# homeassistant.components.sensor.qbittorrent +python-qbittorrent==0.3.1 + # homeassistant.components.sensor.ripple python-ripple-api==0.0.3