Bootstrap / Component setup async (#6264)
* Bootstrap / Entiy setup async * Cleanup add_job stuff / return task/future object * Address paulus comments / part 1 * fix install pip * Cleanup bootstrap / move config stuff to config.py * Make demo async * Further bootstrap improvement * Address Martin's comments * Fix initial tests * Fix final tests * Fix bug with prepare loader * Remove no longer needed things * Log error when invalid config * More cleanup * Cleanups platform events & fix lint * Use a non blocking add_entities callback for platform * Fix Autoamtion is setup befor entity is ready * Better automation fix * Address paulus comments * Typo * fix lint * rename functions * fix tests * fix test * change exceptions * fix spell
This commit is contained in:
parent
383b0914b3
commit
41f558b181
109 changed files with 764 additions and 848 deletions
|
@ -4,38 +4,41 @@ import logging
|
|||
import logging.handlers
|
||||
import os
|
||||
import sys
|
||||
from time import time
|
||||
from collections import OrderedDict
|
||||
|
||||
from types import ModuleType
|
||||
from typing import Any, Optional, Dict
|
||||
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
import homeassistant.components as core_components
|
||||
from homeassistant.components import persistent_notification
|
||||
import homeassistant.config as conf_util
|
||||
from homeassistant.config import async_notify_setup_error
|
||||
import homeassistant.core as core
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE
|
||||
import homeassistant.loader as loader
|
||||
import homeassistant.util.package as pkg_util
|
||||
from homeassistant.util.async import (
|
||||
run_coroutine_threadsafe, run_callback_threadsafe)
|
||||
from homeassistant.util.async import run_coroutine_threadsafe
|
||||
from homeassistant.util.logging import AsyncHandler
|
||||
from homeassistant.util.yaml import clear_secret_cache
|
||||
from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import (
|
||||
event_decorators, service, config_per_platform, extract_domain_configs)
|
||||
from homeassistant.helpers import event_decorators, service
|
||||
from homeassistant.helpers.signal import async_register_signal_handling
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_COMPONENT = 'component'
|
||||
|
||||
DATA_SETUP = 'setup_tasks'
|
||||
DATA_PIP_LOCK = 'pip_lock'
|
||||
|
||||
ERROR_LOG_FILENAME = 'home-assistant.log'
|
||||
DATA_PERSISTENT_ERRORS = 'bootstrap_persistent_errors'
|
||||
HA_COMPONENT_URL = '[{}](https://home-assistant.io/components/{}/)'
|
||||
|
||||
FIRST_INIT_COMPONENT = set((
|
||||
'recorder', 'mqtt', 'mqtt_eventstream', 'logger', 'introduction'))
|
||||
|
||||
|
||||
def setup_component(hass: core.HomeAssistant, domain: str,
|
||||
|
@ -52,49 +55,82 @@ def async_setup_component(hass: core.HomeAssistant, domain: str,
|
|||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
if domain in hass.config.components:
|
||||
_LOGGER.debug('Component %s already set up.', domain)
|
||||
return True
|
||||
setup_tasks = hass.data.get(DATA_SETUP)
|
||||
|
||||
if not loader.PREPARED:
|
||||
yield from hass.loop.run_in_executor(None, loader.prepare, hass)
|
||||
if setup_tasks is not None and domain in setup_tasks:
|
||||
return (yield from setup_tasks[domain])
|
||||
|
||||
if config is None:
|
||||
config = {}
|
||||
|
||||
components = loader.load_order_component(domain)
|
||||
if setup_tasks is None:
|
||||
setup_tasks = hass.data[DATA_SETUP] = {}
|
||||
|
||||
# OrderedSet is empty if component or dependencies could not be resolved
|
||||
if not components:
|
||||
_async_persistent_notification(hass, domain, True)
|
||||
return False
|
||||
task = setup_tasks[domain] = hass.async_add_job(
|
||||
_async_setup_component(hass, domain, config))
|
||||
|
||||
for component in components:
|
||||
res = yield from _async_setup_component(hass, component, config)
|
||||
if not res:
|
||||
_LOGGER.error('Component %s failed to setup', component)
|
||||
_async_persistent_notification(hass, component, True)
|
||||
return False
|
||||
return (yield from task)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _async_process_requirements(hass: core.HomeAssistant, name: str,
|
||||
requirements) -> bool:
|
||||
"""Install the requirements for a component.
|
||||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
if hass.config.skip_pip:
|
||||
return True
|
||||
|
||||
pip_lock = hass.data.get(DATA_PIP_LOCK)
|
||||
if pip_lock is None:
|
||||
pip_lock = hass.data[DATA_PIP_LOCK] = asyncio.Lock(loop=hass.loop)
|
||||
|
||||
def pip_install(mod):
|
||||
"""Install packages."""
|
||||
return pkg_util.install_package(mod, target=hass.config.path('deps'))
|
||||
|
||||
with (yield from pip_lock):
|
||||
for req in requirements:
|
||||
ret = yield from hass.loop.run_in_executor(None, pip_install, req)
|
||||
if not ret:
|
||||
_LOGGER.error('Not initializing %s because could not install '
|
||||
'dependency %s', name, req)
|
||||
async_notify_setup_error(hass, name)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _handle_requirements(hass: core.HomeAssistant, component,
|
||||
name: str) -> bool:
|
||||
"""Install the requirements for a component.
|
||||
@asyncio.coroutine
|
||||
def _async_process_dependencies(hass, config, name, dependencies):
|
||||
"""Ensure all dependencies are set up."""
|
||||
blacklisted = [dep for dep in dependencies
|
||||
if dep in loader.DEPENDENCY_BLACKLIST]
|
||||
|
||||
This method needs to run in an executor.
|
||||
"""
|
||||
if hass.config.skip_pip or not hasattr(component, 'REQUIREMENTS'):
|
||||
if blacklisted:
|
||||
_LOGGER.error('Unable to setup dependencies of %s: '
|
||||
'found blacklisted dependencies: %s',
|
||||
name, ', '.join(blacklisted))
|
||||
return False
|
||||
|
||||
tasks = [async_setup_component(hass, dep, config) for dep
|
||||
in dependencies]
|
||||
|
||||
if not tasks:
|
||||
return True
|
||||
|
||||
for req in component.REQUIREMENTS:
|
||||
if not pkg_util.install_package(req, target=hass.config.path('deps')):
|
||||
_LOGGER.error('Not initializing %s because could not install '
|
||||
'dependency %s', name, req)
|
||||
_async_persistent_notification(hass, name)
|
||||
return False
|
||||
results = yield from asyncio.gather(*tasks, loop=hass.loop)
|
||||
|
||||
failed = [dependencies[idx] for idx, res
|
||||
in enumerate(results) if not res]
|
||||
|
||||
if failed:
|
||||
_LOGGER.error('Unable to setup dependencies of %s. '
|
||||
'Setup failed for dependencies: %s',
|
||||
name, ', '.join(failed))
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
@ -104,172 +140,78 @@ def _async_setup_component(hass: core.HomeAssistant,
|
|||
"""Setup a component for Home Assistant.
|
||||
|
||||
This method is a coroutine.
|
||||
|
||||
hass: Home Assistant instance.
|
||||
domain: Domain of component to setup.
|
||||
config: The Home Assistant configuration.
|
||||
"""
|
||||
# pylint: disable=too-many-return-statements
|
||||
if domain in hass.config.components:
|
||||
return True
|
||||
def log_error(msg):
|
||||
"""Log helper."""
|
||||
_LOGGER.error('Setup failed for %s: %s', domain, msg)
|
||||
async_notify_setup_error(hass, domain, True)
|
||||
|
||||
setup_lock = hass.data.get('setup_lock')
|
||||
if setup_lock is None:
|
||||
setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop)
|
||||
# Validate no circular dependencies
|
||||
components = loader.load_order_component(domain)
|
||||
|
||||
setup_progress = hass.data.get('setup_progress')
|
||||
if setup_progress is None:
|
||||
setup_progress = hass.data['setup_progress'] = []
|
||||
|
||||
if domain in setup_progress:
|
||||
_LOGGER.error('Attempt made to setup %s during setup of %s',
|
||||
domain, domain)
|
||||
_async_persistent_notification(hass, domain, True)
|
||||
# OrderedSet is empty if component or dependencies could not be resolved
|
||||
if not components:
|
||||
log_error('Unable to resolve component or dependencies')
|
||||
return False
|
||||
|
||||
try:
|
||||
# Used to indicate to discovery that a setup is ongoing and allow it
|
||||
# to wait till it is done.
|
||||
did_lock = False
|
||||
if not setup_lock.locked():
|
||||
yield from setup_lock.acquire()
|
||||
did_lock = True
|
||||
|
||||
setup_progress.append(domain)
|
||||
config = yield from async_prepare_setup_component(hass, config, domain)
|
||||
|
||||
if config is None:
|
||||
return False
|
||||
|
||||
component = loader.get_component(domain)
|
||||
if component is None:
|
||||
_async_persistent_notification(hass, domain)
|
||||
return False
|
||||
|
||||
async_comp = hasattr(component, 'async_setup')
|
||||
|
||||
try:
|
||||
_LOGGER.info("Setting up %s", domain)
|
||||
if async_comp:
|
||||
result = yield from component.async_setup(hass, config)
|
||||
else:
|
||||
result = yield from hass.loop.run_in_executor(
|
||||
None, component.setup, hass, config)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception('Error during setup of component %s', domain)
|
||||
_async_persistent_notification(hass, domain, True)
|
||||
return False
|
||||
|
||||
if result is False:
|
||||
_LOGGER.error('component %s failed to initialize', domain)
|
||||
_async_persistent_notification(hass, domain, True)
|
||||
return False
|
||||
elif result is not True:
|
||||
_LOGGER.error('component %s did not return boolean if setup '
|
||||
'was successful. Disabling component.', domain)
|
||||
_async_persistent_notification(hass, domain, True)
|
||||
loader.set_component(domain, None)
|
||||
return False
|
||||
|
||||
hass.config.components.add(component.DOMAIN)
|
||||
|
||||
hass.bus.async_fire(
|
||||
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}
|
||||
)
|
||||
|
||||
return True
|
||||
finally:
|
||||
setup_progress.remove(domain)
|
||||
if did_lock:
|
||||
setup_lock.release()
|
||||
|
||||
|
||||
def prepare_setup_component(hass: core.HomeAssistant, config: dict,
|
||||
domain: str):
|
||||
"""Prepare setup of a component and return processed config."""
|
||||
return run_coroutine_threadsafe(
|
||||
async_prepare_setup_component(hass, config, domain), loop=hass.loop
|
||||
).result()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_prepare_setup_component(hass: core.HomeAssistant, config: dict,
|
||||
domain: str):
|
||||
"""Prepare setup of a component and return processed config.
|
||||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
# pylint: disable=too-many-return-statements
|
||||
component = loader.get_component(domain)
|
||||
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
|
||||
if dep not in hass.config.components]
|
||||
|
||||
if missing_deps:
|
||||
_LOGGER.error(
|
||||
'Not initializing %s because not all dependencies loaded: %s',
|
||||
domain, ", ".join(missing_deps))
|
||||
return None
|
||||
processed_config = \
|
||||
conf_util.async_process_component_config(hass, config, domain)
|
||||
|
||||
if hasattr(component, 'CONFIG_SCHEMA'):
|
||||
try:
|
||||
config = component.CONFIG_SCHEMA(config)
|
||||
except vol.Invalid as ex:
|
||||
async_log_exception(ex, domain, config, hass)
|
||||
return None
|
||||
if processed_config is None:
|
||||
log_error('Invalid config')
|
||||
return False
|
||||
|
||||
elif hasattr(component, 'PLATFORM_SCHEMA'):
|
||||
platforms = []
|
||||
for p_name, p_config in config_per_platform(config, domain):
|
||||
# Validate component specific platform schema
|
||||
try:
|
||||
p_validated = component.PLATFORM_SCHEMA(p_config)
|
||||
except vol.Invalid as ex:
|
||||
async_log_exception(ex, domain, config, hass)
|
||||
continue
|
||||
if not hass.config.skip_pip and hasattr(component, 'REQUIREMENTS'):
|
||||
req_success = yield from _async_process_requirements(
|
||||
hass, domain, component.REQUIREMENTS)
|
||||
if not req_success:
|
||||
log_error('Could not install all requirements.')
|
||||
return False
|
||||
|
||||
# Not all platform components follow same pattern for platforms
|
||||
# So if p_name is None we are not going to validate platform
|
||||
# (the automation component is one of them)
|
||||
if p_name is None:
|
||||
platforms.append(p_validated)
|
||||
continue
|
||||
if hasattr(component, 'DEPENDENCIES'):
|
||||
dep_success = yield from _async_process_dependencies(
|
||||
hass, config, domain, component.DEPENDENCIES)
|
||||
|
||||
platform = yield from async_prepare_setup_platform(
|
||||
hass, config, domain, p_name)
|
||||
if not dep_success:
|
||||
log_error('Could not setup all dependencies.')
|
||||
return False
|
||||
|
||||
if platform is None:
|
||||
continue
|
||||
async_comp = hasattr(component, 'async_setup')
|
||||
|
||||
# Validate platform specific schema
|
||||
if hasattr(platform, 'PLATFORM_SCHEMA'):
|
||||
try:
|
||||
# pylint: disable=no-member
|
||||
p_validated = platform.PLATFORM_SCHEMA(p_validated)
|
||||
except vol.Invalid as ex:
|
||||
async_log_exception(ex, '{}.{}'.format(domain, p_name),
|
||||
p_validated, hass)
|
||||
continue
|
||||
try:
|
||||
_LOGGER.info("Setting up %s", domain)
|
||||
if async_comp:
|
||||
result = yield from component.async_setup(hass, processed_config)
|
||||
else:
|
||||
result = yield from hass.loop.run_in_executor(
|
||||
None, component.setup, hass, processed_config)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception('Error during setup of component %s', domain)
|
||||
async_notify_setup_error(hass, domain, True)
|
||||
return False
|
||||
|
||||
platforms.append(p_validated)
|
||||
if result is False:
|
||||
log_error('Component failed to initialize.')
|
||||
return False
|
||||
elif result is not True:
|
||||
log_error('Component did not return boolean if setup was successful. '
|
||||
'Disabling component.')
|
||||
loader.set_component(domain, None)
|
||||
return False
|
||||
|
||||
# Create a copy of the configuration with all config for current
|
||||
# component removed and add validated config back in.
|
||||
filter_keys = extract_domain_configs(config, domain)
|
||||
config = {key: value for key, value in config.items()
|
||||
if key not in filter_keys}
|
||||
config[domain] = platforms
|
||||
hass.config.components.add(component.DOMAIN)
|
||||
|
||||
res = yield from hass.loop.run_in_executor(
|
||||
None, _handle_requirements, hass, component, domain)
|
||||
if not res:
|
||||
return None
|
||||
hass.bus.async_fire(
|
||||
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
|
||||
platform_name: str) -> Optional[ModuleType]:
|
||||
"""Load a platform and makes sure dependencies are setup."""
|
||||
return run_coroutine_threadsafe(
|
||||
async_prepare_setup_platform(hass, config, domain, platform_name),
|
||||
loop=hass.loop
|
||||
).result()
|
||||
return True
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
@ -280,17 +222,19 @@ def async_prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
|
|||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
if not loader.PREPARED:
|
||||
yield from hass.loop.run_in_executor(None, loader.prepare, hass)
|
||||
|
||||
platform_path = PLATFORM_FORMAT.format(domain, platform_name)
|
||||
|
||||
def log_error(msg):
|
||||
"""Log helper."""
|
||||
_LOGGER.error('Unable to prepare setup for platform %s: %s',
|
||||
platform_path, msg)
|
||||
async_notify_setup_error(hass, platform_path)
|
||||
|
||||
platform = loader.get_platform(domain, platform_name)
|
||||
|
||||
# Not found
|
||||
if platform is None:
|
||||
_LOGGER.error('Unable to find platform %s', platform_path)
|
||||
_async_persistent_notification(hass, platform_path)
|
||||
log_error('Unable to find platform')
|
||||
return None
|
||||
|
||||
# Already loaded
|
||||
|
@ -298,25 +242,22 @@ def async_prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
|
|||
return platform
|
||||
|
||||
# Load dependencies
|
||||
for component in getattr(platform, 'DEPENDENCIES', []):
|
||||
if component in loader.DEPENDENCY_BLACKLIST:
|
||||
raise HomeAssistantError(
|
||||
'{} is not allowed to be a dependency.'.format(component))
|
||||
if hasattr(platform, 'DEPENDENCIES'):
|
||||
dep_success = yield from _async_process_dependencies(
|
||||
hass, config, platform_path, platform.DEPENDENCIES)
|
||||
|
||||
res = yield from async_setup_component(hass, component, config)
|
||||
if not res:
|
||||
_LOGGER.error(
|
||||
'Unable to prepare setup for platform %s because '
|
||||
'dependency %s could not be initialized', platform_path,
|
||||
component)
|
||||
_async_persistent_notification(hass, platform_path, True)
|
||||
if not dep_success:
|
||||
log_error('Could not setup all dependencies.')
|
||||
return False
|
||||
|
||||
if not hass.config.skip_pip and hasattr(platform, 'REQUIREMENTS'):
|
||||
req_success = yield from _async_process_requirements(
|
||||
hass, platform_path, platform.REQUIREMENTS)
|
||||
|
||||
if not req_success:
|
||||
log_error('Could not install all requirements.')
|
||||
return None
|
||||
|
||||
res = yield from hass.loop.run_in_executor(
|
||||
None, _handle_requirements, hass, platform, platform_path)
|
||||
if not res:
|
||||
return None
|
||||
|
||||
return platform
|
||||
|
||||
|
||||
|
@ -339,23 +280,14 @@ def from_config_dict(config: Dict[str, Any],
|
|||
hass.config.config_dir = config_dir
|
||||
mount_local_lib_path(config_dir)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _async_init_from_config_dict(future):
|
||||
try:
|
||||
re_hass = yield from async_from_config_dict(
|
||||
config, hass, config_dir, enable_log, verbose, skip_pip,
|
||||
log_rotate_days)
|
||||
future.set_result(re_hass)
|
||||
# pylint: disable=broad-except
|
||||
except Exception as exc:
|
||||
future.set_exception(exc)
|
||||
|
||||
# run task
|
||||
future = asyncio.Future(loop=hass.loop)
|
||||
hass.async_add_job(_async_init_from_config_dict(future))
|
||||
hass.loop.run_until_complete(future)
|
||||
hass = hass.loop.run_until_complete(
|
||||
async_from_config_dict(
|
||||
config, hass, config_dir, enable_log, verbose, skip_pip,
|
||||
log_rotate_days)
|
||||
)
|
||||
|
||||
return future.result()
|
||||
return hass
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
@ -372,19 +304,15 @@ def async_from_config_dict(config: Dict[str, Any],
|
|||
Dynamically loads required components and its dependencies.
|
||||
This method is a coroutine.
|
||||
"""
|
||||
start = time()
|
||||
hass.async_track_tasks()
|
||||
setup_lock = hass.data.get('setup_lock')
|
||||
if setup_lock is None:
|
||||
setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop)
|
||||
|
||||
yield from setup_lock.acquire()
|
||||
|
||||
core_config = config.get(core.DOMAIN, {})
|
||||
|
||||
try:
|
||||
yield from conf_util.async_process_ha_core_config(hass, core_config)
|
||||
except vol.Invalid as ex:
|
||||
async_log_exception(ex, 'homeassistant', core_config, hass)
|
||||
conf_util.async_log_exception(ex, 'homeassistant', core_config, hass)
|
||||
return None
|
||||
|
||||
yield from hass.loop.run_in_executor(
|
||||
|
@ -433,20 +361,25 @@ def async_from_config_dict(config: Dict[str, Any],
|
|||
event_decorators.HASS = hass
|
||||
service.HASS = hass
|
||||
|
||||
# Setup the components
|
||||
dependency_blacklist = loader.DEPENDENCY_BLACKLIST - set(components)
|
||||
# stage 1
|
||||
for component in components:
|
||||
if component not in FIRST_INIT_COMPONENT:
|
||||
continue
|
||||
hass.async_add_job(async_setup_component(hass, component, config))
|
||||
|
||||
for domain in loader.load_order_components(components):
|
||||
if domain in dependency_blacklist:
|
||||
raise HomeAssistantError(
|
||||
'{} is not allowed to be a dependency'.format(domain))
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
yield from _async_setup_component(hass, domain, config)
|
||||
|
||||
setup_lock.release()
|
||||
# stage 2
|
||||
for component in components:
|
||||
if component in FIRST_INIT_COMPONENT:
|
||||
continue
|
||||
hass.async_add_job(async_setup_component(hass, component, config))
|
||||
|
||||
yield from hass.async_stop_track_tasks()
|
||||
|
||||
stop = time()
|
||||
_LOGGER.info('Home Assistant initialized in %ss', round(stop-start, 2))
|
||||
|
||||
async_register_signal_handling(hass)
|
||||
return hass
|
||||
|
||||
|
@ -464,22 +397,13 @@ def from_config_file(config_path: str,
|
|||
if hass is None:
|
||||
hass = core.HomeAssistant()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _async_init_from_config_file(future):
|
||||
try:
|
||||
re_hass = yield from async_from_config_file(
|
||||
config_path, hass, verbose, skip_pip, log_rotate_days)
|
||||
future.set_result(re_hass)
|
||||
# pylint: disable=broad-except
|
||||
except Exception as exc:
|
||||
future.set_exception(exc)
|
||||
|
||||
# run task
|
||||
future = asyncio.Future(loop=hass.loop)
|
||||
hass.loop.create_task(_async_init_from_config_file(future))
|
||||
hass.loop.run_until_complete(future)
|
||||
hass = hass.loop.run_until_complete(
|
||||
async_from_config_file(
|
||||
config_path, hass, verbose, skip_pip, log_rotate_days)
|
||||
)
|
||||
|
||||
return future.result()
|
||||
return hass
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
@ -588,62 +512,6 @@ def async_enable_logging(hass: core.HomeAssistant, verbose: bool=False,
|
|||
'Unable to setup error log %s (access denied)', err_log_path)
|
||||
|
||||
|
||||
def log_exception(ex, domain, config, hass):
|
||||
"""Generate log exception for config validation."""
|
||||
run_callback_threadsafe(
|
||||
hass.loop, async_log_exception, ex, domain, config, hass).result()
|
||||
|
||||
|
||||
@core.callback
|
||||
def _async_persistent_notification(hass: core.HomeAssistant, component: str,
|
||||
link: Optional[bool]=False):
|
||||
"""Print a persistent notification.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
errors = hass.data.get(DATA_PERSISTENT_ERRORS)
|
||||
|
||||
if errors is None:
|
||||
errors = hass.data[DATA_PERSISTENT_ERRORS] = {}
|
||||
|
||||
errors[component] = errors.get(component) or link
|
||||
_lst = [HA_COMPONENT_URL.format(name.replace('_', '-'), name)
|
||||
if link else name for name, link in errors.items()]
|
||||
message = ('The following components and platforms could not be set up:\n'
|
||||
'* ' + '\n* '.join(list(_lst)) + '\nPlease check your config')
|
||||
persistent_notification.async_create(
|
||||
hass, message, 'Invalid config', 'invalid_config')
|
||||
|
||||
|
||||
@core.callback
|
||||
def async_log_exception(ex, domain, config, hass):
|
||||
"""Generate log exception for config validation.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
message = 'Invalid config for [{}]: '.format(domain)
|
||||
if hass is not None:
|
||||
_async_persistent_notification(hass, domain, True)
|
||||
|
||||
if 'extra keys not allowed' in ex.error_message:
|
||||
message += '[{}] is an invalid option for [{}]. Check: {}->{}.'\
|
||||
.format(ex.path[-1], domain, domain,
|
||||
'->'.join(str(m) for m in ex.path))
|
||||
else:
|
||||
message += '{}.'.format(humanize_error(config, ex))
|
||||
|
||||
domain_config = config.get(domain, config)
|
||||
message += " (See {}, line {}). ".format(
|
||||
getattr(domain_config, '__config_file__', '?'),
|
||||
getattr(domain_config, '__line__', '?'))
|
||||
|
||||
if domain != 'homeassistant':
|
||||
message += ('Please check the docs at '
|
||||
'https://home-assistant.io/components/{}/'.format(domain))
|
||||
|
||||
_LOGGER.error(message)
|
||||
|
||||
|
||||
def mount_local_lib_path(config_dir: str) -> str:
|
||||
"""Add local library to Python Path.
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
)
|
||||
devices.append(device)
|
||||
|
||||
yield from async_add_devices(devices)
|
||||
async_add_devices(devices)
|
||||
|
||||
@callback
|
||||
def alarm_keypress_handler(service):
|
||||
|
|
|
@ -46,7 +46,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Setup the MQTT platform."""
|
||||
yield from async_add_devices([MqttAlarm(
|
||||
async_add_devices([MqttAlarm(
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config.get(CONF_COMMAND_TOPIC),
|
||||
|
|
|
@ -28,8 +28,6 @@ import homeassistant.helpers.config_validation as cv
|
|||
DOMAIN = 'automation'
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
DEPENDENCIES = ['group']
|
||||
|
||||
GROUP_NAME_ALL_AUTOMATIONS = 'all automations'
|
||||
|
||||
CONF_ALIAS = 'alias'
|
||||
|
@ -226,7 +224,7 @@ class AutomationEntity(ToggleEntity):
|
|||
"""Entity to show status of entity."""
|
||||
|
||||
def __init__(self, name, async_attach_triggers, cond_func, async_action,
|
||||
hidden):
|
||||
hidden, initial_state):
|
||||
"""Initialize an automation entity."""
|
||||
self._name = name
|
||||
self._async_attach_triggers = async_attach_triggers
|
||||
|
@ -236,6 +234,7 @@ class AutomationEntity(ToggleEntity):
|
|||
self._enabled = False
|
||||
self._last_triggered = None
|
||||
self._hidden = hidden
|
||||
self._initial_state = initial_state
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -264,6 +263,12 @@ class AutomationEntity(ToggleEntity):
|
|||
"""Return True if entity is on."""
|
||||
return self._enabled
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_added_to_hass(self) -> None:
|
||||
"""Startup if initial_state."""
|
||||
if self._initial_state:
|
||||
yield from self.async_enable()
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_turn_on(self, **kwargs) -> None:
|
||||
"""Turn the entity on and update the state."""
|
||||
|
@ -322,7 +327,6 @@ def _async_process_config(hass, config, component):
|
|||
This method is a coroutine.
|
||||
"""
|
||||
entities = []
|
||||
tasks = []
|
||||
|
||||
for config_key in extract_domain_configs(config, DOMAIN):
|
||||
conf = config[config_key]
|
||||
|
@ -332,6 +336,7 @@ def _async_process_config(hass, config, component):
|
|||
list_no)
|
||||
|
||||
hidden = config_block[CONF_HIDE_ENTITY]
|
||||
initial_state = config_block[CONF_INITIAL_STATE]
|
||||
|
||||
action = _async_get_action(hass, config_block.get(CONF_ACTION, {}),
|
||||
name)
|
||||
|
@ -348,15 +353,14 @@ def _async_process_config(hass, config, component):
|
|||
|
||||
async_attach_triggers = partial(
|
||||
_async_process_trigger, hass, config,
|
||||
config_block.get(CONF_TRIGGER, []), name)
|
||||
entity = AutomationEntity(name, async_attach_triggers, cond_func,
|
||||
action, hidden)
|
||||
if config_block[CONF_INITIAL_STATE]:
|
||||
tasks.append(entity.async_enable())
|
||||
config_block.get(CONF_TRIGGER, []), name
|
||||
)
|
||||
entity = AutomationEntity(
|
||||
name, async_attach_triggers, cond_func, action, hidden,
|
||||
initial_state)
|
||||
|
||||
entities.append(entity)
|
||||
|
||||
if tasks:
|
||||
yield from asyncio.wait(tasks, loop=hass.loop)
|
||||
if entities:
|
||||
yield from component.async_add_entities(entities)
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
)
|
||||
devices.append(device)
|
||||
|
||||
yield from async_add_devices(devices)
|
||||
async_add_devices(devices)
|
||||
|
||||
|
||||
class EnvisalinkBinarySensor(EnvisalinkDevice, BinarySensorDevice):
|
||||
|
|
|
@ -57,7 +57,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
|
||||
# generate sensor object
|
||||
entity = FFmpegMotion(hass, manager, config)
|
||||
yield from async_add_devices([entity])
|
||||
async_add_devices([entity])
|
||||
|
||||
|
||||
class FFmpegBinarySensor(FFmpegBase, BinarySensorDevice):
|
||||
|
|
|
@ -54,7 +54,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
|
||||
# generate sensor object
|
||||
entity = FFmpegNoise(hass, manager, config)
|
||||
yield from async_add_devices([entity])
|
||||
async_add_devices([entity])
|
||||
|
||||
|
||||
class FFmpegNoise(FFmpegBinarySensor):
|
||||
|
|
|
@ -46,7 +46,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if value_template is not None:
|
||||
value_template.hass = hass
|
||||
|
||||
yield from async_add_devices([MqttBinarySensor(
|
||||
async_add_devices([MqttBinarySensor(
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS),
|
||||
|
|
|
@ -66,7 +66,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.error('No sensors added')
|
||||
return False
|
||||
|
||||
yield from async_add_devices(sensors, True)
|
||||
async_add_devices(sensors, True)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
limit_type = config.get(CONF_TYPE)
|
||||
device_class = get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
|
||||
|
||||
yield from async_add_devices(
|
||||
async_add_devices(
|
||||
[ThresholdSensor(hass, entity_id, name, threshold, limit_type,
|
||||
device_class)], True)
|
||||
return True
|
||||
|
|
|
@ -34,7 +34,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
"""Setup a FFmpeg Camera."""
|
||||
if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)):
|
||||
return
|
||||
yield from async_add_devices([FFmpegCamera(hass, config)])
|
||||
async_add_devices([FFmpegCamera(hass, config)])
|
||||
|
||||
|
||||
class FFmpegCamera(Camera):
|
||||
|
|
|
@ -44,7 +44,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||
# pylint: disable=unused-argument
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Setup a generic IP Camera."""
|
||||
yield from async_add_devices([GenericCamera(hass, config)])
|
||||
async_add_devices([GenericCamera(hass, config)])
|
||||
|
||||
|
||||
class GenericCamera(Camera):
|
||||
|
|
|
@ -45,7 +45,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||
# pylint: disable=unused-argument
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Setup a MJPEG IP Camera."""
|
||||
yield from async_add_devices([MjpegCamera(hass, config)])
|
||||
async_add_devices([MjpegCamera(hass, config)])
|
||||
|
||||
|
||||
def extract_image_from_mjpeg(stream):
|
||||
|
|
|
@ -153,7 +153,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
)
|
||||
devices.append(device)
|
||||
|
||||
yield from async_add_devices(devices)
|
||||
async_add_devices(devices)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
|
|
@ -75,4 +75,4 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.warning('No active cameras found')
|
||||
return
|
||||
|
||||
yield from async_add_devices(cameras)
|
||||
async_add_devices(cameras)
|
||||
|
|
|
@ -63,7 +63,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
min_cycle_duration = config.get(CONF_MIN_DUR)
|
||||
tolerance = config.get(CONF_TOLERANCE)
|
||||
|
||||
yield from async_add_devices([GenericThermostat(
|
||||
async_add_devices([GenericThermostat(
|
||||
hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp,
|
||||
target_temp, ac_mode, min_cycle_duration, tolerance)])
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if value_template is not None:
|
||||
value_template.hass = hass
|
||||
|
||||
yield from async_add_devices([MqttCover(
|
||||
async_add_devices([MqttCover(
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config.get(CONF_COMMAND_TOPIC),
|
||||
|
|
|
@ -4,6 +4,7 @@ Sets up a demo environment that mimics interaction with devices.
|
|||
For more details about this component, please refer to the documentation
|
||||
https://home-assistant.io/components/demo/
|
||||
"""
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
import homeassistant.bootstrap as bootstrap
|
||||
|
@ -34,7 +35,8 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
|
|||
]
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
@asyncio.coroutine
|
||||
def async_setup(hass, config):
|
||||
"""Setup a demo environment."""
|
||||
group = loader.get_component('group')
|
||||
configurator = loader.get_component('configurator')
|
||||
|
@ -44,7 +46,7 @@ def setup(hass, config):
|
|||
config.setdefault(DOMAIN, {})
|
||||
|
||||
if config[DOMAIN].get('hide_demo_state') != 1:
|
||||
hass.states.set('a.Demo_Mode', 'Enabled')
|
||||
hass.states.async_set('a.Demo_Mode', 'Enabled')
|
||||
|
||||
# Setup sun
|
||||
if not hass.config.latitude:
|
||||
|
@ -53,50 +55,71 @@ def setup(hass, config):
|
|||
if not hass.config.longitude:
|
||||
hass.config.longitude = 117.22743
|
||||
|
||||
bootstrap.setup_component(hass, 'sun')
|
||||
tasks = [
|
||||
bootstrap.async_setup_component(hass, 'sun')
|
||||
]
|
||||
|
||||
# Setup demo platforms
|
||||
demo_config = config.copy()
|
||||
for component in COMPONENTS_WITH_DEMO_PLATFORM:
|
||||
demo_config[component] = {CONF_PLATFORM: 'demo'}
|
||||
bootstrap.setup_component(hass, component, demo_config)
|
||||
tasks.append(
|
||||
bootstrap.async_setup_component(hass, component, demo_config))
|
||||
|
||||
# Set up input select
|
||||
tasks.append(bootstrap.async_setup_component(
|
||||
hass, 'input_select',
|
||||
{'input_select':
|
||||
{'living_room_preset': {'options': ['Visitors',
|
||||
'Visitors with kids',
|
||||
'Home Alone']},
|
||||
'who_cooks': {'icon': 'mdi:panda',
|
||||
'initial': 'Anne Therese',
|
||||
'name': 'Cook today',
|
||||
'options': ['Paulus', 'Anne Therese']}}}))
|
||||
# Set up input boolean
|
||||
tasks.append(bootstrap.async_setup_component(
|
||||
hass, 'input_boolean',
|
||||
{'input_boolean': {'notify': {
|
||||
'icon': 'mdi:car',
|
||||
'initial': False,
|
||||
'name': 'Notify Anne Therese is home'}}}))
|
||||
|
||||
# Set up input boolean
|
||||
tasks.append(bootstrap.async_setup_component(
|
||||
hass, 'input_slider',
|
||||
{'input_slider': {
|
||||
'noise_allowance': {'icon': 'mdi:bell-ring',
|
||||
'min': 0,
|
||||
'max': 10,
|
||||
'name': 'Allowed Noise',
|
||||
'unit_of_measurement': 'dB'}}}))
|
||||
|
||||
# Set up weblink
|
||||
tasks.append(bootstrap.async_setup_component(
|
||||
hass, 'weblink',
|
||||
{'weblink': {'entities': [{'name': 'Router',
|
||||
'url': 'http://192.168.1.1'}]}}))
|
||||
|
||||
results = yield from asyncio.gather(*tasks, loop=hass.loop)
|
||||
|
||||
if any(not result for result in results):
|
||||
return False
|
||||
|
||||
# Setup example persistent notification
|
||||
persistent_notification.create(
|
||||
persistent_notification.async_create(
|
||||
hass, 'This is an example of a persistent notification.',
|
||||
title='Example Notification')
|
||||
|
||||
# Setup room groups
|
||||
lights = sorted(hass.states.entity_ids('light'))
|
||||
switches = sorted(hass.states.entity_ids('switch'))
|
||||
media_players = sorted(hass.states.entity_ids('media_player'))
|
||||
lights = sorted(hass.states.async_entity_ids('light'))
|
||||
switches = sorted(hass.states.async_entity_ids('switch'))
|
||||
media_players = sorted(hass.states.async_entity_ids('media_player'))
|
||||
|
||||
group.Group.create_group(hass, 'living room', [
|
||||
lights[1], switches[0], 'input_select.living_room_preset',
|
||||
'rollershutter.living_room_window', media_players[1],
|
||||
'scene.romantic_lights'])
|
||||
group.Group.create_group(hass, 'bedroom', [
|
||||
lights[0], switches[1], media_players[0],
|
||||
'input_slider.noise_allowance'])
|
||||
group.Group.create_group(hass, 'kitchen', [
|
||||
lights[2], 'rollershutter.kitchen_window', 'lock.kitchen_door'])
|
||||
group.Group.create_group(hass, 'doors', [
|
||||
'lock.front_door', 'lock.kitchen_door',
|
||||
'garage_door.right_garage_door', 'garage_door.left_garage_door'])
|
||||
group.Group.create_group(hass, 'automations', [
|
||||
'input_select.who_cooks', 'input_boolean.notify', ])
|
||||
group.Group.create_group(hass, 'people', [
|
||||
'device_tracker.demo_anne_therese', 'device_tracker.demo_home_boy',
|
||||
'device_tracker.demo_paulus'])
|
||||
group.Group.create_group(hass, 'downstairs', [
|
||||
'group.living_room', 'group.kitchen',
|
||||
'scene.romantic_lights', 'rollershutter.kitchen_window',
|
||||
'rollershutter.living_room_window', 'group.doors',
|
||||
'thermostat.ecobee',
|
||||
], view=True)
|
||||
tasks2 = []
|
||||
|
||||
# Setup scripts
|
||||
bootstrap.setup_component(
|
||||
tasks2.append(bootstrap.async_setup_component(
|
||||
hass, 'script',
|
||||
{'script': {
|
||||
'demo': {
|
||||
|
@ -115,10 +138,10 @@ def setup(hass, config):
|
|||
'service': 'light.turn_off',
|
||||
'data': {ATTR_ENTITY_ID: lights[0]}
|
||||
}]
|
||||
}}})
|
||||
}}}))
|
||||
|
||||
# Setup scenes
|
||||
bootstrap.setup_component(
|
||||
tasks2.append(bootstrap.async_setup_component(
|
||||
hass, 'scene',
|
||||
{'scene': [
|
||||
{'name': 'Romantic lights',
|
||||
|
@ -132,41 +155,37 @@ def setup(hass, config):
|
|||
switches[0]: True,
|
||||
switches[1]: False,
|
||||
}},
|
||||
]})
|
||||
]}))
|
||||
|
||||
# Set up input select
|
||||
bootstrap.setup_component(
|
||||
hass, 'input_select',
|
||||
{'input_select':
|
||||
{'living_room_preset': {'options': ['Visitors',
|
||||
'Visitors with kids',
|
||||
'Home Alone']},
|
||||
'who_cooks': {'icon': 'mdi:panda',
|
||||
'initial': 'Anne Therese',
|
||||
'name': 'Cook today',
|
||||
'options': ['Paulus', 'Anne Therese']}}})
|
||||
# Set up input boolean
|
||||
bootstrap.setup_component(
|
||||
hass, 'input_boolean',
|
||||
{'input_boolean': {'notify': {'icon': 'mdi:car',
|
||||
'initial': False,
|
||||
'name': 'Notify Anne Therese is home'}}})
|
||||
tasks2.append(group.Group.async_create_group(hass, 'living room', [
|
||||
lights[1], switches[0], 'input_select.living_room_preset',
|
||||
'rollershutter.living_room_window', media_players[1],
|
||||
'scene.romantic_lights']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'bedroom', [
|
||||
lights[0], switches[1], media_players[0],
|
||||
'input_slider.noise_allowance']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'kitchen', [
|
||||
lights[2], 'rollershutter.kitchen_window', 'lock.kitchen_door']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'doors', [
|
||||
'lock.front_door', 'lock.kitchen_door',
|
||||
'garage_door.right_garage_door', 'garage_door.left_garage_door']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'automations', [
|
||||
'input_select.who_cooks', 'input_boolean.notify', ]))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'people', [
|
||||
'device_tracker.demo_anne_therese', 'device_tracker.demo_home_boy',
|
||||
'device_tracker.demo_paulus']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'downstairs', [
|
||||
'group.living_room', 'group.kitchen',
|
||||
'scene.romantic_lights', 'rollershutter.kitchen_window',
|
||||
'rollershutter.living_room_window', 'group.doors',
|
||||
'thermostat.ecobee',
|
||||
], view=True))
|
||||
|
||||
# Set up input boolean
|
||||
bootstrap.setup_component(
|
||||
hass, 'input_slider',
|
||||
{'input_slider': {
|
||||
'noise_allowance': {'icon': 'mdi:bell-ring',
|
||||
'min': 0,
|
||||
'max': 10,
|
||||
'name': 'Allowed Noise',
|
||||
'unit_of_measurement': 'dB'}}})
|
||||
results = yield from asyncio.gather(*tasks2, loop=hass.loop)
|
||||
|
||||
if any(not result for result in results):
|
||||
return False
|
||||
|
||||
# Set up weblink
|
||||
bootstrap.setup_component(
|
||||
hass, 'weblink',
|
||||
{'weblink': {'entities': [{'name': 'Router',
|
||||
'url': 'http://192.168.1.1'}]}})
|
||||
# Setup configurator
|
||||
configurator_ids = []
|
||||
|
||||
|
@ -184,14 +203,17 @@ def setup(hass, config):
|
|||
else:
|
||||
configurator.request_done(configurator_ids[0])
|
||||
|
||||
request_id = configurator.request_config(
|
||||
hass, "Philips Hue", hue_configuration_callback,
|
||||
description=("Press the button on the bridge to register Philips Hue "
|
||||
"with Home Assistant."),
|
||||
description_image="/static/images/config_philips_hue.jpg",
|
||||
submit_caption="I have pressed the button"
|
||||
)
|
||||
def setup_configurator():
|
||||
"""Setup configurator."""
|
||||
request_id = configurator.request_config(
|
||||
hass, "Philips Hue", hue_configuration_callback,
|
||||
description=("Press the button on the bridge to register Philips "
|
||||
"Hue with Home Assistant."),
|
||||
description_image="/static/images/config_philips_hue.jpg",
|
||||
submit_caption="I have pressed the button"
|
||||
)
|
||||
configurator_ids.append(request_id)
|
||||
|
||||
configurator_ids.append(request_id)
|
||||
hass.async_add_job(setup_configurator)
|
||||
|
||||
return True
|
||||
|
|
|
@ -14,12 +14,11 @@ import aiohttp
|
|||
import async_timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.bootstrap import (
|
||||
async_prepare_setup_platform, async_log_exception)
|
||||
from homeassistant.bootstrap import async_prepare_setup_platform
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.components import group, zone
|
||||
from homeassistant.components.discovery import SERVICE_NETGEAR
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.config import load_yaml_config_file, async_log_exception
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers import config_per_platform, discovery
|
||||
|
|
|
@ -78,7 +78,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Setup MQTT fan platform."""
|
||||
yield from async_add_devices([MqttFan(
|
||||
async_add_devices([MqttFan(
|
||||
config.get(CONF_NAME),
|
||||
{
|
||||
key: config.get(key) for key in (
|
||||
|
|
|
@ -60,7 +60,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
camera[CONF_ENTITY_ID], api, attributes, camera.get(CONF_NAME)
|
||||
))
|
||||
|
||||
yield from async_add_devices(entities)
|
||||
async_add_devices(entities)
|
||||
|
||||
|
||||
class MicrosoftFaceDetectEntity(ImageProcessingFaceEntity):
|
||||
|
|
|
@ -54,7 +54,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
camera.get(CONF_NAME)
|
||||
))
|
||||
|
||||
yield from async_add_devices(entities)
|
||||
async_add_devices(entities)
|
||||
|
||||
|
||||
class ImageProcessingFaceEntity(ImageProcessingEntity):
|
||||
|
|
|
@ -66,7 +66,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
camera[CONF_ENTITY_ID], params, confidence, camera.get(CONF_NAME)
|
||||
))
|
||||
|
||||
yield from async_add_devices(entities)
|
||||
async_add_devices(entities)
|
||||
|
||||
|
||||
class OpenAlprCloudEntity(ImageProcessingAlprEntity):
|
||||
|
|
|
@ -70,7 +70,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
camera[CONF_ENTITY_ID], command, confidence, camera.get(CONF_NAME)
|
||||
))
|
||||
|
||||
yield from async_add_devices(entities)
|
||||
async_add_devices(entities)
|
||||
|
||||
|
||||
class ImageProcessingAlprEntity(ImageProcessingEntity):
|
||||
|
|
|
@ -70,7 +70,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
config.setdefault(
|
||||
CONF_STATE_VALUE_TEMPLATE, config.get(CONF_VALUE_TEMPLATE))
|
||||
|
||||
yield from async_add_devices([MqttLight(
|
||||
async_add_devices([MqttLight(
|
||||
config.get(CONF_NAME),
|
||||
{
|
||||
key: config.get(key) for key in (
|
||||
|
|
|
@ -61,7 +61,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Setup a MQTT JSON Light."""
|
||||
yield from async_add_devices([MqttJson(
|
||||
async_add_devices([MqttJson(
|
||||
config.get(CONF_NAME),
|
||||
{
|
||||
key: config.get(key) for key in (
|
||||
|
|
|
@ -64,7 +64,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Setup a MQTT Template light."""
|
||||
yield from async_add_devices([MqttTemplate(
|
||||
async_add_devices([MqttTemplate(
|
||||
hass,
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_EFFECT_LIST),
|
||||
|
|
|
@ -117,7 +117,7 @@ def devices_from_config(domain_config, hass=None):
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Set up the Rflink light platform."""
|
||||
yield from async_add_devices(devices_from_config(config, hass))
|
||||
async_add_devices(devices_from_config(config, hass))
|
||||
|
||||
# Add new (unconfigured) devices to user desired group
|
||||
if config[CONF_NEW_DEVICES_GROUP]:
|
||||
|
@ -136,7 +136,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
|
||||
device_config = config[CONF_DEVICE_DEFAULTS]
|
||||
device = entity_class(device_id, hass, **device_config)
|
||||
yield from async_add_devices([device])
|
||||
async_add_devices([device])
|
||||
|
||||
# Register entity to listen to incoming Rflink events
|
||||
hass.data[DATA_ENTITY_LOOKUP][
|
||||
|
@ -156,7 +156,10 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
class RflinkLight(SwitchableRflinkDevice, Light):
|
||||
"""Representation of a Rflink light."""
|
||||
|
||||
pass
|
||||
@property
|
||||
def entity_id(self):
|
||||
"""Return entity id."""
|
||||
return "light.{}".format(self.name)
|
||||
|
||||
|
||||
class DimmableRflinkLight(SwitchableRflinkDevice, Light):
|
||||
|
@ -164,6 +167,11 @@ class DimmableRflinkLight(SwitchableRflinkDevice, Light):
|
|||
|
||||
_brightness = 255
|
||||
|
||||
@property
|
||||
def entity_id(self):
|
||||
"""Return entity id."""
|
||||
return "light.{}".format(self.name)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_turn_on(self, **kwargs):
|
||||
"""Turn the device on."""
|
||||
|
@ -202,6 +210,11 @@ class HybridRflinkLight(SwitchableRflinkDevice, Light):
|
|||
|
||||
_brightness = 255
|
||||
|
||||
@property
|
||||
def entity_id(self):
|
||||
"""Return entity id."""
|
||||
return "light.{}".format(self.name)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_turn_on(self, **kwargs):
|
||||
"""Turn the device on and set dim level."""
|
||||
|
|
|
@ -47,7 +47,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if value_template is not None:
|
||||
value_template.hass = hass
|
||||
|
||||
yield from async_add_devices([MqttLock(
|
||||
async_add_devices([MqttLock(
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config.get(CONF_COMMAND_TOPIC),
|
||||
|
|
|
@ -63,7 +63,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.debug('dump_rawdata: '+avr.protocol.dump_rawdata)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.avr.close)
|
||||
yield from async_add_devices([device])
|
||||
async_add_devices([device])
|
||||
|
||||
|
||||
class AnthemAVR(MediaPlayerDevice):
|
||||
|
|
|
@ -85,7 +85,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
return False
|
||||
|
||||
players = yield from lms.create_players()
|
||||
yield from async_add_devices(players)
|
||||
async_add_devices(players)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
config[CONF_ATTRS]
|
||||
)
|
||||
|
||||
yield from async_add_devices([player])
|
||||
async_add_devices([player])
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
|
|
|
@ -18,7 +18,6 @@ from homeassistant.helpers.entity import Entity
|
|||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
||||
DOMAIN = 'scene'
|
||||
DEPENDENCIES = ['group']
|
||||
STATE = 'scening'
|
||||
|
||||
CONF_ENTITIES = "entities"
|
||||
|
|
|
@ -13,7 +13,6 @@ from homeassistant.const import (
|
|||
from homeassistant.core import State
|
||||
from homeassistant.helpers.state import async_reproduce_state
|
||||
|
||||
DEPENDENCIES = ['group']
|
||||
STATE = 'scening'
|
||||
|
||||
CONF_ENTITIES = "entities"
|
||||
|
@ -29,7 +28,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if not isinstance(scene_config, list):
|
||||
scene_config = [scene_config]
|
||||
|
||||
yield from async_add_devices(HomeAssistantScene(
|
||||
async_add_devices(HomeAssistantScene(
|
||||
hass, _process_config(scene)) for scene in scene_config)
|
||||
return True
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ from homeassistant.helpers.script import Script
|
|||
DOMAIN = "script"
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
GROUP_NAME_ALL_SCRIPTS = 'all scripts'
|
||||
DEPENDENCIES = ["group"]
|
||||
|
||||
CONF_SEQUENCE = "sequence"
|
||||
|
||||
|
@ -130,6 +129,7 @@ def async_setup(hass, config):
|
|||
schema=SCRIPT_TURN_ONOFF_SCHEMA)
|
||||
hass.services.async_register(DOMAIN, SERVICE_TOGGLE, toggle_service,
|
||||
schema=SCRIPT_TURN_ONOFF_SCHEMA)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, remove_logger)
|
||||
|
||||
yield from async_add_devices([entity])
|
||||
async_add_devices([entity])
|
||||
|
||||
|
||||
class APICount(Entity):
|
||||
|
|
|
@ -49,7 +49,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
else:
|
||||
resolver = config.get(CONF_RESOLVER)
|
||||
|
||||
yield from async_add_devices([WanIpSensor(
|
||||
async_add_devices([WanIpSensor(
|
||||
hass, hostname, resolver, ipv6)], True)
|
||||
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
DerivativeDSMREntity('Hourly Gas Consumption', gas_obis),
|
||||
]
|
||||
|
||||
yield from async_add_devices(devices)
|
||||
async_add_devices(devices)
|
||||
|
||||
def update_entities_telegram(telegram):
|
||||
"""Update entities with latests telegram & trigger state update."""
|
||||
|
|
|
@ -34,7 +34,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
hass.data[DATA_EVL])
|
||||
devices.append(device)
|
||||
|
||||
yield from async_add_devices(devices)
|
||||
async_add_devices(devices)
|
||||
|
||||
|
||||
class EnvisalinkSensor(EnvisalinkDevice, Entity):
|
||||
|
|
|
@ -61,7 +61,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
sensor_type = config.get(CONF_TYPE)
|
||||
round_digits = config.get(CONF_ROUND_DIGITS)
|
||||
|
||||
yield from async_add_devices(
|
||||
async_add_devices(
|
||||
[MinMaxSensor(hass, entity_ids, name, sensor_type, round_digits)],
|
||||
True)
|
||||
return True
|
||||
|
|
|
@ -33,7 +33,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
"""Set up the Moon sensor."""
|
||||
name = config.get(CONF_NAME)
|
||||
|
||||
yield from async_add_devices([MoonSensor(name)], True)
|
||||
async_add_devices([MoonSensor(name)], True)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if value_template is not None:
|
||||
value_template.hass = hass
|
||||
|
||||
yield from async_add_devices([MqttSensor(
|
||||
async_add_devices([MqttSensor(
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config.get(CONF_QOS),
|
||||
|
|
|
@ -59,7 +59,7 @@ MQTT_PAYLOAD = vol.Schema(vol.All(json.loads, vol.Schema({
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Setup MQTT Sensor."""
|
||||
yield from async_add_devices([MQTTRoomSensor(
|
||||
async_add_devices([MQTTRoomSensor(
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config.get(CONF_DEVICE_ID),
|
||||
|
|
|
@ -36,7 +36,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
minimum = config.get(CONF_MINIMUM)
|
||||
maximum = config.get(CONF_MAXIMUM)
|
||||
|
||||
yield from async_add_devices([RandomSensor(name, minimum, maximum)], True)
|
||||
async_add_devices([RandomSensor(name, minimum, maximum)], True)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ def devices_from_config(domain_config, hass=None):
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Set up the Rflink platform."""
|
||||
yield from async_add_devices(devices_from_config(config, hass))
|
||||
async_add_devices(devices_from_config(config, hass))
|
||||
|
||||
# Add new (unconfigured) devices to user desired group
|
||||
if config[CONF_NEW_DEVICES_GROUP]:
|
||||
|
@ -91,7 +91,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
rflinksensor = partial(RflinkSensor, device_id, hass)
|
||||
device = rflinksensor(event[EVENT_KEY_SENSOR], event[EVENT_KEY_UNIT])
|
||||
# Add device entity
|
||||
yield from async_add_devices([device])
|
||||
async_add_devices([device])
|
||||
|
||||
# Register entity to listen to incoming rflink events
|
||||
hass.data[DATA_ENTITY_LOOKUP][
|
||||
|
@ -122,6 +122,11 @@ class RflinkSensor(RflinkDevice):
|
|||
"""Domain specific event handler."""
|
||||
self._state = event['value']
|
||||
|
||||
@property
|
||||
def entity_id(self):
|
||||
"""Return entity id."""
|
||||
return "sensor.{}".format(self.name)
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return measurement unit."""
|
||||
|
|
|
@ -50,7 +50,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
name = config.get(CONF_NAME)
|
||||
sampling_size = config.get(CONF_SAMPLING_SIZE)
|
||||
|
||||
yield from async_add_devices(
|
||||
async_add_devices(
|
||||
[StatisticsSensor(hass, entity_id, name, sampling_size)], True)
|
||||
return True
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.error("No sensors added")
|
||||
return False
|
||||
|
||||
yield from async_add_devices(sensors, True)
|
||||
async_add_devices(sensors, True)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
for variable in config[CONF_DISPLAY_OPTIONS]:
|
||||
devices.append(TimeDateSensor(variable))
|
||||
|
||||
yield from async_add_devices(devices, True)
|
||||
async_add_devices(devices, True)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
name = config.get(CONF_NAME)
|
||||
time_zone = dt_util.get_time_zone(config.get(CONF_TIME_ZONE))
|
||||
|
||||
yield from async_add_devices([WorldClockSensor(time_zone, name)], True)
|
||||
async_add_devices([WorldClockSensor(time_zone, name)], True)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
dev = []
|
||||
for sensor_type in config[CONF_MONITORED_CONDITIONS]:
|
||||
dev.append(YrSensor(sensor_type))
|
||||
yield from async_add_devices(dev)
|
||||
async_add_devices(dev)
|
||||
|
||||
weather = YrData(hass, coordinates, dev)
|
||||
# Update weather on the hour, spread seconds
|
||||
|
|
|
@ -74,7 +74,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if response is not None:
|
||||
yield from response.release()
|
||||
|
||||
yield from async_add_devices(
|
||||
async_add_devices(
|
||||
HookSmartHome(
|
||||
hass,
|
||||
token,
|
||||
|
|
|
@ -43,7 +43,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if value_template is not None:
|
||||
value_template.hass = hass
|
||||
|
||||
yield from async_add_devices([MqttSwitch(
|
||||
async_add_devices([MqttSwitch(
|
||||
config.get(CONF_NAME),
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config.get(CONF_COMMAND_TOPIC),
|
||||
|
|
|
@ -72,7 +72,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
if req is not None:
|
||||
yield from req.release()
|
||||
|
||||
yield from async_add_devices(
|
||||
async_add_devices(
|
||||
[RestSwitch(hass, name, resource, body_on, body_off,
|
||||
is_on_template, timeout)])
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ def devices_from_config(domain_config, hass=None):
|
|||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Set up the Rflink platform."""
|
||||
yield from async_add_devices(devices_from_config(config, hass))
|
||||
async_add_devices(devices_from_config(config, hass))
|
||||
|
||||
|
||||
class RflinkSwitch(SwitchableRflinkDevice, SwitchDevice):
|
||||
|
|
|
@ -70,7 +70,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.error("No switches added")
|
||||
return False
|
||||
|
||||
yield from async_add_devices(switches, True)
|
||||
async_add_devices(switches, True)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -276,7 +276,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
device = hass.data[DATA_ZWAVE_DICT].pop(
|
||||
discovery_info[const.DISCOVERY_DEVICE])
|
||||
if device:
|
||||
yield from async_add_devices([device])
|
||||
async_add_devices([device])
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
|
|
@ -10,23 +10,27 @@ import sys
|
|||
from typing import Any, List, Tuple # NOQA
|
||||
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM,
|
||||
CONF_TIME_ZONE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC,
|
||||
CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS,
|
||||
__version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB)
|
||||
from homeassistant.core import DOMAIN as CONF_CORE
|
||||
from homeassistant.core import callback, DOMAIN as CONF_CORE
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.loader import get_component
|
||||
from homeassistant.loader import get_component, get_platform
|
||||
from homeassistant.util.yaml import load_yaml
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util import dt as date_util, location as loc_util
|
||||
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
|
||||
from homeassistant.helpers.entity_values import EntityValues
|
||||
from homeassistant.helpers import config_per_platform, extract_domain_configs
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_PERSISTENT_ERRORS = 'bootstrap_persistent_errors'
|
||||
HA_COMPONENT_URL = '[{}](https://home-assistant.io/components/{}/)'
|
||||
YAML_CONFIG_FILE = 'configuration.yaml'
|
||||
VERSION_FILE = '.HA_VERSION'
|
||||
CONFIG_DIR_NAME = '.homeassistant'
|
||||
|
@ -274,6 +278,35 @@ def process_ha_config_upgrade(hass):
|
|||
outp.write(__version__)
|
||||
|
||||
|
||||
@callback
|
||||
def async_log_exception(ex, domain, config, hass):
|
||||
"""Generate log exception for config validation.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
message = 'Invalid config for [{}]: '.format(domain)
|
||||
if hass is not None:
|
||||
async_notify_setup_error(hass, domain, True)
|
||||
|
||||
if 'extra keys not allowed' in ex.error_message:
|
||||
message += '[{}] is an invalid option for [{}]. Check: {}->{}.'\
|
||||
.format(ex.path[-1], domain, domain,
|
||||
'->'.join(str(m) for m in ex.path))
|
||||
else:
|
||||
message += '{}.'.format(humanize_error(config, ex))
|
||||
|
||||
domain_config = config.get(domain, config)
|
||||
message += " (See {}, line {}). ".format(
|
||||
getattr(domain_config, '__config_file__', '?'),
|
||||
getattr(domain_config, '__line__', '?'))
|
||||
|
||||
if domain != 'homeassistant':
|
||||
message += ('Please check the docs at '
|
||||
'https://home-assistant.io/components/{}/'.format(domain))
|
||||
|
||||
_LOGGER.error(message)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_process_ha_core_config(hass, config):
|
||||
"""Process the [homeassistant] section from the config.
|
||||
|
@ -483,6 +516,67 @@ def merge_packages_config(config, packages):
|
|||
return config
|
||||
|
||||
|
||||
@callback
|
||||
def async_process_component_config(hass, config, domain):
|
||||
"""Check component config and return processed config.
|
||||
|
||||
Raise a vol.Invalid exception on error.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
component = get_component(domain)
|
||||
|
||||
if hasattr(component, 'CONFIG_SCHEMA'):
|
||||
try:
|
||||
config = component.CONFIG_SCHEMA(config)
|
||||
except vol.Invalid as ex:
|
||||
async_log_exception(ex, domain, config, hass)
|
||||
return None
|
||||
|
||||
elif hasattr(component, 'PLATFORM_SCHEMA'):
|
||||
platforms = []
|
||||
for p_name, p_config in config_per_platform(config, domain):
|
||||
# Validate component specific platform schema
|
||||
try:
|
||||
p_validated = component.PLATFORM_SCHEMA(p_config)
|
||||
except vol.Invalid as ex:
|
||||
async_log_exception(ex, domain, config, hass)
|
||||
continue
|
||||
|
||||
# Not all platform components follow same pattern for platforms
|
||||
# So if p_name is None we are not going to validate platform
|
||||
# (the automation component is one of them)
|
||||
if p_name is None:
|
||||
platforms.append(p_validated)
|
||||
continue
|
||||
|
||||
platform = get_platform(domain, p_name)
|
||||
|
||||
if platform is None:
|
||||
continue
|
||||
|
||||
# Validate platform specific schema
|
||||
if hasattr(platform, 'PLATFORM_SCHEMA'):
|
||||
# pylint: disable=no-member
|
||||
try:
|
||||
p_validated = platform.PLATFORM_SCHEMA(p_validated)
|
||||
except vol.Invalid as ex:
|
||||
async_log_exception(ex, '{}.{}'.format(domain, p_name),
|
||||
p_validated, hass)
|
||||
continue
|
||||
|
||||
platforms.append(p_validated)
|
||||
|
||||
# Create a copy of the configuration with all config for current
|
||||
# component removed and add validated config back in.
|
||||
filter_keys = extract_domain_configs(config, domain)
|
||||
config = {key: value for key, value in config.items()
|
||||
if key not in filter_keys}
|
||||
config[domain] = platforms
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_check_ha_config_file(hass):
|
||||
"""Check if HA config file valid.
|
||||
|
@ -501,3 +595,25 @@ def async_check_ha_config_file(hass):
|
|||
return None
|
||||
|
||||
return re.sub(r'\033\[[^m]*m', '', str(stdout_data, 'utf-8'))
|
||||
|
||||
|
||||
@callback
|
||||
def async_notify_setup_error(hass, component, link=False):
|
||||
"""Print a persistent notification.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
from homeassistant.components import persistent_notification
|
||||
|
||||
errors = hass.data.get(DATA_PERSISTENT_ERRORS)
|
||||
|
||||
if errors is None:
|
||||
errors = hass.data[DATA_PERSISTENT_ERRORS] = {}
|
||||
|
||||
errors[component] = errors.get(component) or link
|
||||
_lst = [HA_COMPONENT_URL.format(name.replace('_', '-'), name)
|
||||
if link else name for name, link in errors.items()]
|
||||
message = ('The following components and platforms could not be set up:\n'
|
||||
'* ' + '\n* '.join(list(_lst)) + '\nPlease check your config')
|
||||
persistent_notification.async_create(
|
||||
hass, message, 'Invalid config', 'invalid_config')
|
||||
|
|
|
@ -122,6 +122,7 @@ class HomeAssistant(object):
|
|||
self.loop.set_default_executor(self.executor)
|
||||
self.loop.set_exception_handler(async_loop_exception_handler)
|
||||
self._pending_tasks = []
|
||||
self._track_task = False
|
||||
self.bus = EventBus(self)
|
||||
self.services = ServiceRegistry(self)
|
||||
self.states = StateMachine(self.bus, self.loop)
|
||||
|
@ -178,28 +179,7 @@ class HomeAssistant(object):
|
|||
self.loop.call_soon_threadsafe(self.async_add_job, target, *args)
|
||||
|
||||
@callback
|
||||
def _async_add_job(self, target: Callable[..., None], *args: Any) -> None:
|
||||
"""Add a job from within the eventloop.
|
||||
|
||||
This method must be run in the event loop.
|
||||
|
||||
target: target to call.
|
||||
args: parameters for method to call.
|
||||
"""
|
||||
if asyncio.iscoroutine(target):
|
||||
self.loop.create_task(target)
|
||||
elif is_callback(target):
|
||||
self.loop.call_soon(target, *args)
|
||||
elif asyncio.iscoroutinefunction(target):
|
||||
self.loop.create_task(target(*args))
|
||||
else:
|
||||
self.loop.run_in_executor(None, target, *args)
|
||||
|
||||
async_add_job = _async_add_job
|
||||
|
||||
@callback
|
||||
def _async_add_job_tracking(self, target: Callable[..., None],
|
||||
*args: Any) -> None:
|
||||
def async_add_job(self, target: Callable[..., None], *args: Any) -> None:
|
||||
"""Add a job from within the eventloop.
|
||||
|
||||
This method must be run in the event loop.
|
||||
|
@ -219,19 +199,21 @@ class HomeAssistant(object):
|
|||
task = self.loop.run_in_executor(None, target, *args)
|
||||
|
||||
# if a task is sheduled
|
||||
if task is not None:
|
||||
if self._track_task and task is not None:
|
||||
self._pending_tasks.append(task)
|
||||
|
||||
return task
|
||||
|
||||
@callback
|
||||
def async_track_tasks(self):
|
||||
"""Track tasks so you can wait for all tasks to be done."""
|
||||
self.async_add_job = self._async_add_job_tracking
|
||||
self._track_task = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_stop_track_tasks(self):
|
||||
"""Track tasks so you can wait for all tasks to be done."""
|
||||
yield from self.async_block_till_done()
|
||||
self.async_add_job = self._async_add_job
|
||||
self._track_task = False
|
||||
|
||||
@callback
|
||||
def async_run_job(self, target: Callable[..., None], *args: Any) -> None:
|
||||
|
|
|
@ -63,20 +63,8 @@ def async_discover(hass, service, discovered=None, component=None,
|
|||
'Cannot discover the {} component.'.format(component))
|
||||
|
||||
if component is not None and component not in hass.config.components:
|
||||
did_lock = False
|
||||
setup_lock = hass.data.get('setup_lock')
|
||||
if setup_lock and setup_lock.locked():
|
||||
did_lock = True
|
||||
yield from setup_lock.acquire()
|
||||
|
||||
try:
|
||||
# Could have been loaded while waiting for lock.
|
||||
if component not in hass.config.components:
|
||||
yield from bootstrap.async_setup_component(hass, component,
|
||||
hass_config)
|
||||
finally:
|
||||
if did_lock:
|
||||
setup_lock.release()
|
||||
yield from bootstrap.async_setup_component(
|
||||
hass, component, hass_config)
|
||||
|
||||
data = {
|
||||
ATTR_SERVICE: service
|
||||
|
@ -160,22 +148,11 @@ def async_load_platform(hass, component, platform, discovered=None,
|
|||
raise HomeAssistantError(
|
||||
'Cannot discover the {} component.'.format(component))
|
||||
|
||||
did_lock = False
|
||||
setup_lock = hass.data.get('setup_lock')
|
||||
if setup_lock and setup_lock.locked():
|
||||
did_lock = True
|
||||
yield from setup_lock.acquire()
|
||||
|
||||
setup_success = True
|
||||
|
||||
try:
|
||||
# Could have been loaded while waiting for lock.
|
||||
if component not in hass.config.components:
|
||||
setup_success = yield from bootstrap.async_setup_component(
|
||||
hass, component, hass_config)
|
||||
finally:
|
||||
if did_lock:
|
||||
setup_lock.release()
|
||||
if component not in hass.config.components:
|
||||
setup_success = yield from bootstrap.async_setup_component(
|
||||
hass, component, hass_config)
|
||||
|
||||
# No need to fire event if we could not setup component
|
||||
if not setup_success:
|
||||
|
|
|
@ -3,8 +3,7 @@ import asyncio
|
|||
from datetime import timedelta
|
||||
|
||||
from homeassistant import config as conf_util
|
||||
from homeassistant.bootstrap import (
|
||||
async_prepare_setup_platform, async_prepare_setup_component)
|
||||
from homeassistant.bootstrap import async_prepare_setup_platform
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, CONF_SCAN_INTERVAL, CONF_ENTITY_NAMESPACE,
|
||||
DEVICE_DEFAULT_NAME)
|
||||
|
@ -49,12 +48,9 @@ class EntityComponent(object):
|
|||
def setup(self, config):
|
||||
"""Set up a full entity component.
|
||||
|
||||
Loads the platforms from the config and will listen for supported
|
||||
discovered platforms.
|
||||
This doesn't block the executor to protect from deadlocks.
|
||||
"""
|
||||
run_coroutine_threadsafe(
|
||||
self.async_setup(config), self.hass.loop
|
||||
).result()
|
||||
self.hass.add_job(self.async_setup(config))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_setup(self, config):
|
||||
|
@ -143,14 +139,16 @@ class EntityComponent(object):
|
|||
if getattr(platform, 'async_setup_platform', None):
|
||||
yield from platform.async_setup_platform(
|
||||
self.hass, platform_config,
|
||||
entity_platform.async_add_entities, discovery_info
|
||||
entity_platform.async_schedule_add_entities, discovery_info
|
||||
)
|
||||
else:
|
||||
yield from self.hass.loop.run_in_executor(
|
||||
None, platform.setup_platform, self.hass, platform_config,
|
||||
entity_platform.add_entities, discovery_info
|
||||
entity_platform.schedule_add_entities, discovery_info
|
||||
)
|
||||
|
||||
yield from entity_platform.async_block_entities_done()
|
||||
|
||||
self.hass.config.components.add(
|
||||
'{}.{}'.format(self.domain, platform_type))
|
||||
except Exception: # pylint: disable=broad-except
|
||||
|
@ -275,7 +273,7 @@ class EntityComponent(object):
|
|||
self.logger.error(err)
|
||||
return None
|
||||
|
||||
conf = yield from async_prepare_setup_component(
|
||||
conf = conf_util.async_process_component_config(
|
||||
self.hass, conf, self.domain)
|
||||
|
||||
if conf is None:
|
||||
|
@ -295,9 +293,40 @@ class EntityPlatform(object):
|
|||
self.scan_interval = scan_interval
|
||||
self.entity_namespace = entity_namespace
|
||||
self.platform_entities = []
|
||||
self._tasks = []
|
||||
self._async_unsub_polling = None
|
||||
self._process_updates = asyncio.Lock(loop=component.hass.loop)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_block_entities_done(self):
|
||||
"""Wait until all entities add to hass."""
|
||||
if self._tasks:
|
||||
pending = [task for task in self._tasks if not task.done()]
|
||||
self._tasks.clear()
|
||||
|
||||
if pending:
|
||||
yield from asyncio.wait(pending, loop=self.component.hass.loop)
|
||||
|
||||
def schedule_add_entities(self, new_entities, update_before_add=False):
|
||||
"""Add entities for a single platform."""
|
||||
if update_before_add:
|
||||
for entity in new_entities:
|
||||
entity.update()
|
||||
|
||||
run_callback_threadsafe(
|
||||
self.component.hass.loop,
|
||||
self.async_schedule_add_entities, list(new_entities), False
|
||||
).result()
|
||||
|
||||
@callback
|
||||
def async_schedule_add_entities(self, new_entities,
|
||||
update_before_add=False):
|
||||
"""Add entities for a single platform async."""
|
||||
self._tasks.append(self.component.hass.async_add_job(
|
||||
self.async_add_entities(
|
||||
new_entities, update_before_add=update_before_add)
|
||||
))
|
||||
|
||||
def add_entities(self, new_entities, update_before_add=False):
|
||||
"""Add entities for a single platform."""
|
||||
if update_before_add:
|
||||
|
@ -306,8 +335,7 @@ class EntityPlatform(object):
|
|||
|
||||
run_coroutine_threadsafe(
|
||||
self.async_add_entities(list(new_entities), False),
|
||||
self.component.hass.loop
|
||||
).result()
|
||||
self.component.hass.loop).result()
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_add_entities(self, new_entities, update_before_add=False):
|
||||
|
@ -319,8 +347,16 @@ class EntityPlatform(object):
|
|||
if not new_entities:
|
||||
return
|
||||
|
||||
tasks = [self._async_process_entity(entity, update_before_add)
|
||||
for entity in new_entities]
|
||||
@asyncio.coroutine
|
||||
def async_process_entity(new_entity):
|
||||
"""Add entities to StateMachine."""
|
||||
ret = yield from self.component.async_add_entity(
|
||||
new_entity, self, update_before_add=update_before_add
|
||||
)
|
||||
if ret:
|
||||
self.platform_entities.append(new_entity)
|
||||
|
||||
tasks = [async_process_entity(entity) for entity in new_entities]
|
||||
|
||||
yield from asyncio.wait(tasks, loop=self.component.hass.loop)
|
||||
yield from self.component.async_update_group()
|
||||
|
@ -334,15 +370,6 @@ class EntityPlatform(object):
|
|||
self.component.hass, self._update_entity_states, self.scan_interval
|
||||
)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _async_process_entity(self, new_entity, update_before_add):
|
||||
"""Add entities to StateMachine."""
|
||||
ret = yield from self.component.async_add_entity(
|
||||
new_entity, self, update_before_add=update_before_add
|
||||
)
|
||||
if ret:
|
||||
self.platform_entities.append(new_entity)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_reset(self):
|
||||
"""Remove all entities and reset data.
|
||||
|
|
|
@ -170,41 +170,6 @@ def get_component(comp_name) -> Optional[ModuleType]:
|
|||
return None
|
||||
|
||||
|
||||
def load_order_components(components: Sequence[str]) -> OrderedSet:
|
||||
"""Take in a list of components we want to load.
|
||||
|
||||
- filters out components we cannot load
|
||||
- filters out components that have invalid/circular dependencies
|
||||
- Will make sure the recorder component is loaded first
|
||||
- Will ensure that all components that do not directly depend on
|
||||
the group component will be loaded before the group component.
|
||||
- returns an OrderedSet load order.
|
||||
- Makes sure MQTT eventstream is available for publish before
|
||||
components start updating states.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
_check_prepared()
|
||||
|
||||
load_order = OrderedSet()
|
||||
|
||||
# Sort the list of modules on if they depend on group component or not.
|
||||
# Components that do not depend on the group usually set up states.
|
||||
# Components that depend on group usually use states in their setup.
|
||||
for comp_load_order in sorted((load_order_component(component)
|
||||
for component in components),
|
||||
key=lambda order: 'group' in order):
|
||||
load_order.update(comp_load_order)
|
||||
|
||||
# Push some to first place in load order
|
||||
for comp in ('mqtt_eventstream', 'mqtt', 'recorder',
|
||||
'introduction', 'logger'):
|
||||
if comp in load_order:
|
||||
load_order.promote(comp)
|
||||
|
||||
return load_order
|
||||
|
||||
|
||||
def load_order_component(comp_name: str) -> OrderedSet:
|
||||
"""Return an OrderedSet of components in the correct order of loading.
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ MOCKS = {
|
|||
'load*': ("homeassistant.config.load_yaml", yaml.load_yaml),
|
||||
'get': ("homeassistant.loader.get_component", loader.get_component),
|
||||
'secrets': ("homeassistant.util.yaml._secret_yaml", yaml._secret_yaml),
|
||||
'except': ("homeassistant.bootstrap.async_log_exception",
|
||||
bootstrap.async_log_exception),
|
||||
'except': ("homeassistant.config.async_log_exception",
|
||||
config_util.async_log_exception),
|
||||
'package_error': ("homeassistant.config._log_pkg_error",
|
||||
config_util._log_pkg_error),
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ def check(config_path):
|
|||
|
||||
def mock_except(ex, domain, config, # pylint: disable=unused-variable
|
||||
hass=None):
|
||||
"""Mock bootstrap.log_exception."""
|
||||
"""Mock config.log_exception."""
|
||||
MOCKS['except'][1](ex, domain, config, hass)
|
||||
res['except'][domain] = config.get(domain, config)
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ from contextlib import contextmanager
|
|||
from aiohttp import web
|
||||
|
||||
from homeassistant import core as ha, loader
|
||||
from homeassistant.bootstrap import (
|
||||
setup_component, async_prepare_setup_component)
|
||||
from homeassistant.bootstrap import setup_component, DATA_SETUP
|
||||
from homeassistant.config import async_process_component_config
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
|
||||
|
@ -93,13 +93,16 @@ def async_test_home_assistant(loop):
|
|||
|
||||
hass = ha.HomeAssistant(loop)
|
||||
|
||||
orig_async_add_job = hass.async_add_job
|
||||
|
||||
def async_add_job(target, *args):
|
||||
"""Add a magic mock."""
|
||||
if isinstance(target, MagicMock):
|
||||
return
|
||||
hass._async_add_job_tracking(target, *args)
|
||||
return orig_async_add_job(target, *args)
|
||||
|
||||
hass.async_add_job = async_add_job
|
||||
hass.async_track_tasks()
|
||||
|
||||
hass.config.location_name = 'test home'
|
||||
hass.config.config_dir = get_test_config_dir()
|
||||
|
@ -230,7 +233,7 @@ def mock_state_change_event(hass, new_state, old_state=None):
|
|||
def mock_http_component(hass, api_password=None):
|
||||
"""Mock the HTTP component."""
|
||||
hass.http = MagicMock(api_password=api_password)
|
||||
hass.config.components.add('http')
|
||||
mock_component(hass, 'http')
|
||||
hass.http.views = {}
|
||||
|
||||
def mock_register_view(view):
|
||||
|
@ -268,6 +271,19 @@ def mock_mqtt_component(hass):
|
|||
return mock_mqtt
|
||||
|
||||
|
||||
def mock_component(hass, component):
|
||||
"""Mock a component is setup."""
|
||||
setup_tasks = hass.data.get(DATA_SETUP)
|
||||
if setup_tasks is None:
|
||||
setup_tasks = hass.data[DATA_SETUP] = {}
|
||||
|
||||
if component not in setup_tasks:
|
||||
AssertionError("Component {} is already setup".format(component))
|
||||
|
||||
hass.config.components.add(component)
|
||||
setup_tasks[component] = asyncio.Task(mock_coro(True), loop=hass.loop)
|
||||
|
||||
|
||||
class MockModule(object):
|
||||
"""Representation of a fake module."""
|
||||
|
||||
|
@ -439,10 +455,10 @@ def assert_setup_component(count, domain=None):
|
|||
"""
|
||||
config = {}
|
||||
|
||||
@asyncio.coroutine
|
||||
@ha.callback
|
||||
def mock_psc(hass, config_input, domain):
|
||||
"""Mock the prepare_setup_component to capture config."""
|
||||
res = yield from async_prepare_setup_component(
|
||||
res = async_process_component_config(
|
||||
hass, config_input, domain)
|
||||
config[domain] = None if res is None else res.get(domain)
|
||||
_LOGGER.debug('Configuration for %s, Validated: %s, Original %s',
|
||||
|
@ -450,7 +466,7 @@ def assert_setup_component(count, domain=None):
|
|||
return res
|
||||
|
||||
assert isinstance(config, dict)
|
||||
with patch('homeassistant.bootstrap.async_prepare_setup_component',
|
||||
with patch('homeassistant.config.async_process_component_config',
|
||||
mock_psc):
|
||||
yield config
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_fail_setup_without_state_topic(self):
|
||||
"""Test for failing with no state topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(0) as config:
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
|
@ -42,7 +41,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_fail_setup_without_command_topic(self):
|
||||
"""Test failing with no command topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(0):
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
|
@ -53,7 +51,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_update_state_via_state_topic(self):
|
||||
"""Test updating with via state topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -77,7 +74,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_ignore_update_state_if_unknown_via_state_topic(self):
|
||||
"""Test ignoring updates via state topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -98,7 +94,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_home_publishes_mqtt(self):
|
||||
"""Test publishing of MQTT messages while armed."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -115,7 +110,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_home_not_publishes_mqtt_with_invalid_code(self):
|
||||
"""Test not publishing of MQTT messages with invalid code."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -133,7 +127,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_away_publishes_mqtt(self):
|
||||
"""Test publishing of MQTT messages while armed."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -150,7 +143,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_away_not_publishes_mqtt_with_invalid_code(self):
|
||||
"""Test not publishing of MQTT messages with invalid code."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -168,7 +160,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_disarm_publishes_mqtt(self):
|
||||
"""Test publishing of MQTT messages while disarmed."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -185,7 +176,6 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_disarm_not_publishes_mqtt_with_invalid_code(self):
|
||||
"""Test not publishing of MQTT messages with invalid code."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
|
|
@ -5,7 +5,7 @@ from homeassistant.core import callback
|
|||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.automation as automation
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -15,7 +15,7 @@ class TestAutomationEvent(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
self.calls = []
|
||||
|
||||
@callback
|
||||
|
|
|
@ -11,7 +11,7 @@ from homeassistant.exceptions import HomeAssistantError
|
|||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import get_test_home_assistant, assert_setup_component, \
|
||||
fire_time_changed
|
||||
fire_time_changed, mock_component
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -21,7 +21,7 @@ class TestAutomation(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
self.calls = []
|
||||
|
||||
@callback
|
||||
|
|
|
@ -5,7 +5,8 @@ from homeassistant.core import callback
|
|||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.automation as automation
|
||||
from tests.common import (
|
||||
mock_mqtt_component, fire_mqtt_message, get_test_home_assistant)
|
||||
mock_mqtt_component, fire_mqtt_message, get_test_home_assistant,
|
||||
mock_component)
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -15,7 +16,7 @@ class TestAutomationMQTT(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
mock_mqtt_component(self.hass)
|
||||
self.calls = []
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from homeassistant.core import callback
|
|||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.automation as automation
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -15,7 +15,7 @@ class TestAutomationNumericState(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
self.calls = []
|
||||
|
||||
@callback
|
||||
|
|
|
@ -10,7 +10,8 @@ import homeassistant.util.dt as dt_util
|
|||
import homeassistant.components.automation as automation
|
||||
|
||||
from tests.common import (
|
||||
fire_time_changed, get_test_home_assistant, assert_setup_component)
|
||||
fire_time_changed, get_test_home_assistant, assert_setup_component,
|
||||
mock_component)
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -20,7 +21,7 @@ class TestAutomationState(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
self.hass.states.set('test.entity', 'hello')
|
||||
self.calls = []
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ from homeassistant.components import sun
|
|||
import homeassistant.components.automation as automation
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import fire_time_changed, get_test_home_assistant
|
||||
from tests.common import (
|
||||
fire_time_changed, get_test_home_assistant, mock_component)
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -20,8 +21,8 @@ class TestAutomationSun(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
self.hass.config.components.add('sun')
|
||||
mock_component(self.hass, 'group')
|
||||
mock_component(self.hass, 'sun')
|
||||
|
||||
self.calls = []
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ from homeassistant.core import callback
|
|||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.automation as automation
|
||||
|
||||
from tests.common import get_test_home_assistant, assert_setup_component
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, mock_component)
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -15,7 +16,7 @@ class TestAutomationTemplate(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
self.hass.states.set('test.entity', 'hello')
|
||||
self.calls = []
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ import homeassistant.util.dt as dt_util
|
|||
import homeassistant.components.automation as automation
|
||||
|
||||
from tests.common import (
|
||||
fire_time_changed, get_test_home_assistant, assert_setup_component)
|
||||
fire_time_changed, get_test_home_assistant, assert_setup_component,
|
||||
mock_component)
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -19,7 +20,7 @@ class TestAutomationTime(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
self.calls = []
|
||||
|
||||
@callback
|
||||
|
|
|
@ -5,7 +5,7 @@ from homeassistant.core import callback
|
|||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.components import automation, zone
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -15,7 +15,7 @@ class TestAutomationZone(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('group')
|
||||
mock_component(self.hass, 'group')
|
||||
assert setup_component(self.hass, zone.DOMAIN, {
|
||||
'zone': {
|
||||
'name': 'test',
|
||||
|
|
|
@ -3,10 +3,10 @@ import unittest
|
|||
|
||||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.binary_sensor as binary_sensor
|
||||
from tests.common import mock_mqtt_component, fire_mqtt_message
|
||||
from homeassistant.const import (STATE_OFF, STATE_ON)
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import (
|
||||
get_test_home_assistant, mock_mqtt_component, fire_mqtt_message)
|
||||
|
||||
|
||||
class TestSensorMQTT(unittest.TestCase):
|
||||
|
@ -23,7 +23,6 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_setting_sensor_value_via_mqtt_message(self):
|
||||
"""Test the setting of the value via MQTT."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, binary_sensor.DOMAIN, {
|
||||
binary_sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -49,7 +48,6 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_valid_device_class(self):
|
||||
"""Test the setting of a valid sensor class."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, binary_sensor.DOMAIN, {
|
||||
binary_sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -64,7 +62,6 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_invalid_device_class(self):
|
||||
"""Test the setting of an invalid sensor class."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, binary_sensor.DOMAIN, {
|
||||
binary_sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
|
|
@ -9,7 +9,7 @@ from uvcclient import nvr
|
|||
|
||||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.components.camera import uvc
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_http_component
|
||||
|
||||
|
||||
class TestUVCSetup(unittest.TestCase):
|
||||
|
@ -18,8 +18,7 @@ class TestUVCSetup(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.http = mock.MagicMock()
|
||||
self.hass.config.components = set(['http'])
|
||||
mock_http_component(self.hass)
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
"""The tests for the generic_thermostat."""
|
||||
import asyncio
|
||||
import datetime
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.bootstrap import setup_component, async_setup_component
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
SERVICE_TURN_OFF,
|
||||
|
@ -105,23 +106,6 @@ class TestClimateGenericThermostat(unittest.TestCase):
|
|||
self.assertEqual(35, state.attributes.get('max_temp'))
|
||||
self.assertEqual(None, state.attributes.get('temperature'))
|
||||
|
||||
def test_custom_setup_params(self):
|
||||
"""Test the setup with custom parameters."""
|
||||
self.hass.config.components.remove(climate.DOMAIN)
|
||||
assert setup_component(self.hass, climate.DOMAIN, {'climate': {
|
||||
'platform': 'generic_thermostat',
|
||||
'name': 'test',
|
||||
'heater': ENT_SWITCH,
|
||||
'target_sensor': ENT_SENSOR,
|
||||
'min_temp': MIN_TEMP,
|
||||
'max_temp': MAX_TEMP,
|
||||
'target_temp': TARGET_TEMP,
|
||||
}})
|
||||
state = self.hass.states.get(ENTITY)
|
||||
self.assertEqual(MIN_TEMP, state.attributes.get('min_temp'))
|
||||
self.assertEqual(MAX_TEMP, state.attributes.get('max_temp'))
|
||||
self.assertEqual(TARGET_TEMP, state.attributes.get('temperature'))
|
||||
|
||||
def test_set_target_temp(self):
|
||||
"""Test the setting of the target temperature."""
|
||||
climate.set_temperature(self.hass, 30)
|
||||
|
@ -538,3 +522,23 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase):
|
|||
|
||||
self.hass.services.register('switch', SERVICE_TURN_ON, log_call)
|
||||
self.hass.services.register('switch', SERVICE_TURN_OFF, log_call)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_custom_setup_params(hass):
|
||||
"""Test the setup with custom parameters."""
|
||||
result = yield from async_setup_component(
|
||||
hass, climate.DOMAIN, {'climate': {
|
||||
'platform': 'generic_thermostat',
|
||||
'name': 'test',
|
||||
'heater': ENT_SWITCH,
|
||||
'target_sensor': ENT_SENSOR,
|
||||
'min_temp': MIN_TEMP,
|
||||
'max_temp': MAX_TEMP,
|
||||
'target_temp': TARGET_TEMP,
|
||||
}})
|
||||
assert result
|
||||
state = hass.states.get(ENTITY)
|
||||
assert state.attributes.get('min_temp') == MIN_TEMP
|
||||
assert state.attributes.get('max_temp') == MAX_TEMP
|
||||
assert state.attributes.get('temperature') == TARGET_TEMP
|
||||
|
|
|
@ -15,6 +15,9 @@ def test_validate_config_ok(hass, test_client):
|
|||
with patch.object(config, 'SECTIONS', ['core']):
|
||||
yield from async_setup_component(hass, 'config', {})
|
||||
|
||||
# yield from hass.async_block_till_done()
|
||||
yield from asyncio.sleep(0.1, loop=hass.loop)
|
||||
|
||||
hass.http.views[CheckConfigView.name].register(app.router)
|
||||
client = yield from test_client(app)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ from homeassistant.const import EVENT_COMPONENT_LOADED
|
|||
from homeassistant.bootstrap import async_setup_component, ATTR_COMPONENT
|
||||
from homeassistant.components import config
|
||||
|
||||
from tests.common import mock_http_component, mock_coro
|
||||
from tests.common import mock_http_component, mock_coro, mock_component
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -27,7 +27,7 @@ def test_config_setup(hass, loop):
|
|||
@asyncio.coroutine
|
||||
def test_load_on_demand_already_loaded(hass, test_client):
|
||||
"""Test getting suites."""
|
||||
hass.config.components.add('zwave')
|
||||
mock_component(hass, 'zwave')
|
||||
|
||||
with patch.object(config, 'SECTIONS', []), \
|
||||
patch.object(config, 'ON_DEMAND', ['zwave']), \
|
||||
|
|
|
@ -4,9 +4,9 @@ import unittest
|
|||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.const import STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN
|
||||
import homeassistant.components.cover as cover
|
||||
from tests.common import mock_mqtt_component, fire_mqtt_message
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import (
|
||||
get_test_home_assistant, mock_mqtt_component, fire_mqtt_message)
|
||||
|
||||
|
||||
class TestCoverMQTT(unittest.TestCase):
|
||||
|
@ -23,7 +23,6 @@ class TestCoverMQTT(unittest.TestCase):
|
|||
|
||||
def test_state_via_state_topic(self):
|
||||
"""Test the controlling state via topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
self.assertTrue(setup_component(self.hass, cover.DOMAIN, {
|
||||
cover.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -72,7 +71,6 @@ class TestCoverMQTT(unittest.TestCase):
|
|||
|
||||
def test_state_via_template(self):
|
||||
"""Test the controlling state via topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
self.assertTrue(setup_component(self.hass, cover.DOMAIN, {
|
||||
cover.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -101,7 +99,6 @@ class TestCoverMQTT(unittest.TestCase):
|
|||
|
||||
def test_optimistic_state_change(self):
|
||||
"""Test changing state optimistically."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
self.assertTrue(setup_component(self.hass, cover.DOMAIN, {
|
||||
cover.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -132,7 +129,6 @@ class TestCoverMQTT(unittest.TestCase):
|
|||
|
||||
def test_send_open_cover_command(self):
|
||||
"""Test the sending of open_cover."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
self.assertTrue(setup_component(self.hass, cover.DOMAIN, {
|
||||
cover.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -156,7 +152,6 @@ class TestCoverMQTT(unittest.TestCase):
|
|||
|
||||
def test_send_close_cover_command(self):
|
||||
"""Test the sending of close_cover."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
self.assertTrue(setup_component(self.hass, cover.DOMAIN, {
|
||||
cover.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -180,7 +175,6 @@ class TestCoverMQTT(unittest.TestCase):
|
|||
|
||||
def test_send_stop__cover_command(self):
|
||||
"""Test the sending of stop_cover."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
self.assertTrue(setup_component(self.hass, cover.DOMAIN, {
|
||||
cover.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -204,7 +198,6 @@ class TestCoverMQTT(unittest.TestCase):
|
|||
|
||||
def test_current_cover_position(self):
|
||||
"""Test the current cover position."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
self.assertTrue(setup_component(self.hass, cover.DOMAIN, {
|
||||
cover.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.components import rfxtrx as rfxtrx_core
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
@pytest.mark.skipif("os.environ.get('RFXTRX') != 'RUN'")
|
||||
|
@ -16,7 +16,7 @@ class TestCoverRfxtrx(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components = set(['rfxtrx'])
|
||||
mock_component('rfxtrx')
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
|
|
@ -17,7 +17,8 @@ from homeassistant.const import (CONF_PLATFORM, CONF_PASSWORD, CONF_USERNAME,
|
|||
CONF_HOST)
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, get_test_config_dir, assert_setup_component)
|
||||
get_test_home_assistant, get_test_config_dir, assert_setup_component,
|
||||
mock_component)
|
||||
|
||||
FAKEFILE = None
|
||||
|
||||
|
@ -43,7 +44,7 @@ class TestComponentsDeviceTrackerASUSWRT(unittest.TestCase):
|
|||
def setup_method(self, _):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components = set(['zone'])
|
||||
mock_component(self.hass, 'zone')
|
||||
|
||||
def teardown_method(self, _):
|
||||
"""Stop everything that was started."""
|
||||
|
|
|
@ -16,7 +16,8 @@ from homeassistant.components.device_tracker import DOMAIN
|
|||
from homeassistant.util import slugify
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, load_fixture)
|
||||
get_test_home_assistant, assert_setup_component, load_fixture,
|
||||
mock_component)
|
||||
|
||||
from ...test_util.aiohttp import mock_aiohttp_client
|
||||
|
||||
|
@ -39,7 +40,7 @@ class TestDdwrt(unittest.TestCase):
|
|||
def setup_method(self, _):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components = set(['zone'])
|
||||
mock_component(self.hass, 'zone')
|
||||
|
||||
def teardown_method(self, _):
|
||||
"""Stop everything that was started."""
|
||||
|
|
|
@ -43,7 +43,6 @@ class TestComponentsDeviceTrackerMQTT(unittest.TestCase):
|
|||
|
||||
dev_id = 'paulus'
|
||||
topic = '/location/paulus'
|
||||
self.hass.config.components = set(['mqtt', 'zone'])
|
||||
assert setup_component(self.hass, device_tracker.DOMAIN, {
|
||||
device_tracker.DOMAIN: {
|
||||
CONF_PLATFORM: 'mqtt',
|
||||
|
|
|
@ -13,7 +13,8 @@ import homeassistant.components.device_tracker.upc_connect as platform
|
|||
from homeassistant.util.async import run_coroutine_threadsafe
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, load_fixture)
|
||||
get_test_home_assistant, assert_setup_component, load_fixture,
|
||||
mock_component)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -30,7 +31,7 @@ class TestUPCConnect(object):
|
|||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components = set(['zone'])
|
||||
mock_component(self.hass, 'zone')
|
||||
|
||||
self.host = "127.0.0.1"
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
"""The tests for the Home Assistant HTTP component."""
|
||||
import asyncio
|
||||
import requests
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from homeassistant import bootstrap, const
|
||||
import homeassistant.components.http as http
|
||||
|
@ -157,46 +156,48 @@ def test_registering_view_while_running(hass, test_client):
|
|||
assert text == 'hello'
|
||||
|
||||
|
||||
def test_api_base_url(loop):
|
||||
@asyncio.coroutine
|
||||
def test_api_base_url_with_domain(hass):
|
||||
"""Test setting api url."""
|
||||
hass = MagicMock()
|
||||
hass.loop = loop
|
||||
|
||||
assert loop.run_until_complete(
|
||||
bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
'base_url': 'example.com'
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
result = yield from bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
'base_url': 'example.com'
|
||||
}
|
||||
})
|
||||
assert result
|
||||
assert hass.config.api.base_url == 'http://example.com'
|
||||
|
||||
assert loop.run_until_complete(
|
||||
bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
'server_host': '1.1.1.1'
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_api_base_url_with_ip(hass):
|
||||
"""Test setting api url."""
|
||||
result = yield from bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
'server_host': '1.1.1.1'
|
||||
}
|
||||
})
|
||||
assert result
|
||||
assert hass.config.api.base_url == 'http://1.1.1.1:8123'
|
||||
|
||||
assert loop.run_until_complete(
|
||||
bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
'server_host': '1.1.1.1'
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
assert hass.config.api.base_url == 'http://1.1.1.1:8123'
|
||||
@asyncio.coroutine
|
||||
def test_api_base_url_with_ip_port(hass):
|
||||
"""Test setting api url."""
|
||||
result = yield from bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
'base_url': '1.1.1.1:8124'
|
||||
}
|
||||
})
|
||||
assert result
|
||||
assert hass.config.api.base_url == 'http://1.1.1.1:8124'
|
||||
|
||||
assert loop.run_until_complete(
|
||||
bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_api_no_base_url(hass):
|
||||
"""Test setting api url."""
|
||||
result = yield from bootstrap.async_setup_component(hass, 'http', {
|
||||
'http': {
|
||||
}
|
||||
})
|
||||
assert result
|
||||
assert hass.config.api.base_url == 'http://127.0.0.1:8123'
|
||||
|
|
|
@ -8,7 +8,7 @@ from homeassistant.bootstrap import setup_component, async_setup_component
|
|||
import homeassistant.components.light as light
|
||||
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
ENTITY_LIGHT = 'light.bed_light'
|
||||
|
||||
|
@ -68,7 +68,7 @@ class TestDemoLight(unittest.TestCase):
|
|||
@asyncio.coroutine
|
||||
def test_restore_state(hass):
|
||||
"""Test state gets restored."""
|
||||
hass.config.components.add('recorder')
|
||||
mock_component(hass, 'recorder')
|
||||
hass.state = CoreState.starting
|
||||
hass.data[DATA_RESTORE_CACHE] = {
|
||||
'light.bed_light': State('light.bed_light', 'on', {
|
||||
|
|
|
@ -100,7 +100,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_fail_setup_if_no_command_topic(self):
|
||||
"""Test if command fails with command topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(0):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -113,7 +112,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
def test_no_color_or_brightness_or_color_temp_if_no_topics(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test if there is no color and brightness if no topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -158,7 +156,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'payload_off': 0
|
||||
}}
|
||||
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, config)
|
||||
|
||||
|
@ -214,7 +211,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_scale(self):
|
||||
"""Test the controlling scale."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -277,7 +273,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'rgb_value_template': '{{ value_json.hello | join(",") }}',
|
||||
}}
|
||||
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, config)
|
||||
|
||||
|
@ -317,7 +312,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'payload_off': 'off'
|
||||
}}
|
||||
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, config)
|
||||
|
||||
|
@ -367,7 +361,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'state_topic': 'test_light_rgb/status',
|
||||
}}
|
||||
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, config)
|
||||
|
||||
|
@ -392,7 +385,6 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'state_topic': 'test_light_rgb/status'
|
||||
}}
|
||||
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, config)
|
||||
|
||||
|
|
|
@ -53,7 +53,6 @@ class TestLightMQTTJSON(unittest.TestCase):
|
|||
def test_fail_setup_if_no_command_topic(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test if setup fails with no command topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(0):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -66,7 +65,6 @@ class TestLightMQTTJSON(unittest.TestCase):
|
|||
def test_no_color_or_brightness_if_no_config(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test if there is no color and brightness if they aren't defined."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt_json',
|
||||
|
@ -92,7 +90,6 @@ class TestLightMQTTJSON(unittest.TestCase):
|
|||
def test_controlling_state_via_topic(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test the controlling of the state via topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt_json',
|
||||
|
@ -152,7 +149,6 @@ class TestLightMQTTJSON(unittest.TestCase):
|
|||
def test_sending_mqtt_commands_and_optimistic(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test the sending of command in optimistic mode."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt_json',
|
||||
|
@ -208,7 +204,6 @@ class TestLightMQTTJSON(unittest.TestCase):
|
|||
def test_flash_short_and_long(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test for flash length being sent when included."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt_json',
|
||||
|
@ -250,7 +245,6 @@ class TestLightMQTTJSON(unittest.TestCase):
|
|||
|
||||
def test_transition(self):
|
||||
"""Test for transition time being sent when included."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt_json',
|
||||
|
@ -292,7 +286,6 @@ class TestLightMQTTJSON(unittest.TestCase):
|
|||
def test_invalid_color_and_brightness_values(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test that invalid color/brightness values are ignored."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt_json',
|
||||
|
|
|
@ -45,7 +45,6 @@ class TestLightMQTTTemplate(unittest.TestCase):
|
|||
def test_setup_fails(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test that setup fails with missing required configuration items."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(0):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -58,7 +57,6 @@ class TestLightMQTTTemplate(unittest.TestCase):
|
|||
def test_state_change_via_topic(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test state change via topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -93,7 +91,6 @@ class TestLightMQTTTemplate(unittest.TestCase):
|
|||
def test_state_brightness_color_effect_change_via_topic(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test state, brightness, color and effect change via topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -170,7 +167,6 @@ class TestLightMQTTTemplate(unittest.TestCase):
|
|||
def test_optimistic(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test optimistic mode."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -232,7 +228,6 @@ class TestLightMQTTTemplate(unittest.TestCase):
|
|||
def test_flash(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test flash."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -276,7 +271,6 @@ class TestLightMQTTTemplate(unittest.TestCase):
|
|||
|
||||
def test_transition(self):
|
||||
"""Test for transition time being sent when included."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
@ -320,7 +314,6 @@ class TestLightMQTTTemplate(unittest.TestCase):
|
|||
def test_invalid_values(self): \
|
||||
# pylint: disable=invalid-name
|
||||
"""Test that invalid values are ignored."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
with assert_setup_component(1):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
|
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.components import rfxtrx as rfxtrx_core
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
@pytest.mark.skipif("os.environ.get('RFXTRX') != 'RUN'")
|
||||
|
@ -16,7 +16,7 @@ class TestLightRfxtrx(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components = set(['rfxtrx'])
|
||||
mock_component(self.hass, 'rfxtrx')
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
|
|
@ -23,7 +23,6 @@ class TestLockMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic(self):
|
||||
"""Test the controlling state via topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, lock.DOMAIN, {
|
||||
lock.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -53,7 +52,6 @@ class TestLockMQTT(unittest.TestCase):
|
|||
|
||||
def test_sending_mqtt_commands_and_optimistic(self):
|
||||
"""Test the sending MQTT commands in optimistic mode."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, lock.DOMAIN, {
|
||||
lock.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -87,7 +85,6 @@ class TestLockMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic_and_json_message(self):
|
||||
"""Test the controlling state via topic and JSON message."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, lock.DOMAIN, {
|
||||
lock.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"""The tests for the Universal Media player platform."""
|
||||
import asyncio
|
||||
from copy import copy
|
||||
import unittest
|
||||
|
||||
|
@ -258,7 +257,6 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
bad_config = {'platform': 'universal'}
|
||||
entities = []
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_devices(new_entities):
|
||||
"""Add devices to list."""
|
||||
for dev in new_entities:
|
||||
|
|
|
@ -4,7 +4,8 @@ from unittest.mock import Mock, MagicMock, patch
|
|||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
|
||||
from tests.common import get_test_home_assistant, mock_coro
|
||||
from tests.common import (
|
||||
get_test_home_assistant, mock_coro, mock_http_component)
|
||||
|
||||
|
||||
class TestMQTT:
|
||||
|
@ -13,7 +14,7 @@ class TestMQTT:
|
|||
def setup_method(self, method):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components.add('http')
|
||||
mock_http_component(self.hass, 'super_secret')
|
||||
|
||||
def teardown_method(self, method):
|
||||
"""Stop everything that was started."""
|
||||
|
@ -33,13 +34,21 @@ class TestMQTT:
|
|||
self.hass.config.api = MagicMock(api_password=password)
|
||||
assert setup_component(self.hass, mqtt.DOMAIN, {})
|
||||
assert mock_mqtt.called
|
||||
from pprint import pprint
|
||||
pprint(mock_mqtt.mock_calls)
|
||||
assert mock_mqtt.mock_calls[1][1][5] == 'homeassistant'
|
||||
assert mock_mqtt.mock_calls[1][1][6] == password
|
||||
|
||||
mock_mqtt.reset_mock()
|
||||
@patch('passlib.apps.custom_app_context', Mock(return_value=''))
|
||||
@patch('tempfile.NamedTemporaryFile', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker.start', Mock(return_value=mock_coro()))
|
||||
@patch('homeassistant.components.mqtt.MQTT')
|
||||
def test_creating_config_with_http_no_pass(self, mock_mqtt):
|
||||
"""Test if the MQTT server gets started and subscribe/publish msg."""
|
||||
mock_mqtt().async_connect.return_value = mock_coro(True)
|
||||
self.hass.bus.listen_once = MagicMock()
|
||||
|
||||
self.hass.config.components = set(['http'])
|
||||
self.hass.config.api = MagicMock(api_password=None)
|
||||
assert setup_component(self.hass, mqtt.DOMAIN, {})
|
||||
assert mock_mqtt.called
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"""The tests for the notify demo platform."""
|
||||
import asyncio
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
|
@ -17,12 +16,6 @@ CONFIG = {
|
|||
}
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def mock_setup_platform():
|
||||
"""Mock prepare_setup_platform."""
|
||||
return None
|
||||
|
||||
|
||||
class TestNotifyDemo(unittest.TestCase):
|
||||
"""Test the demo notify."""
|
||||
|
||||
|
@ -52,15 +45,6 @@ class TestNotifyDemo(unittest.TestCase):
|
|||
"""Test setup."""
|
||||
self._setup_notify()
|
||||
|
||||
@patch('homeassistant.bootstrap.async_prepare_setup_platform',
|
||||
return_value=mock_setup_platform())
|
||||
def test_no_prepare_setup_platform(self, mock_prep_setup_platform):
|
||||
"""Test missing notify platform."""
|
||||
with assert_setup_component(0):
|
||||
setup_component(self.hass, notify.DOMAIN, CONFIG)
|
||||
|
||||
assert mock_prep_setup_platform.called
|
||||
|
||||
@patch('homeassistant.components.notify.demo.get_service', autospec=True)
|
||||
def test_no_notify_service(self, mock_demo_get_service):
|
||||
"""Test missing platform notify service instance."""
|
||||
|
|
|
@ -5,7 +5,7 @@ from homeassistant.bootstrap import setup_component
|
|||
import homeassistant.components.sensor as sensor
|
||||
from tests.common import mock_mqtt_component, fire_mqtt_message
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
class TestSensorMQTT(unittest.TestCase):
|
||||
|
@ -22,7 +22,7 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_setting_sensor_value_via_mqtt_message(self):
|
||||
"""Test the setting of the value via MQTT."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
mock_component(self.hass, 'mqtt')
|
||||
assert setup_component(self.hass, sensor.DOMAIN, {
|
||||
sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -42,7 +42,7 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_setting_sensor_value_via_mqtt_json_message(self):
|
||||
"""Test the setting of the value via MQTT with JSON playload."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
mock_component(self.hass, 'mqtt')
|
||||
assert setup_component(self.hass, sensor.DOMAIN, {
|
||||
sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
|
|
@ -5,7 +5,8 @@ from homeassistant.bootstrap import setup_component
|
|||
import homeassistant.components.sensor as sensor
|
||||
from homeassistant.components import pilight
|
||||
|
||||
from tests.common import get_test_home_assistant, assert_setup_component
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, mock_component)
|
||||
|
||||
HASS = None
|
||||
|
||||
|
@ -23,7 +24,7 @@ def setup_function():
|
|||
global HASS
|
||||
|
||||
HASS = get_test_home_assistant()
|
||||
HASS.config.components = set(['pilight'])
|
||||
mock_component(HASS, 'pilight')
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
|
|
@ -7,7 +7,7 @@ from homeassistant.bootstrap import setup_component
|
|||
from homeassistant.components import rfxtrx as rfxtrx_core
|
||||
from homeassistant.const import TEMP_CELSIUS
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
@pytest.mark.skipif("os.environ.get('RFXTRX') != 'RUN'")
|
||||
|
@ -17,7 +17,7 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components = set(['rfxtrx'])
|
||||
mock_component(self.hass, 'rfxtrx')
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
|
|
@ -22,7 +22,6 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic(self):
|
||||
"""Test the controlling state via topic."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, switch.DOMAIN, {
|
||||
switch.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -52,7 +51,6 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_sending_mqtt_commands_and_optimistic(self):
|
||||
"""Test the sending MQTT commands in optimistic mode."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, switch.DOMAIN, {
|
||||
switch.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
@ -86,7 +84,6 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic_and_json_message(self):
|
||||
"""Test the controlling state via topic and JSON message."""
|
||||
self.hass.config.components = set(['mqtt'])
|
||||
assert setup_component(self.hass, switch.DOMAIN, {
|
||||
switch.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
|
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.components import rfxtrx as rfxtrx_core
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
|
||||
@pytest.mark.skipif("os.environ.get('RFXTRX') != 'RUN'")
|
||||
|
@ -16,7 +16,7 @@ class TestSwitchRfxtrx(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.components = set(['rfxtrx'])
|
||||
mock_component(self.hass, 'rfxtrx')
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
|
|
@ -4,7 +4,7 @@ import asyncio
|
|||
import unittest
|
||||
import logging
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.common import get_test_home_assistant, mock_component
|
||||
|
||||
from homeassistant.core import CoreState, State
|
||||
from homeassistant.bootstrap import setup_component, async_setup_component
|
||||
|
@ -118,7 +118,7 @@ def test_restore_state(hass):
|
|||
}
|
||||
|
||||
hass.state = CoreState.starting
|
||||
hass.config.components.add('recorder')
|
||||
mock_component(hass, 'recorder')
|
||||
|
||||
yield from async_setup_component(hass, DOMAIN, {
|
||||
DOMAIN: {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue