From cf298c2435ecd15f02fe0c516aac94905523ae5c Mon Sep 17 00:00:00 2001 From: Hellowlol Date: Fri, 4 Aug 2017 23:24:55 +0200 Subject: [PATCH] Make HA discover sabnzbd and add it to the Configurator (#8634) * Init discover sab. * Fix hound errors Nobody likes being hound at :( * sabnzbd discovery says if ssl is active. * Fixups after codereview. --- homeassistant/components/discovery.py | 1 + homeassistant/components/sensor/sabnzbd.py | 129 +++++++++++++++++---- 2 files changed, 110 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 5e1dc8d6166..06e6f0b989a 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -59,6 +59,7 @@ SERVICE_HANDLERS = { 'frontier_silicon': ('media_player', 'frontier_silicon'), 'openhome': ('media_player', 'openhome'), 'harmony': ('remote', 'harmony'), + 'sabnzbd': ('sensor', 'sabnzbd'), 'bose_soundtouch': ('media_player', 'soundtouch'), 'bluesound': ('media_player', 'bluesound'), } diff --git a/homeassistant/components/sensor/sabnzbd.py b/homeassistant/components/sensor/sabnzbd.py index 9caa0b08374..dd02ce389f3 100644 --- a/homeassistant/components/sensor/sabnzbd.py +++ b/homeassistant/components/sensor/sabnzbd.py @@ -4,7 +4,9 @@ Support for monitoring an SABnzbd NZB client. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.sabnzbd/ """ +import os import logging +import json from datetime import timedelta import voluptuous as vol @@ -16,14 +18,17 @@ from homeassistant.const import ( from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv +from homeassistant.loader import get_component REQUIREMENTS = ['https://github.com/jamespcole/home-assistant-nzb-clients/' 'archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip' '#python-sabnzbd==0.1'] +_CONFIGURING = {} _LOGGER = logging.getLogger(__name__) _THROTTLED_REFRESH = None +CONFIG_FILE = 'sabnzbd.conf' DEFAULT_NAME = 'SABnzbd' DEFAULT_PORT = 8080 DEFAULT_SSL = False @@ -50,31 +55,24 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up the SABnzbd sensors.""" - from pysabnzbd import SabnzbdApi, SabnzbdApiException - - host = config.get(CONF_HOST) - port = config.get(CONF_PORT) - name = config.get(CONF_NAME) - api_key = config.get(CONF_API_KEY) - monitored_types = config.get(CONF_MONITORED_VARIABLES) - use_ssl = config.get(CONF_SSL) - - if use_ssl: - uri_scheme = 'https://' - else: - uri_scheme = 'http://' - - base_url = "{}{}:{}/".format(uri_scheme, host, port) - - sab_api = SabnzbdApi(base_url, api_key) +def _check_sabnzbd(sab_api, base_url, api_key): + """Check if we can reach SABnzbd.""" + from pysabnzbd import SabnzbdApiException + sab_api = sab_api(base_url, api_key) try: sab_api.check_available() except SabnzbdApiException: _LOGGER.error("Connection to SABnzbd API failed") return False + return True + + +def setup_sabnzbd(base_url, apikey, name, hass, config, add_devices, sab_api): + """Set up polling from SABnzbd and sensors.""" + sab_api = sab_api(base_url, apikey) + # Add minimal info to the front end + monitored = config.get(CONF_MONITORED_VARIABLES, ['current_status']) # pylint: disable=global-statement global _THROTTLED_REFRESH @@ -82,12 +80,84 @@ def setup_platform(hass, config, add_devices, discovery_info=None): MIN_TIME_BETWEEN_UPDATES)(sab_api.refresh_queue) devices = [] - for variable in monitored_types: + for variable in monitored: devices.append(SabnzbdSensor(variable, sab_api, name)) add_devices(devices) +def request_configuration(host, name, hass, config, add_devices, sab_api): + """Request configuration steps from the user.""" + configurator = get_component('configurator') + # We got an error if this method is called while we are configuring + if host in _CONFIGURING: + configurator.notify_errors(_CONFIGURING[host], + 'Failed to register, please try again.') + + return + + def sabnzbd_configuration_callback(data): + """Handle configuration changes.""" + api_key = data.get('api_key') + if _check_sabnzbd(sab_api, host, api_key): + setup_sabnzbd(host, api_key, name, + hass, config, add_devices, sab_api) + + def success(): + """Set up was successful.""" + conf = _read_config(hass) + conf[host] = {'api_key': api_key} + _write_config(hass, conf) + req_config = _CONFIGURING.pop(host) + hass.async_add_job(configurator.request_done, req_config) + + hass.async_add_job(success) + + _CONFIGURING[host] = configurator.request_config( + hass, + DEFAULT_NAME, + sabnzbd_configuration_callback, + description=('Enter the API Key'), + submit_caption='Confirm', + fields=[{ + 'id': 'api_key', + 'name': 'API Key', + 'type': ''}] + ) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the SABnzbd platform.""" + from pysabnzbd import SabnzbdApi + + host = config.get(CONF_HOST) or discovery_info.get(CONF_HOST) + port = config.get(CONF_PORT) or discovery_info.get(CONF_PORT) + name = config.get(CONF_NAME, DEFAULT_NAME) + use_ssl = DEFAULT_SSL + + if config.get(CONF_SSL): + use_ssl = True + elif discovery_info.get('properties', {}).get('https', '0') == '1': + use_ssl = True + + uri_scheme = 'https://' if use_ssl else 'http://' + base_url = "{}{}:{}/".format(uri_scheme, host, port) + api_key = config.get(CONF_API_KEY) + + if not api_key: + conf = _read_config(hass) + if conf.get(base_url, {}).get('api_key'): + api_key = conf[base_url]['api_key'] + + if not _check_sabnzbd(SabnzbdApi, base_url, api_key): + request_configuration(base_url, name, hass, config, + add_devices, SabnzbdApi) + return + + setup_sabnzbd(base_url, api_key, name, hass, + config, add_devices, SabnzbdApi) + + class SabnzbdSensor(Entity): """Representation of an SABnzbd sensor.""" @@ -145,3 +215,22 @@ class SabnzbdSensor(Entity): self._state = self.sabnzb_client.queue.get('diskspace1') else: self._state = 'Unknown' + + +def _read_config(hass): + """Read SABnzbd config.""" + path = hass.config.path(CONFIG_FILE) + + if not os.path.isfile(path): + return {} + + with open(path) as f_handle: + # Guard against empty file + return json.loads(f_handle.read() or '{}') + + +def _write_config(hass, config): + """Write SABnzbd config.""" + data = json.dumps(config) + with open(hass.config.path(CONFIG_FILE), 'w', encoding='utf-8') as outfile: + outfile.write(data)