From 6df5e712f7ab7e4275c7eca09e8494641d5485c2 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Tue, 28 Nov 2017 08:13:30 +0100 Subject: [PATCH] Tellduslive update with support for auto config and Local api (#10435) * Add support for local api connection found in TellStick Znet Lite/Pro. Added auto discovery support for all TelldusLive devices, changed authentication method. Breaking change! Upgraded tellduslive dependency Update CODEOWNERS. * Close any open configurator when configuration done * Add support for Telldus Local API via config (#2) * Updated dependency (addresses issue raised by @rasmusbe in https://github.com/home-assistant/home-assistant/pull/10435#issuecomment-344719714) * Fix requested changes --- CODEOWNERS | 2 + homeassistant/components/discovery.py | 2 + .../www_static/images/logo_tellduslive.png | Bin 0 -> 7796 bytes homeassistant/components/tellduslive.py | 202 ++++++++++++++---- requirements_all.txt | 2 +- 5 files changed, 169 insertions(+), 39 deletions(-) create mode 100644 homeassistant/components/frontend/www_static/images/logo_tellduslive.png diff --git a/CODEOWNERS b/CODEOWNERS index 069edd6cce2..6fa130432f4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -74,6 +74,8 @@ homeassistant/components/tahoma.py @philklei homeassistant/components/*/tahoma.py @philklei homeassistant/components/tesla.py @zabuldon homeassistant/components/*/tesla.py @zabuldon +homeassistant/components/tellduslive.py @molobrakos @fredrike +homeassistant/components/*/tellduslive.py @molobrakos @fredrike homeassistant/components/*/tradfri.py @ggravlingen homeassistant/components/*/xiaomi_aqara.py @danielhiversen @syssi homeassistant/components/*/xiaomi_miio.py @rytilahti @syssi diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 6861c5bdc70..5d362f21cef 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -35,6 +35,7 @@ SERVICE_AXIS = 'axis' SERVICE_APPLE_TV = 'apple_tv' SERVICE_WINK = 'wink' SERVICE_XIAOMI_GW = 'xiaomi_gw' +SERVICE_TELLDUSLIVE = 'tellstick' SERVICE_HANDLERS = { SERVICE_HASS_IOS_APP: ('ios', None), @@ -46,6 +47,7 @@ SERVICE_HANDLERS = { SERVICE_APPLE_TV: ('apple_tv', None), SERVICE_WINK: ('wink', None), SERVICE_XIAOMI_GW: ('xiaomi_aqara', None), + SERVICE_TELLDUSLIVE: ('tellduslive', None), 'philips_hue': ('light', 'hue'), 'google_cast': ('media_player', 'cast'), 'panasonic_viera': ('media_player', 'panasonic_viera'), diff --git a/homeassistant/components/frontend/www_static/images/logo_tellduslive.png b/homeassistant/components/frontend/www_static/images/logo_tellduslive.png new file mode 100644 index 0000000000000000000000000000000000000000..7ea78f8ef3aad4d3cd982835c797693a68264c00 GIT binary patch literal 7796 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 literal 0 HcmV?d00001 diff --git a/homeassistant/components/tellduslive.py b/homeassistant/components/tellduslive.py index a0e1efbd75c..fa8916aca11 100644 --- a/homeassistant/components/tellduslive.py +++ b/homeassistant/components/tellduslive.py @@ -8,35 +8,41 @@ from datetime import datetime, timedelta import logging from homeassistant.const import ( - ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, EVENT_HOMEASSISTANT_START) + ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, + CONF_TOKEN, CONF_HOST, + EVENT_HOMEASSISTANT_START) from homeassistant.helpers import discovery +from homeassistant.components.discovery import SERVICE_TELLDUSLIVE import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.util.dt import utcnow +from homeassistant.util.json import load_json, save_json import voluptuous as vol +APPLICATION_NAME = 'Home Assistant' + DOMAIN = 'tellduslive' -REQUIREMENTS = ['tellduslive==0.3.4'] +REQUIREMENTS = ['tellduslive==0.10.3'] _LOGGER = logging.getLogger(__name__) -CONF_PUBLIC_KEY = 'public_key' -CONF_PRIVATE_KEY = 'private_key' -CONF_TOKEN = 'token' +TELLLDUS_CONFIG_FILE = 'tellduslive.conf' +KEY_CONFIG = 'tellduslive_config' + CONF_TOKEN_SECRET = 'token_secret' CONF_UPDATE_INTERVAL = 'update_interval' +PUBLIC_KEY = 'THUPUNECH5YEQA3RE6UYUPRUZ2DUGUGA' +NOT_SO_PRIVATE_KEY = 'PHES7U2RADREWAFEBUSTUBAWRASWUTUS' + MIN_UPDATE_INTERVAL = timedelta(seconds=5) DEFAULT_UPDATE_INTERVAL = timedelta(minutes=1) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_PUBLIC_KEY): cv.string, - vol.Required(CONF_PRIVATE_KEY): cv.string, - vol.Required(CONF_TOKEN): cv.string, - vol.Required(CONF_TOKEN_SECRET): cv.string, + vol.Optional(CONF_HOST): cv.string, vol.Optional(CONF_UPDATE_INTERVAL, default=DEFAULT_UPDATE_INTERVAL): ( vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL))) }), @@ -45,21 +51,156 @@ CONFIG_SCHEMA = vol.Schema({ ATTR_LAST_UPDATED = 'time_last_updated' +CONFIG_INSTRUCTIONS = """ +To link your TelldusLive account: -def setup(hass, config): +1. Click the link below + +2. Login to Telldus Live + +3. Authorize {app_name}. + +4. Click the Confirm button. + +[Link TelldusLive account]({auth_url}) +""" + + +def setup(hass, config, session=None): """Set up the Telldus Live component.""" - client = TelldusLiveClient(hass, config) + from tellduslive import Session, supports_local_api + config_filename = hass.config.path(TELLLDUS_CONFIG_FILE) + conf = load_json(config_filename) - if not client.validate_session(): + def request_configuration(host=None): + """Request TelldusLive authorization.""" + configurator = hass.components.configurator + hass.data.setdefault(KEY_CONFIG, {}) + data_key = host or DOMAIN + + # Configuration already in progress + if hass.data[KEY_CONFIG].get(data_key): + return + + _LOGGER.info('Configuring TelldusLive %s', + 'local client: {}'.format(host) if host else + 'cloud service') + + session = Session(public_key=PUBLIC_KEY, + private_key=NOT_SO_PRIVATE_KEY, + host=host, + application=APPLICATION_NAME) + + auth_url = session.authorize_url + if not auth_url: + _LOGGER.warning('Failed to retrieve authorization URL') + return + + _LOGGER.debug('Got authorization URL %s', auth_url) + + def configuration_callback(callback_data): + """Handle the submitted configuration.""" + session.authorize() + res = setup(hass, config, session) + if not res: + configurator.notify_errors( + hass.data[KEY_CONFIG].get(data_key), + 'Unable to connect.') + return + + conf.update( + {host: {CONF_HOST: host, + CONF_TOKEN: session.access_token}} if host else + {DOMAIN: {CONF_TOKEN: session.access_token, + CONF_TOKEN_SECRET: session.access_token_secret}}) + save_json(config_filename, conf) + # Close all open configurators: for now, we only support one + # tellstick device, and configuration via either cloud service + # or via local API, not both at the same time + for instance in hass.data[KEY_CONFIG].values(): + configurator.request_done(instance) + + hass.data[KEY_CONFIG][data_key] = \ + configurator.request_config( + 'TelldusLive ({})'.format( + 'LocalAPI' if host + else 'Cloud service'), + configuration_callback, + description=CONFIG_INSTRUCTIONS.format( + app_name=APPLICATION_NAME, + auth_url=auth_url), + submit_caption='Confirm', + entity_picture='/static/images/logo_tellduslive.png', + ) + + def tellstick_discovered(service, info): + """Run when a Tellstick is discovered.""" + _LOGGER.info('Discovered tellstick device') + + if DOMAIN in hass.data: + _LOGGER.debug('Tellstick already configured') + return + + host, device = info[:2] + + if not supports_local_api(device): + _LOGGER.debug('Tellstick does not support local API') + # Configure the cloud service + hass.async_add_job(request_configuration) + return + + _LOGGER.debug('Tellstick does support local API') + + # Ignore any known devices + if conf and host in conf: + _LOGGER.debug('Discovered already known device: %s', host) + return + + # Offer configuration of both live and local API + request_configuration() + request_configuration(host) + + discovery.listen(hass, SERVICE_TELLDUSLIVE, tellstick_discovered) + + if session: + _LOGGER.debug('Continuing setup configured by configurator') + elif conf and CONF_HOST in next(iter(conf.values())): + # For now, only one local device is supported + _LOGGER.debug('Using Local API pre-configured by configurator') + session = Session(**next(iter(conf.values()))) + elif DOMAIN in conf: + _LOGGER.debug('Using TelldusLive cloud service ' + 'pre-configured by configurator') + session = Session(PUBLIC_KEY, NOT_SO_PRIVATE_KEY, + application=APPLICATION_NAME, **conf[DOMAIN]) + elif config.get(DOMAIN): + _LOGGER.info('Found entry in configuration.yaml. ' + 'Requesting TelldusLive cloud service configuration') + request_configuration() + + if CONF_HOST in config.get(DOMAIN, {}): + _LOGGER.info('Found TelldusLive host entry in configuration.yaml. ' + 'Requesting Telldus Local API configuration') + request_configuration(config.get(DOMAIN).get(CONF_HOST)) + + return True + else: + _LOGGER.info('Tellstick discovered, awaiting discovery callback') + return True + + if not session.is_authorized: _LOGGER.error( - "Authentication Error: Please make sure you have configured your " - "keys that can be acquired from " - "https://api.telldus.com/keys/index") + 'Authentication Error') return False + client = TelldusLiveClient(hass, config, session) + hass.data[DOMAIN] = client - hass.bus.listen(EVENT_HOMEASSISTANT_START, client.update) + if session: + client.update() + else: + hass.bus.listen(EVENT_HOMEASSISTANT_START, client.update) return True @@ -67,36 +208,21 @@ def setup(hass, config): class TelldusLiveClient(object): """Get the latest data and update the states.""" - def __init__(self, hass, config): + def __init__(self, hass, config, session): """Initialize the Tellus data object.""" - from tellduslive import Client - - public_key = config[DOMAIN].get(CONF_PUBLIC_KEY) - private_key = config[DOMAIN].get(CONF_PRIVATE_KEY) - token = config[DOMAIN].get(CONF_TOKEN) - token_secret = config[DOMAIN].get(CONF_TOKEN_SECRET) - self.entities = [] self._hass = hass self._config = config - self._interval = config[DOMAIN].get(CONF_UPDATE_INTERVAL) + self._interval = config.get(DOMAIN, {}).get( + CONF_UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL) _LOGGER.debug('Update interval %s', self._interval) - - self._client = Client(public_key, - private_key, - token, - token_secret) - - def validate_session(self): - """Make a request to see if the session is valid.""" - response = self._client.request_user() - return response and 'email' in response + self._client = session def update(self, *args): """Periodically poll the servers for current state.""" - _LOGGER.debug("Updating") + _LOGGER.debug('Updating') try: self._sync() finally: @@ -106,7 +232,7 @@ class TelldusLiveClient(object): def _sync(self): """Update local list of devices.""" if not self._client.update(): - _LOGGER.warning("Failed request") + _LOGGER.warning('Failed request') def identify_device(device): """Find out what type of HA component to create.""" @@ -161,7 +287,7 @@ class TelldusLiveEntity(Entity): self._client = hass.data[DOMAIN] self._client.entities.append(self) self._name = self.device.name - _LOGGER.debug("Created device %s", self) + _LOGGER.debug('Created device %s', self) def changed(self): """Return the property of the device might have changed.""" diff --git a/requirements_all.txt b/requirements_all.txt index dcb48ea597e..76f84ea9ebe 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1064,7 +1064,7 @@ tellcore-net==0.1 tellcore-py==1.1.2 # homeassistant.components.tellduslive -tellduslive==0.3.4 +tellduslive==0.10.3 # homeassistant.components.sensor.temper temperusb==1.5.3