diff --git a/.coveragerc b/.coveragerc index 7fa418f0b46033..465267b53d3469 100644 --- a/.coveragerc +++ b/.coveragerc @@ -348,6 +348,9 @@ omit = 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_call.py @@ -823,7 +826,6 @@ omit = homeassistant/components/sensor/time_date.py homeassistant/components/sensor/torque.py homeassistant/components/sensor/trafikverket_weatherstation.py - homeassistant/components/sensor/transmission.py homeassistant/components/sensor/travisci.py homeassistant/components/sensor/twitch.py homeassistant/components/sensor/uber.py @@ -867,7 +869,6 @@ omit = homeassistant/components/switch/switchmate.py homeassistant/components/switch/telnet.py homeassistant/components/switch/tplink.py - homeassistant/components/switch/transmission.py homeassistant/components/switch/vesync.py homeassistant/components/telegram_bot/* homeassistant/components/thingspeak.py diff --git a/homeassistant/components/sensor/transmission.py b/homeassistant/components/sensor/transmission.py index a669db0e5be887..efe32b07fc095e 100644 --- a/homeassistant/components/sensor/transmission.py +++ b/homeassistant/components/sensor/transmission.py @@ -4,76 +4,38 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.transmission/ """ -from datetime import timedelta import logging -import voluptuous as vol - -from homeassistant.components.sensor import PLATFORM_SCHEMA -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.components.transmission import ( + DATA_TRANSMISSION, SENSOR_TYPES, SCAN_INTERVAL) +from homeassistant.const import STATE_IDLE from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -from homeassistant.exceptions import PlatformNotReady -REQUIREMENTS = ['transmissionrpc==0.11'] +DEPENDENCIES = ['transmission'] _LOGGER = logging.getLogger(__name__) 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): """Set up the Transmission sensors.""" - import transmissionrpc - from transmissionrpc.error import TransmissionError - - name = config.get(CONF_NAME) - host = config.get(CONF_HOST) - username = config.get(CONF_USERNAME) - 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 + if discovery_info is None: + return + + transmission_api = hass.data[DATA_TRANSMISSION] + monitored_variables = discovery_info['sensors'] + name = discovery_info['client_name'] dev = [] - for variable in config[CONF_MONITORED_VARIABLES]: - dev.append(TransmissionSensor(variable, transmission_api, name)) + for sensor_type in monitored_variables: + dev.append(TransmissionSensor( + sensor_type, + transmission_api, + name, + SENSOR_TYPES[sensor_type][0], + SENSOR_TYPES[sensor_type][1])) add_entities(dev, True) @@ -81,12 +43,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class TransmissionSensor(Entity): """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.""" - self._name = SENSOR_TYPES[sensor_type][0] + self._name = sensor_name self._state = None 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.client_name = client_name self.type = sensor_type @@ -111,11 +79,17 @@ def available(self): """Could the device be accessed during the last update call.""" return self._transmission_api.available + @Throttle(SCAN_INTERVAL) def update(self): """Get the latest data from Transmission and updates the state.""" self._transmission_api.update() 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._data: upload = self._data.uploadSpeed @@ -146,25 +120,3 @@ def update(self): self._state = self._data.pausedTorrentCount elif self.type == 'total_torrents': 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") diff --git a/homeassistant/components/switch/transmission.py b/homeassistant/components/switch/transmission.py index 10ab0903dcf2cc..3ce3c7a98f9ce5 100644 --- a/homeassistant/components/switch/transmission.py +++ b/homeassistant/components/switch/transmission.py @@ -6,54 +6,30 @@ """ import logging -import voluptuous as vol - -from homeassistant.components.switch import PLATFORM_SCHEMA +from homeassistant.components.transmission import ( + DATA_TRANSMISSION, SCAN_INTERVAL) from homeassistant.const import ( - CONF_HOST, CONF_NAME, CONF_PORT, CONF_PASSWORD, CONF_USERNAME, STATE_OFF, - STATE_ON) + STATE_OFF, STATE_ON) 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__) 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): """Set up the Transmission switch.""" - import transmissionrpc - from transmissionrpc.error import TransmissionError - - name = config.get(CONF_NAME) - host = config.get(CONF_HOST) - username = config.get(CONF_USERNAME) - password = config.get(CONF_PASSWORD) - port = config.get(CONF_PORT) + if discovery_info is None: + return - try: - 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 + component_name = DATA_TRANSMISSION + transmission_api = hass.data[component_name] + name = discovery_info['client_name'] - add_entities([TransmissionSwitch(transmission_api, name)]) + add_entities([TransmissionSwitch(transmission_api, name)], True) class TransmissionSwitch(ToggleEntity): @@ -88,14 +64,15 @@ def is_on(self): def turn_on(self, **kwargs): """Turn the device 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): """Turn the device 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): """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 diff --git a/homeassistant/components/transmission.py b/homeassistant/components/transmission.py new file mode 100644 index 00000000000000..cdf55c8e049486 --- /dev/null +++ b/homeassistant/components/transmission.py @@ -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 diff --git a/requirements_all.txt b/requirements_all.txt index 59a29eb88b342e..1c1d89dfbee719 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1546,8 +1546,7 @@ tp-connected==0.0.4 # homeassistant.components.device_tracker.tplink tplink==0.2.1 -# homeassistant.components.sensor.transmission -# homeassistant.components.switch.transmission +# homeassistant.components.transmission transmissionrpc==0.11 # homeassistant.components.tuya