New Transmission component (#19230)

* New Transmission component and interaction

First commit for New Transmission component and interaction

* Fix commit

* Fix commit

* Fix + Switch checkin

Fix according to failed build and request, first checkin for Turtle Mode Switch in Transmission, still have to figure it out why it's not working.

* Bugfixing

* Fix commit

Multiple fix

* Fix

* fix for missing config

* Update on requirements_all.txt

* Fix in requirements_all.txt

* Fix

* Fix for build

* fix

* Fix

* Fix (again)

* Fix

* Fix indentation

* Fix indentation

* Fix Throttle

* Update .coveragerc

* Fix import and coveragerc
This commit is contained in:
MatteGary 2019-01-29 10:27:26 +01:00 committed by Martin Hjelmare
parent 6ff4ea1126
commit e95c50c742
5 changed files with 238 additions and 121 deletions

View File

@ -394,6 +394,9 @@ omit =
homeassistant/components/tradfri.py homeassistant/components/tradfri.py
homeassistant/components/*/tradfri.py homeassistant/components/*/tradfri.py
homeassistant/components/transmission.py
homeassistant/components/*/transmission.py
homeassistant/components/notify/twilio_sms.py homeassistant/components/notify/twilio_sms.py
homeassistant/components/notify/twilio_call.py homeassistant/components/notify/twilio_call.py
@ -891,7 +894,6 @@ omit =
homeassistant/components/sensor/time_date.py homeassistant/components/sensor/time_date.py
homeassistant/components/sensor/torque.py homeassistant/components/sensor/torque.py
homeassistant/components/sensor/trafikverket_weatherstation.py homeassistant/components/sensor/trafikverket_weatherstation.py
homeassistant/components/sensor/transmission.py
homeassistant/components/sensor/travisci.py homeassistant/components/sensor/travisci.py
homeassistant/components/sensor/twitch.py homeassistant/components/sensor/twitch.py
homeassistant/components/sensor/uber.py homeassistant/components/sensor/uber.py
@ -936,7 +938,6 @@ omit =
homeassistant/components/switch/switchmate.py homeassistant/components/switch/switchmate.py
homeassistant/components/switch/telnet.py homeassistant/components/switch/telnet.py
homeassistant/components/switch/tplink.py homeassistant/components/switch/tplink.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/vesync.py homeassistant/components/switch/vesync.py
homeassistant/components/telegram_bot/* homeassistant/components/telegram_bot/*
homeassistant/components/thingspeak.py homeassistant/components/thingspeak.py

View File

@ -4,76 +4,38 @@ Support for monitoring the Transmission BitTorrent client API.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.transmission/ https://home-assistant.io/components/sensor.transmission/
""" """
from datetime import timedelta
import logging import logging
import voluptuous as vol from homeassistant.components.transmission import (
DATA_TRANSMISSION, SENSOR_TYPES, SCAN_INTERVAL)
from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import STATE_IDLE
from homeassistant.const import (
CONF_HOST, CONF_MONITORED_VARIABLES, CONF_NAME, CONF_PASSWORD, CONF_PORT,
CONF_USERNAME, STATE_IDLE)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle from homeassistant.util import Throttle
from homeassistant.exceptions import PlatformNotReady
REQUIREMENTS = ['transmissionrpc==0.11'] DEPENDENCIES = ['transmission']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Transmission' DEFAULT_NAME = 'Transmission'
DEFAULT_PORT = 9091
SENSOR_TYPES = {
'active_torrents': ['Active Torrents', None],
'current_status': ['Status', None],
'download_speed': ['Down Speed', 'MB/s'],
'paused_torrents': ['Paused Torrents', None],
'total_torrents': ['Total Torrents', None],
'upload_speed': ['Up Speed', 'MB/s'],
}
SCAN_INTERVAL = timedelta(minutes=2)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_MONITORED_VARIABLES, default=['torrents']):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_USERNAME): cv.string,
})
def setup_platform(hass, config, add_entities, discovery_info=None): def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Transmission sensors.""" """Set up the Transmission sensors."""
import transmissionrpc if discovery_info is None:
from transmissionrpc.error import TransmissionError return
name = config.get(CONF_NAME) transmission_api = hass.data[DATA_TRANSMISSION]
host = config.get(CONF_HOST) monitored_variables = discovery_info['sensors']
username = config.get(CONF_USERNAME) name = discovery_info['client_name']
password = config.get(CONF_PASSWORD)
port = config.get(CONF_PORT)
try:
transmission = transmissionrpc.Client(
host, port=port, user=username, password=password)
transmission_api = TransmissionData(transmission)
except TransmissionError as error:
if str(error).find("401: Unauthorized"):
_LOGGER.error("Credentials for Transmission client are not valid")
return
_LOGGER.warning(
"Unable to connect to Transmission client: %s:%s", host, port)
raise PlatformNotReady
dev = [] dev = []
for variable in config[CONF_MONITORED_VARIABLES]: for sensor_type in monitored_variables:
dev.append(TransmissionSensor(variable, transmission_api, name)) dev.append(TransmissionSensor(
sensor_type,
transmission_api,
name,
SENSOR_TYPES[sensor_type][0],
SENSOR_TYPES[sensor_type][1]))
add_entities(dev, True) add_entities(dev, True)
@ -81,12 +43,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
class TransmissionSensor(Entity): class TransmissionSensor(Entity):
"""Representation of a Transmission sensor.""" """Representation of a Transmission sensor."""
def __init__(self, sensor_type, transmission_api, client_name): def __init__(
self,
sensor_type,
transmission_api,
client_name,
sensor_name,
unit_of_measurement):
"""Initialize the sensor.""" """Initialize the sensor."""
self._name = SENSOR_TYPES[sensor_type][0] self._name = sensor_name
self._state = None self._state = None
self._transmission_api = transmission_api self._transmission_api = transmission_api
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] self._unit_of_measurement = unit_of_measurement
self._data = None self._data = None
self.client_name = client_name self.client_name = client_name
self.type = sensor_type self.type = sensor_type
@ -111,11 +79,17 @@ class TransmissionSensor(Entity):
"""Could the device be accessed during the last update call.""" """Could the device be accessed during the last update call."""
return self._transmission_api.available return self._transmission_api.available
@Throttle(SCAN_INTERVAL)
def update(self): def update(self):
"""Get the latest data from Transmission and updates the state.""" """Get the latest data from Transmission and updates the state."""
self._transmission_api.update() self._transmission_api.update()
self._data = self._transmission_api.data self._data = self._transmission_api.data
if self.type == 'completed_torrents':
self._state = self._transmission_api.get_completed_torrent_count()
elif self.type == 'started_torrents':
self._state = self._transmission_api.get_started_torrent_count()
if self.type == 'current_status': if self.type == 'current_status':
if self._data: if self._data:
upload = self._data.uploadSpeed upload = self._data.uploadSpeed
@ -146,25 +120,3 @@ class TransmissionSensor(Entity):
self._state = self._data.pausedTorrentCount self._state = self._data.pausedTorrentCount
elif self.type == 'total_torrents': elif self.type == 'total_torrents':
self._state = self._data.torrentCount self._state = self._data.torrentCount
class TransmissionData:
"""Get the latest data and update the states."""
def __init__(self, api):
"""Initialize the Transmission data object."""
self.data = None
self.available = True
self._api = api
@Throttle(SCAN_INTERVAL)
def update(self):
"""Get the latest data from Transmission instance."""
from transmissionrpc.error import TransmissionError
try:
self.data = self._api.session_stats()
self.available = True
except TransmissionError:
self.available = False
_LOGGER.error("Unable to connect to Transmission client")

View File

@ -6,54 +6,30 @@ https://home-assistant.io/components/switch.transmission/
""" """
import logging import logging
import voluptuous as vol from homeassistant.components.transmission import (
DATA_TRANSMISSION, SCAN_INTERVAL)
from homeassistant.components.switch import PLATFORM_SCHEMA
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PORT, CONF_PASSWORD, CONF_USERNAME, STATE_OFF, STATE_OFF, STATE_ON)
STATE_ON)
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import ToggleEntity
import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle
REQUIREMENTS = ['transmissionrpc==0.11'] DEPENDENCIES = ['transmission']
_LOGGING = logging.getLogger(__name__) _LOGGING = logging.getLogger(__name__)
DEFAULT_NAME = 'Transmission Turtle Mode' DEFAULT_NAME = 'Transmission Turtle Mode'
DEFAULT_PORT = 9091
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_USERNAME): cv.string,
})
def setup_platform(hass, config, add_entities, discovery_info=None): def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Transmission switch.""" """Set up the Transmission switch."""
import transmissionrpc if discovery_info is None:
from transmissionrpc.error import TransmissionError return
name = config.get(CONF_NAME) component_name = DATA_TRANSMISSION
host = config.get(CONF_HOST) transmission_api = hass.data[component_name]
username = config.get(CONF_USERNAME) name = discovery_info['client_name']
password = config.get(CONF_PASSWORD)
port = config.get(CONF_PORT)
try: add_entities([TransmissionSwitch(transmission_api, name)], True)
transmission_api = transmissionrpc.Client(
host, port=port, user=username, password=password)
transmission_api.session_stats()
except TransmissionError as error:
_LOGGING.error(
"Connection to Transmission API failed on %s:%s with message %s",
host, port, error.original
)
return False
add_entities([TransmissionSwitch(transmission_api, name)])
class TransmissionSwitch(ToggleEntity): class TransmissionSwitch(ToggleEntity):
@ -88,14 +64,15 @@ class TransmissionSwitch(ToggleEntity):
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Turn the device on.""" """Turn the device on."""
_LOGGING.debug("Turning Turtle Mode of Transmission on") _LOGGING.debug("Turning Turtle Mode of Transmission on")
self.transmission_client.set_session(alt_speed_enabled=True) self.transmission_client.set_alt_speed_enabled(True)
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the device off.""" """Turn the device off."""
_LOGGING.debug("Turning Turtle Mode of Transmission off") _LOGGING.debug("Turning Turtle Mode of Transmission off")
self.transmission_client.set_session(alt_speed_enabled=False) self.transmission_client.set_alt_speed_enabled(False)
@Throttle(SCAN_INTERVAL)
def update(self): def update(self):
"""Get the latest data from Transmission and updates the state.""" """Get the latest data from Transmission and updates the state."""
active = self.transmission_client.get_session().alt_speed_enabled active = self.transmission_client.get_alt_speed_enabled()
self._state = STATE_ON if active else STATE_OFF self._state = STATE_ON if active else STATE_OFF

View File

@ -0,0 +1,188 @@
"""
Component for monitoring the Transmission BitTorrent client API.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/transmission/
"""
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.const import (
CONF_HOST,
CONF_MONITORED_CONDITIONS,
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_USERNAME
)
from homeassistant.helpers import discovery, config_validation as cv
from homeassistant.helpers.event import track_time_interval
REQUIREMENTS = ['transmissionrpc==0.11']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'transmission'
DATA_TRANSMISSION = 'data_transmission'
DEFAULT_NAME = 'Transmission'
DEFAULT_PORT = 9091
TURTLE_MODE = 'turtle_mode'
SENSOR_TYPES = {
'active_torrents': ['Active Torrents', None],
'current_status': ['Status', None],
'download_speed': ['Down Speed', 'MB/s'],
'paused_torrents': ['Paused Torrents', None],
'total_torrents': ['Total Torrents', None],
'upload_speed': ['Up Speed', 'MB/s'],
'completed_torrents': ['Completed Torrents', None],
'started_torrents': ['Started Torrents', None],
}
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(TURTLE_MODE, default=False): cv.boolean,
vol.Optional(CONF_MONITORED_CONDITIONS, default=['current_status']):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
})
}, extra=vol.ALLOW_EXTRA)
SCAN_INTERVAL = timedelta(minutes=2)
def setup(hass, config):
"""Set up the Transmission Component."""
host = config[DOMAIN][CONF_HOST]
username = config[DOMAIN][CONF_USERNAME]
password = config[DOMAIN][CONF_PASSWORD]
port = config[DOMAIN][CONF_PORT]
import transmissionrpc
from transmissionrpc.error import TransmissionError
try:
api = transmissionrpc.Client(
host, port=port, user=username, password=password)
api.session_stats()
except TransmissionError as error:
if str(error).find("401: Unauthorized"):
_LOGGER.error("Credentials for"
" Transmission client are not valid")
return False
tm_data = hass.data[DATA_TRANSMISSION] = TransmissionData(
hass, config, api)
tm_data.init_torrent_list()
def refresh(event_time):
"""Get the latest data from Transmission."""
tm_data.update()
track_time_interval(hass, refresh, SCAN_INTERVAL)
sensorconfig = {
'sensors': config[DOMAIN][CONF_MONITORED_CONDITIONS],
'client_name': config[DOMAIN][CONF_NAME]}
discovery.load_platform(hass, 'sensor', DOMAIN, sensorconfig, config)
if config[DOMAIN][TURTLE_MODE]:
discovery.load_platform(hass, 'switch', DOMAIN, sensorconfig, config)
return True
class TransmissionData:
"""Get the latest data and update the states."""
def __init__(self, hass, config, api):
"""Initialize the Transmission RPC API."""
self.data = None
self.torrents = None
self.session = None
self.available = True
self._api = api
self.completed_torrents = []
self.started_torrents = []
self.hass = hass
def update(self):
"""Get the latest data from Transmission instance."""
from transmissionrpc.error import TransmissionError
try:
self.data = self._api.session_stats()
self.torrents = self._api.get_torrents()
self.session = self._api.get_session()
self.check_completed_torrent()
self.check_started_torrent()
_LOGGER.debug("Torrent Data updated")
self.available = True
except TransmissionError:
self.available = False
_LOGGER.error("Unable to connect to Transmission client")
def init_torrent_list(self):
"""Initialize torrent lists."""
self.torrents = self._api.get_torrents()
self.completed_torrents = [
x.name for x in self.torrents if x.status == "seeding"]
self.started_torrents = [
x.name for x in self.torrents if x.status == "downloading"]
def check_completed_torrent(self):
"""Get completed torrent functionality."""
actual_torrents = self.torrents
actual_completed_torrents = [
var.name for var in actual_torrents if var.status == "seeding"]
tmp_completed_torrents = list(
set(actual_completed_torrents).difference(
self.completed_torrents))
for var in tmp_completed_torrents:
self.hass.bus.fire(
'transmission_downloaded_torrent', {
'name': var})
self.completed_torrents = actual_completed_torrents
def check_started_torrent(self):
"""Get started torrent functionality."""
actual_torrents = self.torrents
actual_started_torrents = [
var.name for var
in actual_torrents if var.status == "downloading"]
tmp_started_torrents = list(
set(actual_started_torrents).difference(
self.started_torrents))
for var in tmp_started_torrents:
self.hass.bus.fire(
'transmission_started_torrent', {
'name': var})
self.started_torrents = actual_started_torrents
def get_started_torrent_count(self):
"""Get the number of started torrents."""
return len(self.started_torrents)
def get_completed_torrent_count(self):
"""Get the number of completed torrents."""
return len(self.completed_torrents)
def set_alt_speed_enabled(self, is_enabled):
"""Set the alternative speed flag."""
self._api.set_session(alt_speed_enabled=is_enabled)
def get_alt_speed_enabled(self):
"""Get the alternative speed flag."""
return self.session.alt_speed_enabled

View File

@ -1654,8 +1654,7 @@ tp-connected==0.0.4
# homeassistant.components.device_tracker.tplink # homeassistant.components.device_tracker.tplink
tplink==0.2.1 tplink==0.2.1
# homeassistant.components.sensor.transmission # homeassistant.components.transmission
# homeassistant.components.switch.transmission
transmissionrpc==0.11 transmissionrpc==0.11
# homeassistant.components.tuya # homeassistant.components.tuya