"""Support for monitoring the qBittorrent API.""" from __future__ import annotations import logging from qbittorrent.client import Client, LoginRequired from requests.exceptions import RequestException import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, SensorEntity, SensorEntityDescription, ) from homeassistant.const import ( CONF_NAME, CONF_PASSWORD, CONF_URL, CONF_USERNAME, DATA_RATE_KILOBYTES_PER_SECOND, STATE_IDLE, ) from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv _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: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=SENSOR_TYPE_CURRENT_STATUS, name="Status", ), SensorEntityDescription( key=SENSOR_TYPE_DOWNLOAD_SPEED, name="Down Speed", native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, ), SensorEntityDescription( key=SENSOR_TYPE_UPLOAD_SPEED, name="Up Speed", native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, ), ) 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, } ) 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]) except LoginRequired: _LOGGER.error("Invalid authentication") return except RequestException as err: _LOGGER.error("Connection failed") raise PlatformNotReady from err name = config.get(CONF_NAME) entities = [ QBittorrentSensor(description, client, name, LoginRequired) for description in SENSOR_TYPES ] add_entities(entities, 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(SensorEntity): """Representation of an qBittorrent sensor.""" def __init__( self, description: SensorEntityDescription, qbittorrent_client, client_name, exception, ): """Initialize the qBittorrent sensor.""" self.entity_description = description self.client = qbittorrent_client self._exception = exception self._attr_name = f"{client_name} {description.name}" self._attr_available = False def update(self): """Get the latest data from qBittorrent and updates the state.""" try: data = self.client.sync_main_data() self._attr_available = True except RequestException: _LOGGER.error("Connection lost") self._attr_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"] sensor_type = self.entity_description.key if sensor_type == SENSOR_TYPE_CURRENT_STATUS: if upload > 0 and download > 0: self._attr_native_value = "up_down" elif upload > 0 and download == 0: self._attr_native_value = "seeding" elif upload == 0 and download > 0: self._attr_native_value = "downloading" else: self._attr_native_value = STATE_IDLE elif sensor_type == SENSOR_TYPE_DOWNLOAD_SPEED: self._attr_native_value = format_speed(download) elif sensor_type == SENSOR_TYPE_UPLOAD_SPEED: self._attr_native_value = format_speed(upload)