commit
2aabdf29bb
|
@ -163,7 +163,7 @@ async def async_setup(hass, config):
|
||||||
|
|
||||||
async def atv_discovered(service, info):
|
async def atv_discovered(service, info):
|
||||||
"""Set up an Apple TV that was auto discovered."""
|
"""Set up an Apple TV that was auto discovered."""
|
||||||
await _setup_atv(hass, {
|
await _setup_atv(hass, config, {
|
||||||
CONF_NAME: info['name'],
|
CONF_NAME: info['name'],
|
||||||
CONF_HOST: info['host'],
|
CONF_HOST: info['host'],
|
||||||
CONF_LOGIN_ID: info['properties']['hG'],
|
CONF_LOGIN_ID: info['properties']['hG'],
|
||||||
|
@ -172,7 +172,7 @@ async def async_setup(hass, config):
|
||||||
|
|
||||||
discovery.async_listen(hass, SERVICE_APPLE_TV, atv_discovered)
|
discovery.async_listen(hass, SERVICE_APPLE_TV, atv_discovered)
|
||||||
|
|
||||||
tasks = [_setup_atv(hass, conf) for conf in config.get(DOMAIN, [])]
|
tasks = [_setup_atv(hass, config, conf) for conf in config.get(DOMAIN, [])]
|
||||||
if tasks:
|
if tasks:
|
||||||
await asyncio.wait(tasks, loop=hass.loop)
|
await asyncio.wait(tasks, loop=hass.loop)
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ async def async_setup(hass, config):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def _setup_atv(hass, atv_config):
|
async def _setup_atv(hass, hass_config, atv_config):
|
||||||
"""Set up an Apple TV."""
|
"""Set up an Apple TV."""
|
||||||
import pyatv
|
import pyatv
|
||||||
name = atv_config.get(CONF_NAME)
|
name = atv_config.get(CONF_NAME)
|
||||||
|
@ -212,10 +212,10 @@ async def _setup_atv(hass, atv_config):
|
||||||
}
|
}
|
||||||
|
|
||||||
hass.async_create_task(discovery.async_load_platform(
|
hass.async_create_task(discovery.async_load_platform(
|
||||||
hass, 'media_player', DOMAIN, atv_config))
|
hass, 'media_player', DOMAIN, atv_config, hass_config))
|
||||||
|
|
||||||
hass.async_create_task(discovery.async_load_platform(
|
hass.async_create_task(discovery.async_load_platform(
|
||||||
hass, 'remote', DOMAIN, atv_config))
|
hass, 'remote', DOMAIN, atv_config, hass_config))
|
||||||
|
|
||||||
|
|
||||||
class AppleTVPowerManager:
|
class AppleTVPowerManager:
|
||||||
|
|
|
@ -4,20 +4,16 @@ Component to integrate the Home Assistant cloud.
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/cloud/
|
https://home-assistant.io/components/cloud/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import async_timeout
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START, CONF_REGION, CONF_MODE, CONF_NAME)
|
EVENT_HOMEASSISTANT_START, CONF_REGION, CONF_MODE, CONF_NAME)
|
||||||
from homeassistant.helpers import entityfilter, config_validation as cv
|
from homeassistant.helpers import entityfilter, config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
from homeassistant.components.alexa import smart_home as alexa_sh
|
from homeassistant.components.alexa import smart_home as alexa_sh
|
||||||
from homeassistant.components.google_assistant import helpers as ga_h
|
from homeassistant.components.google_assistant import helpers as ga_h
|
||||||
|
@ -129,7 +125,6 @@ class Cloud:
|
||||||
self._google_actions = google_actions
|
self._google_actions = google_actions
|
||||||
self._gactions_config = None
|
self._gactions_config = None
|
||||||
self._prefs = None
|
self._prefs = None
|
||||||
self.jwt_keyset = None
|
|
||||||
self.id_token = None
|
self.id_token = None
|
||||||
self.access_token = None
|
self.access_token = None
|
||||||
self.refresh_token = None
|
self.refresh_token = None
|
||||||
|
@ -262,13 +257,6 @@ class Cloud:
|
||||||
}
|
}
|
||||||
self._prefs = prefs
|
self._prefs = prefs
|
||||||
|
|
||||||
success = await self._fetch_jwt_keyset()
|
|
||||||
|
|
||||||
# Fetching keyset can fail if internet is not up yet.
|
|
||||||
if not success:
|
|
||||||
self.hass.helpers.event.async_call_later(5, self.async_start)
|
|
||||||
return
|
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
"""Load config."""
|
"""Load config."""
|
||||||
# Ensure config dir exists
|
# Ensure config dir exists
|
||||||
|
@ -288,14 +276,6 @@ class Cloud:
|
||||||
if info is None:
|
if info is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Validate tokens
|
|
||||||
try:
|
|
||||||
for token in 'id_token', 'access_token':
|
|
||||||
self._decode_claims(info[token])
|
|
||||||
except ValueError as err: # Raised when token is invalid
|
|
||||||
_LOGGER.warning("Found invalid token %s: %s", token, err)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.id_token = info['id_token']
|
self.id_token = info['id_token']
|
||||||
self.access_token = info['access_token']
|
self.access_token = info['access_token']
|
||||||
self.refresh_token = info['refresh_token']
|
self.refresh_token = info['refresh_token']
|
||||||
|
@ -311,49 +291,7 @@ class Cloud:
|
||||||
self._prefs[STORAGE_ENABLE_ALEXA] = alexa_enabled
|
self._prefs[STORAGE_ENABLE_ALEXA] = alexa_enabled
|
||||||
await self._store.async_save(self._prefs)
|
await self._store.async_save(self._prefs)
|
||||||
|
|
||||||
async def _fetch_jwt_keyset(self):
|
def _decode_claims(self, token): # pylint: disable=no-self-use
|
||||||
"""Fetch the JWT keyset for the Cognito instance."""
|
|
||||||
session = async_get_clientsession(self.hass)
|
|
||||||
url = ("https://cognito-idp.us-east-1.amazonaws.com/"
|
|
||||||
"{}/.well-known/jwks.json".format(self.user_pool_id))
|
|
||||||
|
|
||||||
try:
|
|
||||||
with async_timeout.timeout(10, loop=self.hass.loop):
|
|
||||||
req = await session.get(url)
|
|
||||||
self.jwt_keyset = await req.json()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
|
|
||||||
_LOGGER.error("Error fetching Cognito keyset: %s", err)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _decode_claims(self, token):
|
|
||||||
"""Decode the claims in a token."""
|
"""Decode the claims in a token."""
|
||||||
from jose import jwt, exceptions as jose_exceptions
|
from jose import jwt
|
||||||
try:
|
return jwt.get_unverified_claims(token)
|
||||||
header = jwt.get_unverified_header(token)
|
|
||||||
except jose_exceptions.JWTError as err:
|
|
||||||
raise ValueError(str(err)) from None
|
|
||||||
kid = header.get('kid')
|
|
||||||
|
|
||||||
if kid is None:
|
|
||||||
raise ValueError("No kid in header")
|
|
||||||
|
|
||||||
# Locate the key for this kid
|
|
||||||
key = None
|
|
||||||
for key_dict in self.jwt_keyset['keys']:
|
|
||||||
if key_dict['kid'] == kid:
|
|
||||||
key = key_dict
|
|
||||||
break
|
|
||||||
if not key:
|
|
||||||
raise ValueError(
|
|
||||||
"Unable to locate kid ({}) in keyset".format(kid))
|
|
||||||
|
|
||||||
try:
|
|
||||||
return jwt.decode(
|
|
||||||
token, key, audience=self.cognito_client_id, options={
|
|
||||||
'verify_exp': False,
|
|
||||||
})
|
|
||||||
except jose_exceptions.JWTError as err:
|
|
||||||
raise ValueError(str(err)) from None
|
|
||||||
|
|
|
@ -146,7 +146,8 @@ async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool:
|
||||||
hass.data[DOMAIN] = {'elk': elk, 'config': config, 'keypads': {}}
|
hass.data[DOMAIN] = {'elk': elk, 'config': config, 'keypads': {}}
|
||||||
for component in SUPPORTED_DOMAINS:
|
for component in SUPPORTED_DOMAINS:
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
discovery.async_load_platform(hass, component, DOMAIN, {}))
|
discovery.async_load_platform(hass, component, DOMAIN, {},
|
||||||
|
hass_config))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,6 @@ def setup(hass, config):
|
||||||
|
|
||||||
_LOGGER.debug("setup(), location = %s", tmp_loc)
|
_LOGGER.debug("setup(), location = %s", tmp_loc)
|
||||||
|
|
||||||
load_platform(hass, 'climate', DOMAIN)
|
load_platform(hass, 'climate', DOMAIN, {}, config)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -88,7 +88,7 @@ DEVICE_SCHEMA = vol.Schema({
|
||||||
}, extra=vol.ALLOW_EXTRA)
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
|
||||||
def do_authentication(hass, config):
|
def do_authentication(hass, hass_config, config):
|
||||||
"""Notify user of actions and authenticate.
|
"""Notify user of actions and authenticate.
|
||||||
|
|
||||||
Notify user of user_code and verification_url then poll
|
Notify user of user_code and verification_url then poll
|
||||||
|
@ -145,7 +145,7 @@ def do_authentication(hass, config):
|
||||||
|
|
||||||
storage = Storage(hass.config.path(TOKEN_FILE))
|
storage = Storage(hass.config.path(TOKEN_FILE))
|
||||||
storage.put(credentials)
|
storage.put(credentials)
|
||||||
do_setup(hass, config)
|
do_setup(hass, hass_config, config)
|
||||||
listener()
|
listener()
|
||||||
hass.components.persistent_notification.create(
|
hass.components.persistent_notification.create(
|
||||||
'We are all setup now. Check {} for calendars that have '
|
'We are all setup now. Check {} for calendars that have '
|
||||||
|
@ -167,14 +167,15 @@ def setup(hass, config):
|
||||||
|
|
||||||
token_file = hass.config.path(TOKEN_FILE)
|
token_file = hass.config.path(TOKEN_FILE)
|
||||||
if not os.path.isfile(token_file):
|
if not os.path.isfile(token_file):
|
||||||
do_authentication(hass, conf)
|
do_authentication(hass, config, conf)
|
||||||
else:
|
else:
|
||||||
do_setup(hass, conf)
|
do_setup(hass, config, conf)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup_services(hass, track_new_found_calendars, calendar_service):
|
def setup_services(hass, hass_config, track_new_found_calendars,
|
||||||
|
calendar_service):
|
||||||
"""Set up the service listeners."""
|
"""Set up the service listeners."""
|
||||||
def _found_calendar(call):
|
def _found_calendar(call):
|
||||||
"""Check if we know about a calendar and generate PLATFORM_DISCOVER."""
|
"""Check if we know about a calendar and generate PLATFORM_DISCOVER."""
|
||||||
|
@ -190,7 +191,8 @@ def setup_services(hass, track_new_found_calendars, calendar_service):
|
||||||
)
|
)
|
||||||
|
|
||||||
discovery.load_platform(hass, 'calendar', DOMAIN,
|
discovery.load_platform(hass, 'calendar', DOMAIN,
|
||||||
hass.data[DATA_INDEX][calendar[CONF_CAL_ID]])
|
hass.data[DATA_INDEX][calendar[CONF_CAL_ID]],
|
||||||
|
hass_config)
|
||||||
|
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_FOUND_CALENDARS, _found_calendar)
|
DOMAIN, SERVICE_FOUND_CALENDARS, _found_calendar)
|
||||||
|
@ -210,7 +212,7 @@ def setup_services(hass, track_new_found_calendars, calendar_service):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def do_setup(hass, config):
|
def do_setup(hass, hass_config, config):
|
||||||
"""Run the setup after we have everything configured."""
|
"""Run the setup after we have everything configured."""
|
||||||
# Load calendars the user has configured
|
# Load calendars the user has configured
|
||||||
hass.data[DATA_INDEX] = load_config(hass.config.path(YAML_DEVICES))
|
hass.data[DATA_INDEX] = load_config(hass.config.path(YAML_DEVICES))
|
||||||
|
@ -218,13 +220,15 @@ def do_setup(hass, config):
|
||||||
calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE))
|
calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE))
|
||||||
track_new_found_calendars = convert(config.get(CONF_TRACK_NEW),
|
track_new_found_calendars = convert(config.get(CONF_TRACK_NEW),
|
||||||
bool, DEFAULT_CONF_TRACK_NEW)
|
bool, DEFAULT_CONF_TRACK_NEW)
|
||||||
setup_services(hass, track_new_found_calendars, calendar_service)
|
setup_services(hass, hass_config, track_new_found_calendars,
|
||||||
|
calendar_service)
|
||||||
|
|
||||||
# Ensure component is loaded
|
# Ensure component is loaded
|
||||||
setup_component(hass, 'calendar', config)
|
setup_component(hass, 'calendar', config)
|
||||||
|
|
||||||
for calendar in hass.data[DATA_INDEX].values():
|
for calendar in hass.data[DATA_INDEX].values():
|
||||||
discovery.load_platform(hass, 'calendar', DOMAIN, calendar)
|
discovery.load_platform(hass, 'calendar', DOMAIN, calendar,
|
||||||
|
hass_config)
|
||||||
|
|
||||||
# Look for any new calendars
|
# Look for any new calendars
|
||||||
hass.services.call(DOMAIN, SERVICE_SCAN_CALENDARS, None)
|
hass.services.call(DOMAIN, SERVICE_SCAN_CALENDARS, None)
|
||||||
|
|
|
@ -73,8 +73,8 @@ def setup(hass, config):
|
||||||
if connection_failed >= len(gateways):
|
if connection_failed >= len(gateways):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
load_platform(hass, 'climate', DOMAIN)
|
load_platform(hass, 'climate', DOMAIN, {}, config)
|
||||||
load_platform(hass, 'binary_sensor', DOMAIN)
|
load_platform(hass, 'binary_sensor', DOMAIN, {}, config)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,6 @@ def setup(hass, config):
|
||||||
api = melissa.Melissa(username=username, password=password)
|
api = melissa.Melissa(username=username, password=password)
|
||||||
hass.data[DATA_MELISSA] = api
|
hass.data[DATA_MELISSA] = api
|
||||||
|
|
||||||
load_platform(hass, 'sensor', DOMAIN, {})
|
load_platform(hass, 'sensor', DOMAIN, {}, config)
|
||||||
load_platform(hass, 'climate', DOMAIN, {})
|
load_platform(hass, 'climate', DOMAIN, {}, config)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -111,7 +111,7 @@ async def async_setup(hass, config):
|
||||||
|
|
||||||
hass.data[MYSENSORS_GATEWAYS] = gateways
|
hass.data[MYSENSORS_GATEWAYS] = gateways
|
||||||
|
|
||||||
hass.async_create_task(finish_setup(hass, gateways))
|
hass.async_create_task(finish_setup(hass, config, gateways))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ async def _get_gateway(hass, config, gateway_conf, persistence_file):
|
||||||
gateway.metric = hass.config.units.is_metric
|
gateway.metric = hass.config.units.is_metric
|
||||||
gateway.optimistic = conf[CONF_OPTIMISTIC]
|
gateway.optimistic = conf[CONF_OPTIMISTIC]
|
||||||
gateway.device = device
|
gateway.device = device
|
||||||
gateway.event_callback = _gw_callback_factory(hass)
|
gateway.event_callback = _gw_callback_factory(hass, config)
|
||||||
gateway.nodes_config = gateway_conf[CONF_NODES]
|
gateway.nodes_config = gateway_conf[CONF_NODES]
|
||||||
if persistence:
|
if persistence:
|
||||||
await gateway.start_persistence()
|
await gateway.start_persistence()
|
||||||
|
@ -145,12 +145,13 @@ async def _get_gateway(hass, config, gateway_conf, persistence_file):
|
||||||
return gateway
|
return gateway
|
||||||
|
|
||||||
|
|
||||||
async def finish_setup(hass, gateways):
|
async def finish_setup(hass, hass_config, gateways):
|
||||||
"""Load any persistent devices and platforms and start gateway."""
|
"""Load any persistent devices and platforms and start gateway."""
|
||||||
discover_tasks = []
|
discover_tasks = []
|
||||||
start_tasks = []
|
start_tasks = []
|
||||||
for gateway in gateways.values():
|
for gateway in gateways.values():
|
||||||
discover_tasks.append(_discover_persistent_devices(hass, gateway))
|
discover_tasks.append(_discover_persistent_devices(
|
||||||
|
hass, hass_config, gateway))
|
||||||
start_tasks.append(_gw_start(hass, gateway))
|
start_tasks.append(_gw_start(hass, gateway))
|
||||||
if discover_tasks:
|
if discover_tasks:
|
||||||
# Make sure all devices and platforms are loaded before gateway start.
|
# Make sure all devices and platforms are loaded before gateway start.
|
||||||
|
@ -159,7 +160,7 @@ async def finish_setup(hass, gateways):
|
||||||
await asyncio.wait(start_tasks, loop=hass.loop)
|
await asyncio.wait(start_tasks, loop=hass.loop)
|
||||||
|
|
||||||
|
|
||||||
async def _discover_persistent_devices(hass, gateway):
|
async def _discover_persistent_devices(hass, hass_config, gateway):
|
||||||
"""Discover platforms for devices loaded via persistence file."""
|
"""Discover platforms for devices loaded via persistence file."""
|
||||||
tasks = []
|
tasks = []
|
||||||
new_devices = defaultdict(list)
|
new_devices = defaultdict(list)
|
||||||
|
@ -170,17 +171,18 @@ async def _discover_persistent_devices(hass, gateway):
|
||||||
for platform, dev_ids in validated.items():
|
for platform, dev_ids in validated.items():
|
||||||
new_devices[platform].extend(dev_ids)
|
new_devices[platform].extend(dev_ids)
|
||||||
for platform, dev_ids in new_devices.items():
|
for platform, dev_ids in new_devices.items():
|
||||||
tasks.append(_discover_mysensors_platform(hass, platform, dev_ids))
|
tasks.append(_discover_mysensors_platform(
|
||||||
|
hass, hass_config, platform, dev_ids))
|
||||||
if tasks:
|
if tasks:
|
||||||
await asyncio.wait(tasks, loop=hass.loop)
|
await asyncio.wait(tasks, loop=hass.loop)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _discover_mysensors_platform(hass, platform, new_devices):
|
def _discover_mysensors_platform(hass, hass_config, platform, new_devices):
|
||||||
"""Discover a MySensors platform."""
|
"""Discover a MySensors platform."""
|
||||||
task = hass.async_create_task(discovery.async_load_platform(
|
task = hass.async_create_task(discovery.async_load_platform(
|
||||||
hass, platform, DOMAIN,
|
hass, platform, DOMAIN,
|
||||||
{ATTR_DEVICES: new_devices, CONF_NAME: DOMAIN}))
|
{ATTR_DEVICES: new_devices, CONF_NAME: DOMAIN}, hass_config))
|
||||||
return task
|
return task
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,7 +217,7 @@ async def _gw_start(hass, gateway):
|
||||||
hass.data.pop(gateway_ready_key, None)
|
hass.data.pop(gateway_ready_key, None)
|
||||||
|
|
||||||
|
|
||||||
def _gw_callback_factory(hass):
|
def _gw_callback_factory(hass, hass_config):
|
||||||
"""Return a new callback for the gateway."""
|
"""Return a new callback for the gateway."""
|
||||||
@callback
|
@callback
|
||||||
def mysensors_callback(msg):
|
def mysensors_callback(msg):
|
||||||
|
@ -246,7 +248,8 @@ def _gw_callback_factory(hass):
|
||||||
else:
|
else:
|
||||||
new_dev_ids.append(dev_id)
|
new_dev_ids.append(dev_id)
|
||||||
if new_dev_ids:
|
if new_dev_ids:
|
||||||
_discover_mysensors_platform(hass, platform, new_dev_ids)
|
_discover_mysensors_platform(
|
||||||
|
hass, hass_config, platform, new_dev_ids)
|
||||||
for signal in set(signals):
|
for signal in set(signals):
|
||||||
# Only one signal per device is needed.
|
# Only one signal per device is needed.
|
||||||
# A device can have multiple platforms, ie multiple schemas.
|
# A device can have multiple platforms, ie multiple schemas.
|
||||||
|
|
|
@ -114,11 +114,12 @@ def setup(hass, config):
|
||||||
sensors = printer[CONF_SENSORS][CONF_MONITORED_CONDITIONS]
|
sensors = printer[CONF_SENSORS][CONF_MONITORED_CONDITIONS]
|
||||||
load_platform(hass, 'sensor', DOMAIN, {'name': name,
|
load_platform(hass, 'sensor', DOMAIN, {'name': name,
|
||||||
'base_url': base_url,
|
'base_url': base_url,
|
||||||
'sensors': sensors})
|
'sensors': sensors}, config)
|
||||||
b_sensors = printer[CONF_BINARY_SENSORS][CONF_MONITORED_CONDITIONS]
|
b_sensors = printer[CONF_BINARY_SENSORS][CONF_MONITORED_CONDITIONS]
|
||||||
load_platform(hass, 'binary_sensor', DOMAIN, {'name': name,
|
load_platform(hass, 'binary_sensor', DOMAIN, {'name': name,
|
||||||
'base_url': base_url,
|
'base_url': base_url,
|
||||||
'sensors': b_sensors})
|
'sensors': b_sensors},
|
||||||
|
config)
|
||||||
success = True
|
success = True
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
|
@ -64,9 +64,10 @@ async def async_setup(hass, config):
|
||||||
hass.async_create_task(connect_and_subscribe(
|
hass.async_create_task(connect_and_subscribe(
|
||||||
hass, conf[CONF_DEVICE], gateway))
|
hass, conf[CONF_DEVICE], gateway))
|
||||||
hass.async_create_task(async_load_platform(
|
hass.async_create_task(async_load_platform(
|
||||||
hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE)))
|
hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE), config))
|
||||||
if monitored_vars:
|
if monitored_vars:
|
||||||
hass.async_create_task(setup_monitored_vars(hass, monitored_vars))
|
hass.async_create_task(setup_monitored_vars(
|
||||||
|
hass, config, monitored_vars))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ async def connect_and_subscribe(hass, device_path, gateway):
|
||||||
gateway.subscribe(handle_report)
|
gateway.subscribe(handle_report)
|
||||||
|
|
||||||
|
|
||||||
async def setup_monitored_vars(hass, monitored_vars):
|
async def setup_monitored_vars(hass, config, monitored_vars):
|
||||||
"""Set up requested sensors."""
|
"""Set up requested sensors."""
|
||||||
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS]
|
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS]
|
||||||
sensor_type_map = {
|
sensor_type_map = {
|
||||||
|
@ -200,6 +201,6 @@ async def setup_monitored_vars(hass, monitored_vars):
|
||||||
_LOGGER.error("Monitored variable not supported: %s", var)
|
_LOGGER.error("Monitored variable not supported: %s", var)
|
||||||
if binary_sensors:
|
if binary_sensors:
|
||||||
hass.async_create_task(async_load_platform(
|
hass.async_create_task(async_load_platform(
|
||||||
hass, COMP_BINARY_SENSOR, DOMAIN, binary_sensors))
|
hass, COMP_BINARY_SENSOR, DOMAIN, binary_sensors, config))
|
||||||
if sensors:
|
if sensors:
|
||||||
await async_load_platform(hass, COMP_SENSOR, DOMAIN, sensors)
|
await async_load_platform(hass, COMP_SENSOR, DOMAIN, sensors, config)
|
||||||
|
|
|
@ -66,8 +66,8 @@ def setup(hass, config):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
hass.data[DATA_SMAPPEE] = smappee
|
hass.data[DATA_SMAPPEE] = smappee
|
||||||
load_platform(hass, 'switch', DOMAIN)
|
load_platform(hass, 'switch', DOMAIN, {}, config)
|
||||||
load_platform(hass, 'sensor', DOMAIN)
|
load_platform(hass, 'sensor', DOMAIN, {}, config)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -63,11 +63,11 @@ async def async_setup(hass, config):
|
||||||
|
|
||||||
# add sensor devices for each zone (typically motion/fire/door sensors)
|
# add sensor devices for each zone (typically motion/fire/door sensors)
|
||||||
hass.async_create_task(discovery.async_load_platform(
|
hass.async_create_task(discovery.async_load_platform(
|
||||||
hass, 'binary_sensor', DOMAIN, {}))
|
hass, 'binary_sensor', DOMAIN, {}, config))
|
||||||
|
|
||||||
# create a separate alarm panel for each area
|
# create a separate alarm panel for each area
|
||||||
hass.async_create_task(discovery.async_load_platform(
|
hass.async_create_task(discovery.async_load_platform(
|
||||||
hass, 'alarm_control_panel', DOMAIN, {}))
|
hass, 'alarm_control_panel', DOMAIN, {}, config))
|
||||||
|
|
||||||
# start listening for incoming events over websocket
|
# start listening for incoming events over websocket
|
||||||
spc.start()
|
spc.start()
|
||||||
|
|
|
@ -56,7 +56,7 @@ def setup(hass, config):
|
||||||
}
|
}
|
||||||
|
|
||||||
for component in SPIDER_COMPONENTS:
|
for component in SPIDER_COMPONENTS:
|
||||||
load_platform(hass, component, DOMAIN, {})
|
load_platform(hass, component, DOMAIN, {}, config)
|
||||||
|
|
||||||
_LOGGER.debug("Connection with Spider API succeeded")
|
_LOGGER.debug("Connection with Spider API succeeded")
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -32,20 +32,21 @@ async def async_setup(hass, config):
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry):
|
async def async_setup_entry(hass, config_entry):
|
||||||
"""Set up the UniFi component."""
|
"""Set up the UniFi component."""
|
||||||
controller = UniFiController(hass, config_entry)
|
|
||||||
|
|
||||||
if DOMAIN not in hass.data:
|
if DOMAIN not in hass.data:
|
||||||
hass.data[DOMAIN] = {}
|
hass.data[DOMAIN] = {}
|
||||||
|
|
||||||
|
controller = UniFiController(hass, config_entry)
|
||||||
|
|
||||||
controller_id = CONTROLLER_ID.format(
|
controller_id = CONTROLLER_ID.format(
|
||||||
host=config_entry.data[CONF_CONTROLLER][CONF_HOST],
|
host=config_entry.data[CONF_CONTROLLER][CONF_HOST],
|
||||||
site=config_entry.data[CONF_CONTROLLER][CONF_SITE_ID]
|
site=config_entry.data[CONF_CONTROLLER][CONF_SITE_ID]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
hass.data[DOMAIN][controller_id] = controller
|
||||||
|
|
||||||
if not await controller.async_setup():
|
if not await controller.async_setup():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
hass.data[DOMAIN][controller_id] = controller
|
|
||||||
|
|
||||||
if controller.mac is None:
|
if controller.mac is None:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 0
|
MAJOR_VERSION = 0
|
||||||
MINOR_VERSION = 81
|
MINOR_VERSION = 81
|
||||||
PATCH_VERSION = '1'
|
PATCH_VERSION = '2'
|
||||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||||
REQUIRED_PYTHON_VER = (3, 5, 3)
|
REQUIRED_PYTHON_VER = (3, 5, 3)
|
||||||
|
|
|
@ -114,8 +114,7 @@ def async_listen_platform(hass, component, callback):
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def load_platform(hass, component, platform, discovered=None,
|
def load_platform(hass, component, platform, discovered, hass_config):
|
||||||
hass_config=None):
|
|
||||||
"""Load a component and platform dynamically.
|
"""Load a component and platform dynamically.
|
||||||
|
|
||||||
Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be
|
Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be
|
||||||
|
@ -132,8 +131,8 @@ def load_platform(hass, component, platform, discovered=None,
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
async def async_load_platform(hass, component, platform, discovered=None,
|
async def async_load_platform(hass, component, platform, discovered,
|
||||||
hass_config=None):
|
hass_config):
|
||||||
"""Load a component and platform dynamically.
|
"""Load a component and platform dynamically.
|
||||||
|
|
||||||
Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be
|
Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be
|
||||||
|
@ -149,6 +148,8 @@ async def async_load_platform(hass, component, platform, discovered=None,
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
"""
|
"""
|
||||||
|
assert hass_config, 'You need to pass in the real hass config'
|
||||||
|
|
||||||
if component in DEPENDENCY_BLACKLIST:
|
if component in DEPENDENCY_BLACKLIST:
|
||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
'Cannot discover the {} component.'.format(component))
|
'Cannot discover the {} component.'.format(component))
|
||||||
|
|
|
@ -10,7 +10,7 @@ cryptography==2.3.1
|
||||||
pip>=8.0.3
|
pip>=8.0.3
|
||||||
pytz>=2018.04
|
pytz>=2018.04
|
||||||
pyyaml>=3.13,<4
|
pyyaml>=3.13,<4
|
||||||
requests==2.19.1
|
requests==2.20.0
|
||||||
voluptuous==0.11.5
|
voluptuous==0.11.5
|
||||||
voluptuous-serialize==2.0.0
|
voluptuous-serialize==2.0.0
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ cryptography==2.3.1
|
||||||
pip>=8.0.3
|
pip>=8.0.3
|
||||||
pytz>=2018.04
|
pytz>=2018.04
|
||||||
pyyaml>=3.13,<4
|
pyyaml>=3.13,<4
|
||||||
requests==2.19.1
|
requests==2.20.0
|
||||||
voluptuous==0.11.5
|
voluptuous==0.11.5
|
||||||
voluptuous-serialize==2.0.0
|
voluptuous-serialize==2.0.0
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -45,7 +45,7 @@ REQUIRES = [
|
||||||
'pip>=8.0.3',
|
'pip>=8.0.3',
|
||||||
'pytz>=2018.04',
|
'pytz>=2018.04',
|
||||||
'pyyaml>=3.13,<4',
|
'pyyaml>=3.13,<4',
|
||||||
'requests==2.19.1',
|
'requests==2.20.0',
|
||||||
'voluptuous==0.11.5',
|
'voluptuous==0.11.5',
|
||||||
'voluptuous-serialize==2.0.0',
|
'voluptuous-serialize==2.0.0',
|
||||||
]
|
]
|
||||||
|
|
|
@ -32,8 +32,7 @@ def test_constructor_loads_info_from_constant():
|
||||||
'google_actions_sync_url': 'test-google_actions_sync_url',
|
'google_actions_sync_url': 'test-google_actions_sync_url',
|
||||||
'subscription_info_url': 'test-subscription-info-url'
|
'subscription_info_url': 'test-subscription-info-url'
|
||||||
}
|
}
|
||||||
}), patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
|
}):
|
||||||
return_value=mock_coro(True)):
|
|
||||||
result = yield from cloud.async_setup(hass, {
|
result = yield from cloud.async_setup(hass, {
|
||||||
'cloud': {cloud.CONF_MODE: 'beer'}
|
'cloud': {cloud.CONF_MODE: 'beer'}
|
||||||
})
|
})
|
||||||
|
@ -54,17 +53,15 @@ def test_constructor_loads_info_from_config():
|
||||||
"""Test non-dev mode loads info from SERVERS constant."""
|
"""Test non-dev mode loads info from SERVERS constant."""
|
||||||
hass = MagicMock(data={})
|
hass = MagicMock(data={})
|
||||||
|
|
||||||
with patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
|
result = yield from cloud.async_setup(hass, {
|
||||||
return_value=mock_coro(True)):
|
'cloud': {
|
||||||
result = yield from cloud.async_setup(hass, {
|
cloud.CONF_MODE: cloud.MODE_DEV,
|
||||||
'cloud': {
|
'cognito_client_id': 'test-cognito_client_id',
|
||||||
cloud.CONF_MODE: cloud.MODE_DEV,
|
'user_pool_id': 'test-user_pool_id',
|
||||||
'cognito_client_id': 'test-cognito_client_id',
|
'region': 'test-region',
|
||||||
'user_pool_id': 'test-user_pool_id',
|
'relayer': 'test-relayer',
|
||||||
'region': 'test-region',
|
}
|
||||||
'relayer': 'test-relayer',
|
})
|
||||||
}
|
|
||||||
})
|
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
cl = hass.data['cloud']
|
cl = hass.data['cloud']
|
||||||
|
@ -89,8 +86,6 @@ async def test_initialize_loads_info(mock_os, hass):
|
||||||
cl.iot.connect.return_value = mock_coro()
|
cl.iot.connect.return_value = mock_coro()
|
||||||
|
|
||||||
with patch('homeassistant.components.cloud.open', mopen, create=True), \
|
with patch('homeassistant.components.cloud.open', mopen, create=True), \
|
||||||
patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
|
|
||||||
return_value=mock_coro(True)), \
|
|
||||||
patch('homeassistant.components.cloud.Cloud._decode_claims'):
|
patch('homeassistant.components.cloud.Cloud._decode_claims'):
|
||||||
await cl.async_start(None)
|
await cl.async_start(None)
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
||||||
assert device_tracker.DOMAIN not in self.hass.config.components
|
assert device_tracker.DOMAIN not in self.hass.config.components
|
||||||
discovery.load_platform(
|
discovery.load_platform(
|
||||||
self.hass, device_tracker.DOMAIN, 'demo', {'test_key': 'test_val'},
|
self.hass, device_tracker.DOMAIN, 'demo', {'test_key': 'test_val'},
|
||||||
{})
|
{'demo': {}})
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
assert device_tracker.DOMAIN in self.hass.config.components
|
assert device_tracker.DOMAIN in self.hass.config.components
|
||||||
assert mock_demo_setup_scanner.called
|
assert mock_demo_setup_scanner.called
|
||||||
|
|
|
@ -674,7 +674,7 @@ class TestLightMQTTJSON(unittest.TestCase):
|
||||||
|
|
||||||
async def test_discovery_removal(hass, mqtt_mock, caplog):
|
async def test_discovery_removal(hass, mqtt_mock, caplog):
|
||||||
"""Test removal of discovered mqtt_json lights."""
|
"""Test removal of discovered mqtt_json lights."""
|
||||||
await async_start(hass, 'homeassistant', {})
|
await async_start(hass, 'homeassistant', {'mqtt': {}})
|
||||||
data = (
|
data = (
|
||||||
'{ "name": "Beer",'
|
'{ "name": "Beer",'
|
||||||
' "platform": "mqtt_json",'
|
' "platform": "mqtt_json",'
|
||||||
|
|
|
@ -180,7 +180,7 @@ class TestLockMQTT(unittest.TestCase):
|
||||||
|
|
||||||
async def test_discovery_removal_lock(hass, mqtt_mock, caplog):
|
async def test_discovery_removal_lock(hass, mqtt_mock, caplog):
|
||||||
"""Test removal of discovered lock."""
|
"""Test removal of discovered lock."""
|
||||||
await async_start(hass, 'homeassistant', {})
|
await async_start(hass, 'homeassistant', {'mqtt': {}})
|
||||||
data = (
|
data = (
|
||||||
'{ "name": "Beer",'
|
'{ "name": "Beer",'
|
||||||
' "command_topic": "test_topic" }'
|
' "command_topic": "test_topic" }'
|
||||||
|
|
|
@ -66,7 +66,8 @@ class TestNotifyDemo(unittest.TestCase):
|
||||||
"""Test discovery of notify demo platform."""
|
"""Test discovery of notify demo platform."""
|
||||||
assert notify.DOMAIN not in self.hass.config.components
|
assert notify.DOMAIN not in self.hass.config.components
|
||||||
discovery.load_platform(
|
discovery.load_platform(
|
||||||
self.hass, 'notify', 'demo', {'test_key': 'test_val'}, {})
|
self.hass, 'notify', 'demo', {'test_key': 'test_val'},
|
||||||
|
{'notify': {}})
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
assert notify.DOMAIN in self.hass.config.components
|
assert notify.DOMAIN in self.hass.config.components
|
||||||
assert mock_demo_get_service.called
|
assert mock_demo_get_service.called
|
||||||
|
|
|
@ -85,8 +85,8 @@ class TestGoogle(unittest.TestCase):
|
||||||
|
|
||||||
calendar_service = google.GoogleCalendarService(
|
calendar_service = google.GoogleCalendarService(
|
||||||
self.hass.config.path(google.TOKEN_FILE))
|
self.hass.config.path(google.TOKEN_FILE))
|
||||||
self.assertTrue(google.setup_services(self.hass, True,
|
assert google.setup_services(
|
||||||
calendar_service))
|
self.hass, {'google': {}}, True, calendar_service)
|
||||||
# self.hass.services.call('google', 'found_calendar', calendar,
|
# self.hass.services.call('google', 'found_calendar', calendar,
|
||||||
# blocking=True)
|
# blocking=True)
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ async def test_successful_config_entry(hass):
|
||||||
|
|
||||||
|
|
||||||
async def test_controller_fail_setup(hass):
|
async def test_controller_fail_setup(hass):
|
||||||
"""Test that configured options for a host are loaded via config entry."""
|
"""Test that a failed setup still stores controller."""
|
||||||
entry = MockConfigEntry(domain=unifi.DOMAIN, data={
|
entry = MockConfigEntry(domain=unifi.DOMAIN, data={
|
||||||
'controller': {
|
'controller': {
|
||||||
'host': '0.0.0.0',
|
'host': '0.0.0.0',
|
||||||
|
@ -75,7 +75,7 @@ async def test_controller_fail_setup(hass):
|
||||||
controller_id = unifi.CONTROLLER_ID.format(
|
controller_id = unifi.CONTROLLER_ID.format(
|
||||||
host='0.0.0.0', site='default'
|
host='0.0.0.0', site='default'
|
||||||
)
|
)
|
||||||
assert controller_id not in hass.data[unifi.DOMAIN]
|
assert controller_id in hass.data[unifi.DOMAIN]
|
||||||
|
|
||||||
|
|
||||||
async def test_controller_no_mac(hass):
|
async def test_controller_no_mac(hass):
|
||||||
|
|
|
@ -79,15 +79,15 @@ class TestHelpersDiscovery:
|
||||||
platform_callback)
|
platform_callback)
|
||||||
|
|
||||||
discovery.load_platform(self.hass, 'test_component', 'test_platform',
|
discovery.load_platform(self.hass, 'test_component', 'test_platform',
|
||||||
'discovery info')
|
'discovery info', {'test_component': {}})
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
assert mock_setup_component.called
|
assert mock_setup_component.called
|
||||||
assert mock_setup_component.call_args[0] == \
|
assert mock_setup_component.call_args[0] == \
|
||||||
(self.hass, 'test_component', None)
|
(self.hass, 'test_component', {'test_component': {}})
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
|
|
||||||
discovery.load_platform(self.hass, 'test_component_2', 'test_platform',
|
discovery.load_platform(self.hass, 'test_component_2', 'test_platform',
|
||||||
'discovery info')
|
'discovery info', {'test_component': {}})
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
|
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
|
@ -202,7 +202,8 @@ class TestHelpersDiscovery:
|
||||||
async def test_load_platform_forbids_config():
|
async def test_load_platform_forbids_config():
|
||||||
"""Test you cannot setup config component with load_platform."""
|
"""Test you cannot setup config component with load_platform."""
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError):
|
||||||
await discovery.async_load_platform(None, 'config', 'zwave')
|
await discovery.async_load_platform(None, 'config', 'zwave', {},
|
||||||
|
{'config': {}})
|
||||||
|
|
||||||
|
|
||||||
async def test_discover_forbids_config():
|
async def test_discover_forbids_config():
|
||||||
|
|
|
@ -131,7 +131,7 @@ class TestHelpersEntityComponent(unittest.TestCase):
|
||||||
component.setup({})
|
component.setup({})
|
||||||
|
|
||||||
discovery.load_platform(self.hass, DOMAIN, 'platform_test',
|
discovery.load_platform(self.hass, DOMAIN, 'platform_test',
|
||||||
{'msg': 'discovery_info'})
|
{'msg': 'discovery_info'}, {DOMAIN: {}})
|
||||||
|
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue