Add support for Automatic OAuth2 authentication (#8962)
* Add support for Automatic OAuth2 authentication * Fix async conversion of configurator * Rename method for async * Use hass.components to get configurator component * Fix typo * Move session data to hidden directory * Make configurator callback optional
This commit is contained in:
parent
8fcec03adf
commit
19d1d748d4
25 changed files with 314 additions and 149 deletions
|
@ -91,7 +91,7 @@ def request_configuration(hass, config, atv, credentials):
|
||||||
hass.async_add_job(configurator.request_done, instance)
|
hass.async_add_job(configurator.request_done, instance)
|
||||||
|
|
||||||
instance = configurator.request_config(
|
instance = configurator.request_config(
|
||||||
hass, 'Apple TV Authentication', configuration_callback,
|
'Apple TV Authentication', configuration_callback,
|
||||||
description='Please enter PIN code shown on screen.',
|
description='Please enter PIN code shown on screen.',
|
||||||
submit_caption='Confirm',
|
submit_caption='Confirm',
|
||||||
fields=[{'id': 'pin', 'name': 'PIN Code', 'type': 'password'}]
|
fields=[{'id': 'pin', 'name': 'PIN Code', 'type': 'password'}]
|
||||||
|
|
|
@ -110,7 +110,7 @@ def request_configuration(hass, name, host, serialnumber):
|
||||||
|
|
||||||
title = '{} ({})'.format(name, host)
|
title = '{} ({})'.format(name, host)
|
||||||
request_id = configurator.request_config(
|
request_id = configurator.request_config(
|
||||||
hass, title, configuration_callback,
|
title, configuration_callback,
|
||||||
description='Functionality: ' + str(AXIS_INCLUDE),
|
description='Functionality: ' + str(AXIS_INCLUDE),
|
||||||
entity_picture="/static/images/logo_axis.png",
|
entity_picture="/static/images/logo_axis.png",
|
||||||
link_name='Axis platform documentation',
|
link_name='Axis platform documentation',
|
||||||
|
|
|
@ -7,19 +7,21 @@ A callback has to be provided to `request_config` which will be called when
|
||||||
the user has submitted configuration information.
|
the user has submitted configuration information.
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import functools as ft
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.core import callback as async_callback
|
from homeassistant.core import callback as async_callback
|
||||||
from homeassistant.const import EVENT_TIME_CHANGED, ATTR_FRIENDLY_NAME, \
|
from homeassistant.const import EVENT_TIME_CHANGED, ATTR_FRIENDLY_NAME, \
|
||||||
ATTR_ENTITY_PICTURE
|
ATTR_ENTITY_PICTURE
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.helpers.entity import generate_entity_id
|
from homeassistant.helpers.entity import async_generate_entity_id
|
||||||
from homeassistant.util.async import run_callback_threadsafe
|
from homeassistant.util.async import run_callback_threadsafe
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_REQUESTS = {}
|
|
||||||
_KEY_INSTANCE = 'configurator'
|
_KEY_INSTANCE = 'configurator'
|
||||||
|
|
||||||
|
DATA_REQUESTS = 'configurator_requests'
|
||||||
|
|
||||||
ATTR_CONFIGURE_ID = 'configure_id'
|
ATTR_CONFIGURE_ID = 'configure_id'
|
||||||
ATTR_DESCRIPTION = 'description'
|
ATTR_DESCRIPTION = 'description'
|
||||||
ATTR_DESCRIPTION_IMAGE = 'description_image'
|
ATTR_DESCRIPTION_IMAGE = 'description_image'
|
||||||
|
@ -39,63 +41,89 @@ STATE_CONFIGURED = 'configured'
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def request_config(
|
@async_callback
|
||||||
hass, name, callback, description=None, description_image=None,
|
def async_request_config(
|
||||||
|
hass, name, callback=None, description=None, description_image=None,
|
||||||
submit_caption=None, fields=None, link_name=None, link_url=None,
|
submit_caption=None, fields=None, link_name=None, link_url=None,
|
||||||
entity_picture=None):
|
entity_picture=None):
|
||||||
"""Create a new request for configuration.
|
"""Create a new request for configuration.
|
||||||
|
|
||||||
Will return an ID to be used for sequent calls.
|
Will return an ID to be used for sequent calls.
|
||||||
"""
|
"""
|
||||||
instance = run_callback_threadsafe(hass.loop,
|
instance = hass.data.get(_KEY_INSTANCE)
|
||||||
_async_get_instance,
|
|
||||||
hass).result()
|
|
||||||
|
|
||||||
request_id = instance.request_config(
|
if instance is None:
|
||||||
|
instance = hass.data[_KEY_INSTANCE] = Configurator(hass)
|
||||||
|
|
||||||
|
request_id = instance.async_request_config(
|
||||||
name, callback,
|
name, callback,
|
||||||
description, description_image, submit_caption,
|
description, description_image, submit_caption,
|
||||||
fields, link_name, link_url, entity_picture)
|
fields, link_name, link_url, entity_picture)
|
||||||
|
|
||||||
_REQUESTS[request_id] = instance
|
if DATA_REQUESTS not in hass.data:
|
||||||
|
hass.data[DATA_REQUESTS] = {}
|
||||||
|
|
||||||
|
hass.data[DATA_REQUESTS][request_id] = instance
|
||||||
|
|
||||||
return request_id
|
return request_id
|
||||||
|
|
||||||
|
|
||||||
def notify_errors(request_id, error):
|
@bind_hass
|
||||||
|
def request_config(hass, *args, **kwargs):
|
||||||
|
"""Create a new request for configuration.
|
||||||
|
|
||||||
|
Will return an ID to be used for sequent calls.
|
||||||
|
"""
|
||||||
|
return run_callback_threadsafe(
|
||||||
|
hass.loop, ft.partial(async_request_config, hass, *args, **kwargs)
|
||||||
|
).result()
|
||||||
|
|
||||||
|
|
||||||
|
@bind_hass
|
||||||
|
@async_callback
|
||||||
|
def async_notify_errors(hass, request_id, error):
|
||||||
"""Add errors to a config request."""
|
"""Add errors to a config request."""
|
||||||
try:
|
try:
|
||||||
_REQUESTS[request_id].notify_errors(request_id, error)
|
hass.data[DATA_REQUESTS][request_id].async_notify_errors(
|
||||||
|
request_id, error)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# If request_id does not exist
|
# If request_id does not exist
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def request_done(request_id):
|
@bind_hass
|
||||||
|
def notify_errors(hass, request_id, error):
|
||||||
|
"""Add errors to a config request."""
|
||||||
|
return run_callback_threadsafe(
|
||||||
|
hass.loop, async_notify_errors, hass, request_id, error
|
||||||
|
).result()
|
||||||
|
|
||||||
|
|
||||||
|
@bind_hass
|
||||||
|
@async_callback
|
||||||
|
def async_request_done(hass, request_id):
|
||||||
"""Mark a configuration request as done."""
|
"""Mark a configuration request as done."""
|
||||||
try:
|
try:
|
||||||
_REQUESTS.pop(request_id).request_done(request_id)
|
hass.data[DATA_REQUESTS].pop(request_id).async_request_done(request_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# If request_id does not exist
|
# If request_id does not exist
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@bind_hass
|
||||||
|
def request_done(hass, request_id):
|
||||||
|
"""Mark a configuration request as done."""
|
||||||
|
return run_callback_threadsafe(
|
||||||
|
hass.loop, async_request_done, hass, request_id
|
||||||
|
).result()
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_setup(hass, config):
|
def async_setup(hass, config):
|
||||||
"""Set up the configurator component."""
|
"""Set up the configurator component."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@async_callback
|
|
||||||
def _async_get_instance(hass):
|
|
||||||
"""Get an instance per hass object."""
|
|
||||||
instance = hass.data.get(_KEY_INSTANCE)
|
|
||||||
|
|
||||||
if instance is None:
|
|
||||||
instance = hass.data[_KEY_INSTANCE] = Configurator(hass)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
class Configurator(object):
|
class Configurator(object):
|
||||||
"""The class to keep track of current configuration requests."""
|
"""The class to keep track of current configuration requests."""
|
||||||
|
|
||||||
|
@ -105,14 +133,16 @@ class Configurator(object):
|
||||||
self._cur_id = 0
|
self._cur_id = 0
|
||||||
self._requests = {}
|
self._requests = {}
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN, SERVICE_CONFIGURE, self.handle_service_call)
|
DOMAIN, SERVICE_CONFIGURE, self.async_handle_service_call)
|
||||||
|
|
||||||
def request_config(
|
@async_callback
|
||||||
|
def async_request_config(
|
||||||
self, name, callback,
|
self, name, callback,
|
||||||
description, description_image, submit_caption,
|
description, description_image, submit_caption,
|
||||||
fields, link_name, link_url, entity_picture):
|
fields, link_name, link_url, entity_picture):
|
||||||
"""Set up a request for configuration."""
|
"""Set up a request for configuration."""
|
||||||
entity_id = generate_entity_id(ENTITY_ID_FORMAT, name, hass=self.hass)
|
entity_id = async_generate_entity_id(
|
||||||
|
ENTITY_ID_FORMAT, name, hass=self.hass)
|
||||||
|
|
||||||
if fields is None:
|
if fields is None:
|
||||||
fields = []
|
fields = []
|
||||||
|
@ -138,11 +168,12 @@ class Configurator(object):
|
||||||
] if value is not None
|
] if value is not None
|
||||||
})
|
})
|
||||||
|
|
||||||
self.hass.states.set(entity_id, STATE_CONFIGURE, data)
|
self.hass.states.async_set(entity_id, STATE_CONFIGURE, data)
|
||||||
|
|
||||||
return request_id
|
return request_id
|
||||||
|
|
||||||
def notify_errors(self, request_id, error):
|
@async_callback
|
||||||
|
def async_notify_errors(self, request_id, error):
|
||||||
"""Update the state with errors."""
|
"""Update the state with errors."""
|
||||||
if not self._validate_request_id(request_id):
|
if not self._validate_request_id(request_id):
|
||||||
return
|
return
|
||||||
|
@ -154,9 +185,10 @@ class Configurator(object):
|
||||||
new_data = dict(state.attributes)
|
new_data = dict(state.attributes)
|
||||||
new_data[ATTR_ERRORS] = error
|
new_data[ATTR_ERRORS] = error
|
||||||
|
|
||||||
self.hass.states.set(entity_id, STATE_CONFIGURE, new_data)
|
self.hass.states.async_set(entity_id, STATE_CONFIGURE, new_data)
|
||||||
|
|
||||||
def request_done(self, request_id):
|
@async_callback
|
||||||
|
def async_request_done(self, request_id):
|
||||||
"""Remove the configuration request."""
|
"""Remove the configuration request."""
|
||||||
if not self._validate_request_id(request_id):
|
if not self._validate_request_id(request_id):
|
||||||
return
|
return
|
||||||
|
@ -167,15 +199,16 @@ class Configurator(object):
|
||||||
# the result fo the service call (current design limitation).
|
# the result fo the service call (current design limitation).
|
||||||
# Instead, we will set it to configured to give as feedback but delete
|
# Instead, we will set it to configured to give as feedback but delete
|
||||||
# it shortly after so that it is deleted when the client updates.
|
# it shortly after so that it is deleted when the client updates.
|
||||||
self.hass.states.set(entity_id, STATE_CONFIGURED)
|
self.hass.states.async_set(entity_id, STATE_CONFIGURED)
|
||||||
|
|
||||||
def deferred_remove(event):
|
def deferred_remove(event):
|
||||||
"""Remove the request state."""
|
"""Remove the request state."""
|
||||||
self.hass.states.remove(entity_id)
|
self.hass.states.async_remove(entity_id)
|
||||||
|
|
||||||
self.hass.bus.listen_once(EVENT_TIME_CHANGED, deferred_remove)
|
self.hass.bus.async_listen_once(EVENT_TIME_CHANGED, deferred_remove)
|
||||||
|
|
||||||
def handle_service_call(self, call):
|
@async_callback
|
||||||
|
def async_handle_service_call(self, call):
|
||||||
"""Handle a configure service call."""
|
"""Handle a configure service call."""
|
||||||
request_id = call.data.get(ATTR_CONFIGURE_ID)
|
request_id = call.data.get(ATTR_CONFIGURE_ID)
|
||||||
|
|
||||||
|
@ -186,8 +219,8 @@ class Configurator(object):
|
||||||
entity_id, fields, callback = self._requests[request_id]
|
entity_id, fields, callback = self._requests[request_id]
|
||||||
|
|
||||||
# field validation goes here?
|
# field validation goes here?
|
||||||
|
if callback:
|
||||||
self.hass.async_add_job(callback, call.data.get(ATTR_FIELDS, {}))
|
self.hass.async_add_job(callback, call.data.get(ATTR_FIELDS, {}))
|
||||||
|
|
||||||
def _generate_unique_id(self):
|
def _generate_unique_id(self):
|
||||||
"""Generate a unique configurator ID."""
|
"""Generate a unique configurator ID."""
|
||||||
|
|
|
@ -6,28 +6,33 @@ https://home-assistant.io/components/device_tracker.automatic/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.device_tracker import (
|
from homeassistant.components.device_tracker import (
|
||||||
PLATFORM_SCHEMA, ATTR_ATTRIBUTES, ATTR_DEV_ID, ATTR_HOST_NAME, ATTR_MAC,
|
PLATFORM_SCHEMA, ATTR_ATTRIBUTES, ATTR_DEV_ID, ATTR_HOST_NAME, ATTR_MAC,
|
||||||
ATTR_GPS, ATTR_GPS_ACCURACY)
|
ATTR_GPS, ATTR_GPS_ACCURACY)
|
||||||
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_USERNAME, CONF_PASSWORD, EVENT_HOMEASSISTANT_STOP,
|
CONF_USERNAME, CONF_PASSWORD, EVENT_HOMEASSISTANT_STOP)
|
||||||
EVENT_HOMEASSISTANT_START)
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
|
||||||
REQUIREMENTS = ['aioautomatic==0.4.0']
|
REQUIREMENTS = ['aioautomatic==0.5.0']
|
||||||
|
DEPENDENCIES = ['http']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF_CLIENT_ID = 'client_id'
|
CONF_CLIENT_ID = 'client_id'
|
||||||
CONF_SECRET = 'secret'
|
CONF_SECRET = 'secret'
|
||||||
CONF_DEVICES = 'devices'
|
CONF_DEVICES = 'devices'
|
||||||
|
CONF_CURRENT_LOCATION = 'current_location'
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 5
|
DEFAULT_TIMEOUT = 5
|
||||||
|
|
||||||
|
@ -38,38 +43,76 @@ ATTR_FUEL_LEVEL = 'fuel_level'
|
||||||
|
|
||||||
EVENT_AUTOMATIC_UPDATE = 'automatic_update'
|
EVENT_AUTOMATIC_UPDATE = 'automatic_update'
|
||||||
|
|
||||||
|
AUTOMATIC_CONFIG_FILE = '.automatic/session-{}.json'
|
||||||
|
|
||||||
|
DATA_CONFIGURING = 'automatic_configurator_clients'
|
||||||
|
DATA_REFRESH_TOKEN = 'refresh_token'
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Required(CONF_CLIENT_ID): cv.string,
|
vol.Required(CONF_CLIENT_ID): cv.string,
|
||||||
vol.Required(CONF_SECRET): cv.string,
|
vol.Required(CONF_SECRET): cv.string,
|
||||||
vol.Required(CONF_USERNAME): cv.string,
|
vol.Inclusive(CONF_USERNAME, 'auth'): cv.string,
|
||||||
vol.Required(CONF_PASSWORD): cv.string,
|
vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string,
|
||||||
|
vol.Optional(CONF_CURRENT_LOCATION, default=False): cv.boolean,
|
||||||
vol.Optional(CONF_DEVICES, default=None): vol.All(
|
vol.Optional(CONF_DEVICES, default=None): vol.All(
|
||||||
cv.ensure_list, [cv.string])
|
cv.ensure_list, [cv.string])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def _get_refresh_token_from_file(hass, filename):
|
||||||
|
"""Attempt to load session data from file."""
|
||||||
|
path = hass.config.path(filename)
|
||||||
|
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path) as data_file:
|
||||||
|
data = json.load(data_file)
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return data.get(DATA_REFRESH_TOKEN)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _write_refresh_token_to_file(hass, filename, refresh_token):
|
||||||
|
"""Attempt to store session data to file."""
|
||||||
|
path = hass.config.path(filename)
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
|
with open(path, 'w+') as data_file:
|
||||||
|
json.dump({
|
||||||
|
DATA_REFRESH_TOKEN: refresh_token
|
||||||
|
}, data_file)
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
||||||
"""Validate the configuration and return an Automatic scanner."""
|
"""Validate the configuration and return an Automatic scanner."""
|
||||||
import aioautomatic
|
import aioautomatic
|
||||||
|
|
||||||
|
hass.http.register_view(AutomaticAuthCallbackView())
|
||||||
|
|
||||||
|
scope = FULL_SCOPE if config.get(CONF_CURRENT_LOCATION) else DEFAULT_SCOPE
|
||||||
|
|
||||||
client = aioautomatic.Client(
|
client = aioautomatic.Client(
|
||||||
client_id=config[CONF_CLIENT_ID],
|
client_id=config[CONF_CLIENT_ID],
|
||||||
client_secret=config[CONF_SECRET],
|
client_secret=config[CONF_SECRET],
|
||||||
client_session=async_get_clientsession(hass),
|
client_session=async_get_clientsession(hass),
|
||||||
request_kwargs={'timeout': DEFAULT_TIMEOUT})
|
request_kwargs={'timeout': DEFAULT_TIMEOUT})
|
||||||
try:
|
|
||||||
try:
|
|
||||||
session = yield from client.create_session_from_password(
|
|
||||||
FULL_SCOPE, config[CONF_USERNAME], config[CONF_PASSWORD])
|
|
||||||
except aioautomatic.exceptions.ForbiddenError as exc:
|
|
||||||
if not str(exc).startswith("invalid_scope"):
|
|
||||||
raise exc
|
|
||||||
_LOGGER.info("Client not authorized for current_location scope. "
|
|
||||||
"location:updated events will not be received.")
|
|
||||||
session = yield from client.create_session_from_password(
|
|
||||||
DEFAULT_SCOPE, config[CONF_USERNAME], config[CONF_PASSWORD])
|
|
||||||
|
|
||||||
|
filename = AUTOMATIC_CONFIG_FILE.format(config[CONF_CLIENT_ID])
|
||||||
|
refresh_token = yield from hass.async_add_job(
|
||||||
|
_get_refresh_token_from_file, hass, filename)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def initialize_data(session):
|
||||||
|
"""Initialize the AutomaticData object from the created session."""
|
||||||
|
hass.async_add_job(
|
||||||
|
_write_refresh_token_to_file, hass, filename,
|
||||||
|
session.refresh_token)
|
||||||
data = AutomaticData(
|
data = AutomaticData(
|
||||||
hass, client, session, config[CONF_DEVICES], async_see)
|
hass, client, session, config[CONF_DEVICES], async_see)
|
||||||
|
|
||||||
|
@ -77,26 +120,105 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
||||||
vehicles = yield from session.get_vehicles()
|
vehicles = yield from session.get_vehicles()
|
||||||
for vehicle in vehicles:
|
for vehicle in vehicles:
|
||||||
hass.async_add_job(data.load_vehicle(vehicle))
|
hass.async_add_job(data.load_vehicle(vehicle))
|
||||||
except aioautomatic.exceptions.AutomaticError as err:
|
|
||||||
_LOGGER.error(str(err))
|
|
||||||
return False
|
|
||||||
|
|
||||||
@callback
|
# Create a task instead of adding a tracking job, since this task will
|
||||||
def ws_connect(event):
|
# run until the websocket connection is closed.
|
||||||
"""Open the websocket connection."""
|
hass.loop.create_task(data.ws_connect())
|
||||||
hass.async_add_job(data.ws_connect())
|
|
||||||
|
|
||||||
@callback
|
if refresh_token is not None:
|
||||||
def ws_close(event):
|
try:
|
||||||
"""Close the websocket connection."""
|
session = yield from client.create_session_from_refresh_token(
|
||||||
hass.async_add_job(data.ws_close())
|
refresh_token)
|
||||||
|
yield from initialize_data(session)
|
||||||
|
return True
|
||||||
|
except aioautomatic.exceptions.BadRequestError as err:
|
||||||
|
if str(err) == 'err_invalid_refresh_token':
|
||||||
|
_LOGGER.error("Stored refresh token was invalid.")
|
||||||
|
yield from hass.async_add_job(
|
||||||
|
_write_refresh_token_to_file, hass, filename, None)
|
||||||
|
else:
|
||||||
|
_LOGGER.error(str(err))
|
||||||
|
return False
|
||||||
|
except aioautomatic.exceptions.AutomaticError as err:
|
||||||
|
_LOGGER.error(str(err))
|
||||||
|
return False
|
||||||
|
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, ws_connect)
|
if CONF_USERNAME in config:
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, ws_close)
|
try:
|
||||||
|
session = yield from client.create_session_from_password(
|
||||||
|
scope, config[CONF_USERNAME], config[CONF_PASSWORD])
|
||||||
|
yield from initialize_data(session)
|
||||||
|
return True
|
||||||
|
except aioautomatic.exceptions.AutomaticError as err:
|
||||||
|
_LOGGER.error(str(err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
configurator = hass.components.configurator
|
||||||
|
request_id = configurator.async_request_config(
|
||||||
|
"Automatic", description=(
|
||||||
|
"Authorization required for Automatic device tracker."),
|
||||||
|
link_name="Click here to authorize Home Assistant.",
|
||||||
|
link_url=client.generate_oauth_url(scope),
|
||||||
|
entity_picture="/static/images/logo_automatic.png",
|
||||||
|
)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def initialize_callback(code, state):
|
||||||
|
"""Callback after OAuth2 response is returned."""
|
||||||
|
try:
|
||||||
|
session = yield from client.create_session_from_oauth_code(
|
||||||
|
code, state)
|
||||||
|
yield from initialize_data(session)
|
||||||
|
configurator.async_request_done(request_id)
|
||||||
|
except aioautomatic.exceptions.AutomaticError as err:
|
||||||
|
_LOGGER.error(str(err))
|
||||||
|
configurator.async_notify_errors(request_id, str(err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
if DATA_CONFIGURING not in hass.data:
|
||||||
|
hass.data[DATA_CONFIGURING] = {}
|
||||||
|
|
||||||
|
hass.data[DATA_CONFIGURING][client.state] = initialize_callback
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class AutomaticAuthCallbackView(HomeAssistantView):
|
||||||
|
"""Handle OAuth finish callback requests."""
|
||||||
|
|
||||||
|
requires_auth = False
|
||||||
|
url = '/api/automatic/callback'
|
||||||
|
name = 'api:automatic:callback'
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def get(self, request): # pylint: disable=no-self-use
|
||||||
|
"""Finish OAuth callback request."""
|
||||||
|
hass = request.app['hass']
|
||||||
|
params = request.query
|
||||||
|
response = web.HTTPFound('/states')
|
||||||
|
|
||||||
|
if 'state' not in params or 'code' not in params:
|
||||||
|
if 'error' in params:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Error authorizing Automatic: %s", params['error'])
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Error authorizing Automatic. Invalid response returned.")
|
||||||
|
return response
|
||||||
|
|
||||||
|
if DATA_CONFIGURING not in hass.data or \
|
||||||
|
params['state'] not in hass.data[DATA_CONFIGURING]:
|
||||||
|
_LOGGER.error("Automatic configuration request not found.")
|
||||||
|
return response
|
||||||
|
|
||||||
|
code = params['code']
|
||||||
|
state = params['state']
|
||||||
|
initialize_callback = hass.data[DATA_CONFIGURING][state]
|
||||||
|
hass.async_add_job(initialize_callback(code, state))
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class AutomaticData(object):
|
class AutomaticData(object):
|
||||||
"""A class representing an Automatic cloud service connection."""
|
"""A class representing an Automatic cloud service connection."""
|
||||||
|
|
||||||
|
@ -115,6 +237,8 @@ class AutomaticData(object):
|
||||||
lambda name, event: self.hass.async_add_job(
|
lambda name, event: self.hass.async_add_job(
|
||||||
self.handle_event(name, event)))
|
self.handle_event(name, event)))
|
||||||
|
|
||||||
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.ws_close())
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def handle_event(self, name, event):
|
def handle_event(self, name, event):
|
||||||
"""Coroutine to update state for a realtime event."""
|
"""Coroutine to update state for a realtime event."""
|
||||||
|
|
|
@ -19,7 +19,6 @@ import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.util.location import distance
|
from homeassistant.util.location import distance
|
||||||
from homeassistant.loader import get_component
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -209,7 +208,7 @@ class Icloud(DeviceScanner):
|
||||||
|
|
||||||
if self.accountname in _CONFIGURING:
|
if self.accountname in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(self.accountname)
|
request_id = _CONFIGURING.pop(self.accountname)
|
||||||
configurator = get_component('configurator')
|
configurator = self.hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
|
|
||||||
# Trigger the next step immediately
|
# Trigger the next step immediately
|
||||||
|
@ -217,7 +216,7 @@ class Icloud(DeviceScanner):
|
||||||
|
|
||||||
def icloud_need_trusted_device(self):
|
def icloud_need_trusted_device(self):
|
||||||
"""We need a trusted device."""
|
"""We need a trusted device."""
|
||||||
configurator = get_component('configurator')
|
configurator = self.hass.components.configurator
|
||||||
if self.accountname in _CONFIGURING:
|
if self.accountname in _CONFIGURING:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -229,7 +228,7 @@ class Icloud(DeviceScanner):
|
||||||
devicesstring += "{}: {};".format(i, devicename)
|
devicesstring += "{}: {};".format(i, devicename)
|
||||||
|
|
||||||
_CONFIGURING[self.accountname] = configurator.request_config(
|
_CONFIGURING[self.accountname] = configurator.request_config(
|
||||||
self.hass, 'iCloud {}'.format(self.accountname),
|
'iCloud {}'.format(self.accountname),
|
||||||
self.icloud_trusted_device_callback,
|
self.icloud_trusted_device_callback,
|
||||||
description=(
|
description=(
|
||||||
'Please choose your trusted device by entering'
|
'Please choose your trusted device by entering'
|
||||||
|
@ -259,17 +258,17 @@ class Icloud(DeviceScanner):
|
||||||
|
|
||||||
if self.accountname in _CONFIGURING:
|
if self.accountname in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(self.accountname)
|
request_id = _CONFIGURING.pop(self.accountname)
|
||||||
configurator = get_component('configurator')
|
configurator = self.hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
|
|
||||||
def icloud_need_verification_code(self):
|
def icloud_need_verification_code(self):
|
||||||
"""Return the verification code."""
|
"""Return the verification code."""
|
||||||
configurator = get_component('configurator')
|
configurator = self.hass.components.configurator
|
||||||
if self.accountname in _CONFIGURING:
|
if self.accountname in _CONFIGURING:
|
||||||
return
|
return
|
||||||
|
|
||||||
_CONFIGURING[self.accountname] = configurator.request_config(
|
_CONFIGURING[self.accountname] = configurator.request_config(
|
||||||
self.hass, 'iCloud {}'.format(self.accountname),
|
'iCloud {}'.format(self.accountname),
|
||||||
self.icloud_verification_callback,
|
self.icloud_verification_callback,
|
||||||
description=('Please enter the validation code:'),
|
description=('Please enter the validation code:'),
|
||||||
entity_picture="/static/images/config_icloud.png",
|
entity_picture="/static/images/config_icloud.png",
|
||||||
|
|
|
@ -13,7 +13,6 @@ import voluptuous as vol
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
from homeassistant.const import CONF_API_KEY
|
from homeassistant.const import CONF_API_KEY
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
REQUIREMENTS = ['python-ecobee-api==0.0.7']
|
REQUIREMENTS = ['python-ecobee-api==0.0.7']
|
||||||
|
@ -41,7 +40,7 @@ CONFIG_SCHEMA = vol.Schema({
|
||||||
|
|
||||||
def request_configuration(network, hass, config):
|
def request_configuration(network, hass, config):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
if 'ecobee' in _CONFIGURING:
|
if 'ecobee' in _CONFIGURING:
|
||||||
configurator.notify_errors(
|
configurator.notify_errors(
|
||||||
_CONFIGURING['ecobee'], "Failed to register, please try again.")
|
_CONFIGURING['ecobee'], "Failed to register, please try again.")
|
||||||
|
@ -56,7 +55,7 @@ def request_configuration(network, hass, config):
|
||||||
setup_ecobee(hass, network, config)
|
setup_ecobee(hass, network, config)
|
||||||
|
|
||||||
_CONFIGURING['ecobee'] = configurator.request_config(
|
_CONFIGURING['ecobee'] = configurator.request_config(
|
||||||
hass, "Ecobee", ecobee_configuration_callback,
|
"Ecobee", ecobee_configuration_callback,
|
||||||
description=(
|
description=(
|
||||||
'Please authorize this app at https://www.ecobee.com/consumer'
|
'Please authorize this app at https://www.ecobee.com/consumer'
|
||||||
'portal/index.html with pin code: ' + network.pin),
|
'portal/index.html with pin code: ' + network.pin),
|
||||||
|
@ -73,7 +72,7 @@ def setup_ecobee(hass, network, config):
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'ecobee' in _CONFIGURING:
|
if 'ecobee' in _CONFIGURING:
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(_CONFIGURING.pop('ecobee'))
|
configurator.request_done(_CONFIGURING.pop('ecobee'))
|
||||||
|
|
||||||
hold_temp = config[DOMAIN].get(CONF_HOLD_TEMP)
|
hold_temp = config[DOMAIN].get(CONF_HOLD_TEMP)
|
||||||
|
|
|
@ -13,7 +13,6 @@ from homeassistant.components.fan import (
|
||||||
ATTR_SPEED, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH,
|
ATTR_SPEED, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH,
|
||||||
SUPPORT_SET_SPEED, FanEntity)
|
SUPPORT_SET_SPEED, FanEntity)
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
from homeassistant.loader import get_component
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
_CONFIGURING = {}
|
_CONFIGURING = {}
|
||||||
|
@ -57,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
def request_configuration(device_id, insteonhub, model, hass,
|
def request_configuration(device_id, insteonhub, model, hass,
|
||||||
add_devices_callback):
|
add_devices_callback):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if device_id in _CONFIGURING:
|
if device_id in _CONFIGURING:
|
||||||
|
@ -72,7 +71,7 @@ def request_configuration(device_id, insteonhub, model, hass,
|
||||||
add_devices_callback)
|
add_devices_callback)
|
||||||
|
|
||||||
_CONFIGURING[device_id] = configurator.request_config(
|
_CONFIGURING[device_id] = configurator.request_config(
|
||||||
hass, 'Insteon ' + model + ' addr: ' + device_id,
|
'Insteon ' + model + ' addr: ' + device_id,
|
||||||
insteon_fan_config_callback,
|
insteon_fan_config_callback,
|
||||||
description=('Enter a name for ' + model + ' Fan addr: ' + device_id),
|
description=('Enter a name for ' + model + ' Fan addr: ' + device_id),
|
||||||
entity_picture='/static/images/config_insteon.png',
|
entity_picture='/static/images/config_insteon.png',
|
||||||
|
@ -85,7 +84,7 @@ def setup_fan(device_id, name, insteonhub, hass, add_devices_callback):
|
||||||
"""Set up the fan."""
|
"""Set up the fan."""
|
||||||
if device_id in _CONFIGURING:
|
if device_id in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(device_id)
|
request_id = _CONFIGURING.pop(device_id)
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
_LOGGER.info("Device configuration done!")
|
_LOGGER.info("Device configuration done!")
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
|
@ -23,7 +23,6 @@ from homeassistant.components.light import (
|
||||||
SUPPORT_XY_COLOR, Light, PLATFORM_SCHEMA)
|
SUPPORT_XY_COLOR, Light, PLATFORM_SCHEMA)
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.const import (CONF_FILENAME, CONF_HOST, DEVICE_DEFAULT_NAME)
|
from homeassistant.const import (CONF_FILENAME, CONF_HOST, DEVICE_DEFAULT_NAME)
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant.components.emulated_hue import ATTR_EMULATED_HUE
|
from homeassistant.components.emulated_hue import ATTR_EMULATED_HUE
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
|
@ -164,9 +163,7 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable,
|
||||||
# If we came here and configuring this host, mark as done
|
# If we came here and configuring this host, mark as done
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(host)
|
request_id = _CONFIGURING.pop(host)
|
||||||
|
configurator = hass.components.configurator
|
||||||
configurator = get_component('configurator')
|
|
||||||
|
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
|
|
||||||
lights = {}
|
lights = {}
|
||||||
|
@ -268,7 +265,7 @@ def request_configuration(host, hass, add_devices, filename,
|
||||||
allow_unreachable, allow_in_emulated_hue,
|
allow_unreachable, allow_in_emulated_hue,
|
||||||
allow_hue_groups):
|
allow_hue_groups):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
|
@ -284,7 +281,7 @@ def request_configuration(host, hass, add_devices, filename,
|
||||||
allow_in_emulated_hue, allow_hue_groups)
|
allow_in_emulated_hue, allow_hue_groups)
|
||||||
|
|
||||||
_CONFIGURING[host] = configurator.request_config(
|
_CONFIGURING[host] = configurator.request_config(
|
||||||
hass, "Philips Hue", hue_configuration_callback,
|
"Philips Hue", hue_configuration_callback,
|
||||||
description=("Press the button on the bridge to register Philips Hue "
|
description=("Press the button on the bridge to register Philips Hue "
|
||||||
"with Home Assistant."),
|
"with Home Assistant."),
|
||||||
entity_picture="/static/images/logo_philips_hue.png",
|
entity_picture="/static/images/logo_philips_hue.png",
|
||||||
|
|
|
@ -11,7 +11,6 @@ from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light)
|
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light)
|
||||||
from homeassistant.loader import get_component
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
_CONFIGURING = {}
|
_CONFIGURING = {}
|
||||||
|
@ -54,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
def request_configuration(device_id, insteonhub, model, hass,
|
def request_configuration(device_id, insteonhub, model, hass,
|
||||||
add_devices_callback):
|
add_devices_callback):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if device_id in _CONFIGURING:
|
if device_id in _CONFIGURING:
|
||||||
|
@ -69,7 +68,7 @@ def request_configuration(device_id, insteonhub, model, hass,
|
||||||
add_devices_callback)
|
add_devices_callback)
|
||||||
|
|
||||||
_CONFIGURING[device_id] = configurator.request_config(
|
_CONFIGURING[device_id] = configurator.request_config(
|
||||||
hass, 'Insteon ' + model + ' addr: ' + device_id,
|
'Insteon ' + model + ' addr: ' + device_id,
|
||||||
insteon_light_config_callback,
|
insteon_light_config_callback,
|
||||||
description=('Enter a name for ' + model + ' addr: ' + device_id),
|
description=('Enter a name for ' + model + ' addr: ' + device_id),
|
||||||
entity_picture='/static/images/config_insteon.png',
|
entity_picture='/static/images/config_insteon.png',
|
||||||
|
@ -82,7 +81,7 @@ def setup_light(device_id, name, insteonhub, hass, add_devices_callback):
|
||||||
"""Set up the light."""
|
"""Set up the light."""
|
||||||
if device_id in _CONFIGURING:
|
if device_id in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(device_id)
|
request_id = _CONFIGURING.pop(device_id)
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
_LOGGER.debug("Device configuration done")
|
_LOGGER.debug("Device configuration done")
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import re
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_ON,
|
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_ON,
|
||||||
SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP, SUPPORT_PLAY,
|
SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP, SUPPORT_PLAY,
|
||||||
|
@ -132,7 +131,7 @@ def setup_bravia(config, pin, hass, add_devices):
|
||||||
# If we came here and configuring this host, mark as done
|
# If we came here and configuring this host, mark as done
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(host)
|
request_id = _CONFIGURING.pop(host)
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
_LOGGER.info("Discovery configuration done")
|
_LOGGER.info("Discovery configuration done")
|
||||||
|
|
||||||
|
@ -150,7 +149,7 @@ def request_configuration(config, hass, add_devices):
|
||||||
host = config.get(CONF_HOST)
|
host = config.get(CONF_HOST)
|
||||||
name = config.get(CONF_NAME)
|
name = config.get(CONF_NAME)
|
||||||
|
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
|
@ -171,7 +170,7 @@ def request_configuration(config, hass, add_devices):
|
||||||
request_configuration(config, hass, add_devices)
|
request_configuration(config, hass, add_devices)
|
||||||
|
|
||||||
_CONFIGURING[host] = configurator.request_config(
|
_CONFIGURING[host] = configurator.request_config(
|
||||||
hass, name, bravia_configuration_callback,
|
name, bravia_configuration_callback,
|
||||||
description='Enter the Pin shown on your Sony Bravia TV.' +
|
description='Enter the Pin shown on your Sony Bravia TV.' +
|
||||||
'If no Pin is shown, enter 0000 to let TV show you a Pin.',
|
'If no Pin is shown, enter 0000 to let TV show you a Pin.',
|
||||||
description_image="/static/images/smart-tv.png",
|
description_image="/static/images/smart-tv.png",
|
||||||
|
|
|
@ -18,7 +18,6 @@ from homeassistant.components.media_player import (
|
||||||
MediaPlayerDevice, PLATFORM_SCHEMA)
|
MediaPlayerDevice, PLATFORM_SCHEMA)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_PLAYING, STATE_PAUSED, STATE_OFF, CONF_HOST, CONF_PORT, CONF_NAME)
|
STATE_PLAYING, STATE_PAUSED, STATE_OFF, CONF_HOST, CONF_PORT, CONF_NAME)
|
||||||
from homeassistant.loader import get_component
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['websocket-client==0.37.0']
|
REQUIREMENTS = ['websocket-client==0.37.0']
|
||||||
|
@ -48,7 +47,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
|
|
||||||
def request_configuration(hass, config, url, add_devices_callback):
|
def request_configuration(hass, config, url, add_devices_callback):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
if 'gpmdp' in _CONFIGURING:
|
if 'gpmdp' in _CONFIGURING:
|
||||||
configurator.notify_errors(
|
configurator.notify_errors(
|
||||||
_CONFIGURING['gpmdp'], "Failed to register, please try again.")
|
_CONFIGURING['gpmdp'], "Failed to register, please try again.")
|
||||||
|
@ -96,7 +95,7 @@ def request_configuration(hass, config, url, add_devices_callback):
|
||||||
break
|
break
|
||||||
|
|
||||||
_CONFIGURING['gpmdp'] = configurator.request_config(
|
_CONFIGURING['gpmdp'] = configurator.request_config(
|
||||||
hass, DEFAULT_NAME, gpmdp_configuration_callback,
|
DEFAULT_NAME, gpmdp_configuration_callback,
|
||||||
description=(
|
description=(
|
||||||
'Enter the pin that is displayed in the '
|
'Enter the pin that is displayed in the '
|
||||||
'Google Play Music Desktop Player.'),
|
'Google Play Music Desktop Player.'),
|
||||||
|
@ -117,7 +116,7 @@ def setup_gpmdp(hass, config, code, add_devices):
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'gpmdp' in _CONFIGURING:
|
if 'gpmdp' in _CONFIGURING:
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(_CONFIGURING.pop('gpmdp'))
|
configurator.request_done(_CONFIGURING.pop('gpmdp'))
|
||||||
|
|
||||||
add_devices([GPMDP(name, url, code)], True)
|
add_devices([GPMDP(name, url, code)], True)
|
||||||
|
|
|
@ -23,7 +23,6 @@ from homeassistant.const import (
|
||||||
DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING)
|
DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING)
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.event import track_utc_time_change
|
from homeassistant.helpers.event import track_utc_time_change
|
||||||
from homeassistant.loader import get_component
|
|
||||||
|
|
||||||
REQUIREMENTS = ['plexapi==2.0.2']
|
REQUIREMENTS = ['plexapi==2.0.2']
|
||||||
|
|
||||||
|
@ -143,7 +142,7 @@ def setup_plexserver(
|
||||||
# If we came here and configuring this host, mark as done
|
# If we came here and configuring this host, mark as done
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(host)
|
request_id = _CONFIGURING.pop(host)
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
_LOGGER.info("Discovery configuration done")
|
_LOGGER.info("Discovery configuration done")
|
||||||
|
|
||||||
|
@ -236,7 +235,7 @@ def setup_plexserver(
|
||||||
|
|
||||||
def request_configuration(host, hass, config, add_devices_callback):
|
def request_configuration(host, hass, config, add_devices_callback):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
configurator.notify_errors(_CONFIGURING[host],
|
configurator.notify_errors(_CONFIGURING[host],
|
||||||
|
@ -254,7 +253,6 @@ def request_configuration(host, hass, config, add_devices_callback):
|
||||||
)
|
)
|
||||||
|
|
||||||
_CONFIGURING[host] = configurator.request_config(
|
_CONFIGURING[host] = configurator.request_config(
|
||||||
hass,
|
|
||||||
'Plex Media Server',
|
'Plex Media Server',
|
||||||
plex_configuration_callback,
|
plex_configuration_callback,
|
||||||
description=('Enter the X-Plex-Token'),
|
description=('Enter the X-Plex-Token'),
|
||||||
|
|
|
@ -10,7 +10,6 @@ from datetime import timedelta
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, SUPPORT_VOLUME_SET,
|
MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, SUPPORT_VOLUME_SET,
|
||||||
|
@ -62,9 +61,9 @@ SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
def request_configuration(hass, config, add_devices, oauth):
|
def request_configuration(hass, config, add_devices, oauth):
|
||||||
"""Request Spotify authorization."""
|
"""Request Spotify authorization."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
hass.data[DOMAIN] = configurator.request_config(
|
hass.data[DOMAIN] = configurator.request_config(
|
||||||
hass, DEFAULT_NAME, lambda _: None,
|
DEFAULT_NAME, lambda _: None,
|
||||||
link_name=CONFIGURATOR_LINK_NAME,
|
link_name=CONFIGURATOR_LINK_NAME,
|
||||||
link_url=oauth.get_authorize_url(),
|
link_url=oauth.get_authorize_url(),
|
||||||
description=CONFIGURATOR_DESCRIPTION,
|
description=CONFIGURATOR_DESCRIPTION,
|
||||||
|
@ -88,7 +87,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
request_configuration(hass, config, add_devices, oauth)
|
request_configuration(hass, config, add_devices, oauth)
|
||||||
return
|
return
|
||||||
if hass.data.get(DOMAIN):
|
if hass.data.get(DOMAIN):
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(hass.data.get(DOMAIN))
|
configurator.request_done(hass.data.get(DOMAIN))
|
||||||
del hass.data[DOMAIN]
|
del hass.data[DOMAIN]
|
||||||
player = SpotifyMediaPlayer(oauth, config.get(CONF_NAME, DEFAULT_NAME),
|
player = SpotifyMediaPlayer(oauth, config.get(CONF_NAME, DEFAULT_NAME),
|
||||||
|
|
|
@ -22,7 +22,6 @@ from homeassistant.const import (
|
||||||
CONF_HOST, CONF_MAC, CONF_CUSTOMIZE, STATE_OFF,
|
CONF_HOST, CONF_MAC, CONF_CUSTOMIZE, STATE_OFF,
|
||||||
STATE_PLAYING, STATE_PAUSED,
|
STATE_PLAYING, STATE_PAUSED,
|
||||||
STATE_UNKNOWN, CONF_NAME, CONF_FILENAME)
|
STATE_UNKNOWN, CONF_NAME, CONF_FILENAME)
|
||||||
from homeassistant.loader import get_component
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['pylgtv==0.1.7',
|
REQUIREMENTS = ['pylgtv==0.1.7',
|
||||||
|
@ -114,7 +113,7 @@ def setup_tv(host, mac, name, customize, config, hass, add_devices):
|
||||||
# If we came here and configuring this host, mark as done.
|
# If we came here and configuring this host, mark as done.
|
||||||
if client.is_registered() and host in _CONFIGURING:
|
if client.is_registered() and host in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(host)
|
request_id = _CONFIGURING.pop(host)
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
|
|
||||||
add_devices([LgWebOSDevice(host, mac, name, customize, config)], True)
|
add_devices([LgWebOSDevice(host, mac, name, customize, config)], True)
|
||||||
|
@ -123,7 +122,7 @@ def setup_tv(host, mac, name, customize, config, hass, add_devices):
|
||||||
def request_configuration(
|
def request_configuration(
|
||||||
host, mac, name, customize, config, hass, add_devices):
|
host, mac, name, customize, config, hass, add_devices):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
|
@ -137,7 +136,7 @@ def request_configuration(
|
||||||
setup_tv(host, mac, name, customize, config, hass, add_devices)
|
setup_tv(host, mac, name, customize, config, hass, add_devices)
|
||||||
|
|
||||||
_CONFIGURING[host] = configurator.request_config(
|
_CONFIGURING[host] = configurator.request_config(
|
||||||
hass, name, lgtv_configuration_callback,
|
name, lgtv_configuration_callback,
|
||||||
description='Click start and accept the pairing request on your TV.',
|
description='Click start and accept the pairing request on your TV.',
|
||||||
description_image='/static/images/config_webos.png',
|
description_image='/static/images/config_webos.png',
|
||||||
submit_caption='Start pairing request'
|
submit_caption='Start pairing request'
|
||||||
|
|
|
@ -14,7 +14,6 @@ from homeassistant.helpers import discovery
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_STRUCTURE, CONF_FILENAME, CONF_BINARY_SENSORS, CONF_SENSORS,
|
CONF_STRUCTURE, CONF_FILENAME, CONF_BINARY_SENSORS, CONF_SENSORS,
|
||||||
CONF_MONITORED_CONDITIONS)
|
CONF_MONITORED_CONDITIONS)
|
||||||
from homeassistant.loader import get_component
|
|
||||||
|
|
||||||
REQUIREMENTS = ['python-nest==3.1.0']
|
REQUIREMENTS = ['python-nest==3.1.0']
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ CONFIG_SCHEMA = vol.Schema({
|
||||||
|
|
||||||
def request_configuration(nest, hass, config):
|
def request_configuration(nest, hass, config):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
if 'nest' in _CONFIGURING:
|
if 'nest' in _CONFIGURING:
|
||||||
_LOGGER.debug("configurator failed")
|
_LOGGER.debug("configurator failed")
|
||||||
configurator.notify_errors(
|
configurator.notify_errors(
|
||||||
|
@ -68,7 +67,7 @@ def request_configuration(nest, hass, config):
|
||||||
setup_nest(hass, nest, config, pin=pin)
|
setup_nest(hass, nest, config, pin=pin)
|
||||||
|
|
||||||
_CONFIGURING['nest'] = configurator.request_config(
|
_CONFIGURING['nest'] = configurator.request_config(
|
||||||
hass, "Nest", nest_configuration_callback,
|
"Nest", nest_configuration_callback,
|
||||||
description=('To configure Nest, click Request Authorization below, '
|
description=('To configure Nest, click Request Authorization below, '
|
||||||
'log into your Nest account, '
|
'log into your Nest account, '
|
||||||
'and then enter the resulting PIN'),
|
'and then enter the resulting PIN'),
|
||||||
|
@ -92,7 +91,7 @@ def setup_nest(hass, nest, config, pin=None):
|
||||||
|
|
||||||
if 'nest' in _CONFIGURING:
|
if 'nest' in _CONFIGURING:
|
||||||
_LOGGER.debug("configuration done")
|
_LOGGER.debug("configuration done")
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(_CONFIGURING.pop('nest'))
|
configurator.request_done(_CONFIGURING.pop('nest'))
|
||||||
|
|
||||||
_LOGGER.debug("proceeding with setup")
|
_LOGGER.debug("proceeding with setup")
|
||||||
|
|
|
@ -17,7 +17,6 @@ from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
from homeassistant.const import ATTR_ATTRIBUTION
|
from homeassistant.const import ATTR_ATTRIBUTION
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.loader import get_component
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['fitbit==0.2.3']
|
REQUIREMENTS = ['fitbit==0.2.3']
|
||||||
|
@ -155,7 +154,7 @@ def config_from_file(filename, config=None):
|
||||||
def request_app_setup(hass, config, add_devices, config_path,
|
def request_app_setup(hass, config, add_devices, config_path,
|
||||||
discovery_info=None):
|
discovery_info=None):
|
||||||
"""Assist user with configuring the Fitbit dev application."""
|
"""Assist user with configuring the Fitbit dev application."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def fitbit_configuration_callback(callback_data):
|
def fitbit_configuration_callback(callback_data):
|
||||||
|
@ -166,7 +165,8 @@ def request_app_setup(hass, config, add_devices, config_path,
|
||||||
if config_file == DEFAULT_CONFIG:
|
if config_file == DEFAULT_CONFIG:
|
||||||
error_msg = ("You didn't correctly modify fitbit.conf",
|
error_msg = ("You didn't correctly modify fitbit.conf",
|
||||||
" please try again")
|
" please try again")
|
||||||
configurator.notify_errors(_CONFIGURING['fitbit'], error_msg)
|
configurator.notify_errors(_CONFIGURING['fitbit'],
|
||||||
|
error_msg)
|
||||||
else:
|
else:
|
||||||
setup_platform(hass, config, add_devices, discovery_info)
|
setup_platform(hass, config, add_devices, discovery_info)
|
||||||
else:
|
else:
|
||||||
|
@ -187,7 +187,7 @@ def request_app_setup(hass, config, add_devices, config_path,
|
||||||
submit = "I have saved my Client ID and Client Secret into fitbit.conf."
|
submit = "I have saved my Client ID and Client Secret into fitbit.conf."
|
||||||
|
|
||||||
_CONFIGURING['fitbit'] = configurator.request_config(
|
_CONFIGURING['fitbit'] = configurator.request_config(
|
||||||
hass, 'Fitbit', fitbit_configuration_callback,
|
'Fitbit', fitbit_configuration_callback,
|
||||||
description=description, submit_caption=submit,
|
description=description, submit_caption=submit,
|
||||||
description_image="/static/images/config_fitbit_app.png"
|
description_image="/static/images/config_fitbit_app.png"
|
||||||
)
|
)
|
||||||
|
@ -195,7 +195,7 @@ def request_app_setup(hass, config, add_devices, config_path,
|
||||||
|
|
||||||
def request_oauth_completion(hass):
|
def request_oauth_completion(hass):
|
||||||
"""Request user complete Fitbit OAuth2 flow."""
|
"""Request user complete Fitbit OAuth2 flow."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
if "fitbit" in _CONFIGURING:
|
if "fitbit" in _CONFIGURING:
|
||||||
configurator.notify_errors(
|
configurator.notify_errors(
|
||||||
_CONFIGURING['fitbit'], "Failed to register, please try again.")
|
_CONFIGURING['fitbit'], "Failed to register, please try again.")
|
||||||
|
@ -211,7 +211,7 @@ def request_oauth_completion(hass):
|
||||||
description = "Please authorize Fitbit by visiting {}".format(start_url)
|
description = "Please authorize Fitbit by visiting {}".format(start_url)
|
||||||
|
|
||||||
_CONFIGURING['fitbit'] = configurator.request_config(
|
_CONFIGURING['fitbit'] = configurator.request_config(
|
||||||
hass, 'Fitbit', fitbit_configuration_callback,
|
'Fitbit', fitbit_configuration_callback,
|
||||||
description=description,
|
description=description,
|
||||||
submit_caption="I have authorized Fitbit."
|
submit_caption="I have authorized Fitbit."
|
||||||
)
|
)
|
||||||
|
@ -233,7 +233,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if "fitbit" in _CONFIGURING:
|
if "fitbit" in _CONFIGURING:
|
||||||
get_component('configurator').request_done(_CONFIGURING.pop("fitbit"))
|
hass.components.configurator.request_done(_CONFIGURING.pop("fitbit"))
|
||||||
|
|
||||||
import fitbit
|
import fitbit
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ from homeassistant.const import (
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.loader import get_component
|
|
||||||
|
|
||||||
REQUIREMENTS = ['https://github.com/jamespcole/home-assistant-nzb-clients/'
|
REQUIREMENTS = ['https://github.com/jamespcole/home-assistant-nzb-clients/'
|
||||||
'archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip'
|
'archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip'
|
||||||
|
@ -88,7 +87,7 @@ def setup_sabnzbd(base_url, apikey, name, hass, config, add_devices, sab_api):
|
||||||
|
|
||||||
def request_configuration(host, name, hass, config, add_devices, sab_api):
|
def request_configuration(host, name, hass, config, add_devices, sab_api):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if host in _CONFIGURING:
|
if host in _CONFIGURING:
|
||||||
configurator.notify_errors(_CONFIGURING[host],
|
configurator.notify_errors(_CONFIGURING[host],
|
||||||
|
@ -114,7 +113,6 @@ def request_configuration(host, name, hass, config, add_devices, sab_api):
|
||||||
hass.async_add_job(success)
|
hass.async_add_job(success)
|
||||||
|
|
||||||
_CONFIGURING[host] = configurator.request_config(
|
_CONFIGURING[host] = configurator.request_config(
|
||||||
hass,
|
|
||||||
DEFAULT_NAME,
|
DEFAULT_NAME,
|
||||||
sabnzbd_configuration_callback,
|
sabnzbd_configuration_callback,
|
||||||
description=('Enter the API Key'),
|
description=('Enter the API Key'),
|
||||||
|
|
|
@ -10,7 +10,6 @@ import os
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchDevice
|
from homeassistant.components.switch import SwitchDevice
|
||||||
from homeassistant.loader import get_component
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
_CONFIGURING = {}
|
_CONFIGURING = {}
|
||||||
|
@ -51,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
def request_configuration(
|
def request_configuration(
|
||||||
device_id, insteonhub, model, hass, add_devices_callback):
|
device_id, insteonhub, model, hass, add_devices_callback):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# We got an error if this method is called while we are configuring
|
# We got an error if this method is called while we are configuring
|
||||||
if device_id in _CONFIGURING:
|
if device_id in _CONFIGURING:
|
||||||
|
@ -66,7 +65,7 @@ def request_configuration(
|
||||||
add_devices_callback)
|
add_devices_callback)
|
||||||
|
|
||||||
_CONFIGURING[device_id] = configurator.request_config(
|
_CONFIGURING[device_id] = configurator.request_config(
|
||||||
hass, 'Insteon Switch ' + model + ' addr: ' + device_id,
|
'Insteon Switch ' + model + ' addr: ' + device_id,
|
||||||
insteon_switch_config_callback,
|
insteon_switch_config_callback,
|
||||||
description=('Enter a name for ' + model + ' addr: ' + device_id),
|
description=('Enter a name for ' + model + ' addr: ' + device_id),
|
||||||
entity_picture='/static/images/config_insteon.png',
|
entity_picture='/static/images/config_insteon.png',
|
||||||
|
@ -79,7 +78,7 @@ def setup_switch(device_id, name, insteonhub, hass, add_devices_callback):
|
||||||
"""Set up the switch."""
|
"""Set up the switch."""
|
||||||
if device_id in _CONFIGURING:
|
if device_id in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(device_id)
|
request_id = _CONFIGURING.pop(device_id)
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
_LOGGER.info("Device configuration done")
|
_LOGGER.info("Device configuration done")
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ import voluptuous as vol
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
from homeassistant.const import CONF_HOST, CONF_API_KEY
|
from homeassistant.const import CONF_HOST, CONF_API_KEY
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant.components.discovery import SERVICE_IKEA_TRADFRI
|
from homeassistant.components.discovery import SERVICE_IKEA_TRADFRI
|
||||||
|
|
||||||
REQUIREMENTS = ['pytradfri==1.1']
|
REQUIREMENTS = ['pytradfri==1.1']
|
||||||
|
@ -41,7 +40,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
def request_configuration(hass, config, host):
|
def request_configuration(hass, config, host):
|
||||||
"""Request configuration steps from the user."""
|
"""Request configuration steps from the user."""
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
hass.data.setdefault(KEY_CONFIG, {})
|
hass.data.setdefault(KEY_CONFIG, {})
|
||||||
instance = hass.data[KEY_CONFIG].get(host)
|
instance = hass.data[KEY_CONFIG].get(host)
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ def request_configuration(hass, config, host):
|
||||||
hass.async_add_job(success)
|
hass.async_add_job(success)
|
||||||
|
|
||||||
instance = configurator.request_config(
|
instance = configurator.request_config(
|
||||||
hass, "IKEA Trådfri", configuration_callback,
|
"IKEA Trådfri", configuration_callback,
|
||||||
description='Please enter the security code written at the bottom of '
|
description='Please enter the security code written at the bottom of '
|
||||||
'your IKEA Trådfri Gateway.',
|
'your IKEA Trådfri Gateway.',
|
||||||
submit_caption="Confirm",
|
submit_caption="Confirm",
|
||||||
|
|
|
@ -13,7 +13,6 @@ from datetime import timedelta
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
|
@ -103,7 +102,7 @@ def _read_config_file(file_path):
|
||||||
def _request_app_setup(hass, config):
|
def _request_app_setup(hass, config):
|
||||||
"""Assist user with configuring the Wink dev application."""
|
"""Assist user with configuring the Wink dev application."""
|
||||||
hass.data[DOMAIN]['configurator'] = True
|
hass.data[DOMAIN]['configurator'] = True
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def wink_configuration_callback(callback_data):
|
def wink_configuration_callback(callback_data):
|
||||||
|
@ -138,7 +137,7 @@ def _request_app_setup(hass, config):
|
||||||
""".format(start_url)
|
""".format(start_url)
|
||||||
|
|
||||||
hass.data[DOMAIN]['configuring'][DOMAIN] = configurator.request_config(
|
hass.data[DOMAIN]['configuring'][DOMAIN] = configurator.request_config(
|
||||||
hass, DOMAIN, wink_configuration_callback,
|
DOMAIN, wink_configuration_callback,
|
||||||
description=description, submit_caption="submit",
|
description=description, submit_caption="submit",
|
||||||
description_image="/static/images/config_wink.png",
|
description_image="/static/images/config_wink.png",
|
||||||
fields=[{'id': 'client_id', 'name': 'Client ID', 'type': 'string'},
|
fields=[{'id': 'client_id', 'name': 'Client ID', 'type': 'string'},
|
||||||
|
@ -151,7 +150,7 @@ def _request_app_setup(hass, config):
|
||||||
def _request_oauth_completion(hass, config):
|
def _request_oauth_completion(hass, config):
|
||||||
"""Request user complete Wink OAuth2 flow."""
|
"""Request user complete Wink OAuth2 flow."""
|
||||||
hass.data[DOMAIN]['configurator'] = True
|
hass.data[DOMAIN]['configurator'] = True
|
||||||
configurator = get_component('configurator')
|
configurator = hass.components.configurator
|
||||||
if DOMAIN in hass.data[DOMAIN]['configuring']:
|
if DOMAIN in hass.data[DOMAIN]['configuring']:
|
||||||
configurator.notify_errors(
|
configurator.notify_errors(
|
||||||
hass.data[DOMAIN]['configuring'][DOMAIN],
|
hass.data[DOMAIN]['configuring'][DOMAIN],
|
||||||
|
@ -168,7 +167,7 @@ def _request_oauth_completion(hass, config):
|
||||||
description = "Please authorize Wink by visiting {}".format(start_url)
|
description = "Please authorize Wink by visiting {}".format(start_url)
|
||||||
|
|
||||||
hass.data[DOMAIN]['configuring'][DOMAIN] = configurator.request_config(
|
hass.data[DOMAIN]['configuring'][DOMAIN] = configurator.request_config(
|
||||||
hass, DOMAIN, wink_configuration_callback,
|
DOMAIN, wink_configuration_callback,
|
||||||
description=description
|
description=description
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -248,7 +247,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
if DOMAIN in hass.data[DOMAIN]['configuring']:
|
if DOMAIN in hass.data[DOMAIN]['configuring']:
|
||||||
_configurator = hass.data[DOMAIN]['configuring']
|
_configurator = hass.data[DOMAIN]['configuring']
|
||||||
get_component('configurator').request_done(_configurator.pop(
|
hass.components.configurator.request_done(_configurator.pop(
|
||||||
DOMAIN))
|
DOMAIN))
|
||||||
|
|
||||||
# Using oauth
|
# Using oauth
|
||||||
|
|
|
@ -39,7 +39,7 @@ SoCo==0.12
|
||||||
TwitterAPI==2.4.6
|
TwitterAPI==2.4.6
|
||||||
|
|
||||||
# homeassistant.components.device_tracker.automatic
|
# homeassistant.components.device_tracker.automatic
|
||||||
aioautomatic==0.4.0
|
aioautomatic==0.5.0
|
||||||
|
|
||||||
# homeassistant.components.sensor.dnsip
|
# homeassistant.components.sensor.dnsip
|
||||||
aiodns==1.1.1
|
aiodns==1.1.1
|
||||||
|
|
|
@ -27,7 +27,7 @@ PyJWT==1.5.2
|
||||||
SoCo==0.12
|
SoCo==0.12
|
||||||
|
|
||||||
# homeassistant.components.device_tracker.automatic
|
# homeassistant.components.device_tracker.automatic
|
||||||
aioautomatic==0.4.0
|
aioautomatic==0.5.0
|
||||||
|
|
||||||
# homeassistant.components.emulated_hue
|
# homeassistant.components.emulated_hue
|
||||||
# homeassistant.components.http
|
# homeassistant.components.http
|
||||||
|
|
|
@ -7,12 +7,16 @@ import aioautomatic
|
||||||
from homeassistant.components.device_tracker.automatic import (
|
from homeassistant.components.device_tracker.automatic import (
|
||||||
async_setup_scanner)
|
async_setup_scanner)
|
||||||
|
|
||||||
|
from tests.common import mock_http_component
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@patch('aioautomatic.Client.create_session_from_password')
|
@patch('aioautomatic.Client.create_session_from_password')
|
||||||
def test_invalid_credentials(mock_create_session, hass):
|
def test_invalid_credentials(mock_create_session, hass):
|
||||||
"""Test with invalid credentials."""
|
"""Test with invalid credentials."""
|
||||||
|
mock_http_component(hass)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def get_session(*args, **kwargs):
|
def get_session(*args, **kwargs):
|
||||||
"""Return the test session."""
|
"""Return the test session."""
|
||||||
|
@ -34,8 +38,15 @@ def test_invalid_credentials(mock_create_session, hass):
|
||||||
|
|
||||||
|
|
||||||
@patch('aioautomatic.Client.create_session_from_password')
|
@patch('aioautomatic.Client.create_session_from_password')
|
||||||
def test_valid_credentials(mock_create_session, hass):
|
@patch('aioautomatic.Client.ws_connect')
|
||||||
|
@patch('json.dump')
|
||||||
|
@patch('os.makedirs')
|
||||||
|
@patch('homeassistant.components.device_tracker.automatic.open', create=True)
|
||||||
|
def test_valid_credentials(mock_open, mock_os_makedirs, mock_json_dump,
|
||||||
|
mock_ws_connect, mock_create_session, hass):
|
||||||
"""Test with valid credentials."""
|
"""Test with valid credentials."""
|
||||||
|
mock_http_component(hass)
|
||||||
|
|
||||||
session = MagicMock()
|
session = MagicMock()
|
||||||
vehicle = MagicMock()
|
vehicle = MagicMock()
|
||||||
trip = MagicMock()
|
trip = MagicMock()
|
||||||
|
@ -66,13 +77,21 @@ def test_valid_credentials(mock_create_session, hass):
|
||||||
return [trip]
|
return [trip]
|
||||||
|
|
||||||
mock_create_session.side_effect = get_session
|
mock_create_session.side_effect = get_session
|
||||||
|
session.ws_connect = MagicMock()
|
||||||
session.get_vehicles.side_effect = get_vehicles
|
session.get_vehicles.side_effect = get_vehicles
|
||||||
session.get_trips.side_effect = get_trips
|
session.get_trips.side_effect = get_trips
|
||||||
|
session.refresh_token = 'mock_refresh_token'
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def ws_connect():
|
||||||
|
return asyncio.Future(loop=hass.loop)
|
||||||
|
|
||||||
|
mock_ws_connect.side_effect = ws_connect
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
'platform': 'automatic',
|
'platform': 'automatic',
|
||||||
'username': 'bad_username',
|
'username': 'good_username',
|
||||||
'password': 'bad_password',
|
'password': 'good_password',
|
||||||
'client_id': 'client_id',
|
'client_id': 'client_id',
|
||||||
'secret': 'client_secret',
|
'secret': 'client_secret',
|
||||||
'devices': None,
|
'devices': None,
|
||||||
|
@ -80,6 +99,8 @@ def test_valid_credentials(mock_create_session, hass):
|
||||||
result = hass.loop.run_until_complete(
|
result = hass.loop.run_until_complete(
|
||||||
async_setup_scanner(hass, config, mock_see))
|
async_setup_scanner(hass, config, mock_see))
|
||||||
|
|
||||||
|
hass.async_block_till_done()
|
||||||
|
|
||||||
assert result
|
assert result
|
||||||
assert mock_see.called
|
assert mock_see.called
|
||||||
assert len(mock_see.mock_calls) == 2
|
assert len(mock_see.mock_calls) == 2
|
||||||
|
@ -89,3 +110,9 @@ def test_valid_credentials(mock_create_session, hass):
|
||||||
assert mock_see.mock_calls[0][2]['attributes'] == {'fuel_level': 45.6}
|
assert mock_see.mock_calls[0][2]['attributes'] == {'fuel_level': 45.6}
|
||||||
assert mock_see.mock_calls[0][2]['gps'] == (45.567, 34.345)
|
assert mock_see.mock_calls[0][2]['gps'] == (45.567, 34.345)
|
||||||
assert mock_see.mock_calls[0][2]['gps_accuracy'] == 5.6
|
assert mock_see.mock_calls[0][2]['gps_accuracy'] == 5.6
|
||||||
|
|
||||||
|
assert mock_json_dump.called
|
||||||
|
assert len(mock_json_dump.mock_calls) == 1
|
||||||
|
assert mock_json_dump.mock_calls[0][1][0] == {
|
||||||
|
'refresh_token': 'mock_refresh_token'
|
||||||
|
}
|
||||||
|
|
|
@ -90,20 +90,20 @@ class TestConfigurator(unittest.TestCase):
|
||||||
request_id = configurator.request_config(
|
request_id = configurator.request_config(
|
||||||
self.hass, "Test Request", lambda _: None)
|
self.hass, "Test Request", lambda _: None)
|
||||||
error = "Oh no bad bad bad"
|
error = "Oh no bad bad bad"
|
||||||
configurator.notify_errors(request_id, error)
|
configurator.notify_errors(self.hass, request_id, error)
|
||||||
|
|
||||||
state = self.hass.states.all()[0]
|
state = self.hass.states.all()[0]
|
||||||
self.assertEqual(error, state.attributes.get(configurator.ATTR_ERRORS))
|
self.assertEqual(error, state.attributes.get(configurator.ATTR_ERRORS))
|
||||||
|
|
||||||
def test_notify_errors_fail_silently_on_bad_request_id(self):
|
def test_notify_errors_fail_silently_on_bad_request_id(self):
|
||||||
"""Test if notify errors fails silently with a bad request id."""
|
"""Test if notify errors fails silently with a bad request id."""
|
||||||
configurator.notify_errors(2015, "Try this error")
|
configurator.notify_errors(self.hass, 2015, "Try this error")
|
||||||
|
|
||||||
def test_request_done_works(self):
|
def test_request_done_works(self):
|
||||||
"""Test if calling request done works."""
|
"""Test if calling request done works."""
|
||||||
request_id = configurator.request_config(
|
request_id = configurator.request_config(
|
||||||
self.hass, "Test Request", lambda _: None)
|
self.hass, "Test Request", lambda _: None)
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(self.hass, request_id)
|
||||||
self.assertEqual(1, len(self.hass.states.all()))
|
self.assertEqual(1, len(self.hass.states.all()))
|
||||||
|
|
||||||
self.hass.bus.fire(EVENT_TIME_CHANGED)
|
self.hass.bus.fire(EVENT_TIME_CHANGED)
|
||||||
|
@ -112,4 +112,4 @@ class TestConfigurator(unittest.TestCase):
|
||||||
|
|
||||||
def test_request_done_fail_silently_on_bad_request_id(self):
|
def test_request_done_fail_silently_on_bad_request_id(self):
|
||||||
"""Test that request_done fails silently with a bad request id."""
|
"""Test that request_done fails silently with a bad request id."""
|
||||||
configurator.request_done(2016)
|
configurator.request_done(self.hass, 2016)
|
||||||
|
|
Loading…
Add table
Reference in a new issue