Пример #1
0
def test_component_dependencies(hass):
    """Test if we can get the proper load order of components."""
    loader.set_component(hass, 'mod1', MockModule('mod1'))
    loader.set_component(hass, 'mod2', MockModule('mod2', ['mod1']))
    loader.set_component(hass, 'mod3', MockModule('mod3', ['mod2']))

    assert {'mod1', 'mod2', 'mod3'} == \
        loader.component_dependencies(hass, 'mod3')

    # Create circular dependency
    loader.set_component(hass, 'mod1', MockModule('mod1', ['mod3']))

    with pytest.raises(loader.CircularDependency):
        print(loader.component_dependencies(hass, 'mod3'))

    # Depend on non-existing component
    loader.set_component(hass, 'mod1',
                         MockModule('mod1', ['nonexisting']))

    with pytest.raises(loader.ComponentNotFound):
        print(loader.component_dependencies(hass, 'mod1'))

    # Try to get dependencies for non-existing component
    with pytest.raises(loader.ComponentNotFound):
        print(loader.component_dependencies(hass, 'nonexisting'))
Пример #2
0
async def async_from_config_dict(config: Dict[str, Any],
                                 hass: core.HomeAssistant,
                                 config_dir: Optional[str] = None,
                                 enable_log: bool = True,
                                 verbose: bool = False,
                                 skip_pip: bool = False,
                                 log_rotate_days: Any = None,
                                 log_file: Any = None,
                                 log_no_color: bool = False) \
                           -> Optional[core.HomeAssistant]:
    """Try to configure Home Assistant from a configuration dictionary.

    Dynamically loads required components and its dependencies.
    This method is a coroutine.
    """
    start = time()

    if enable_log:
        async_enable_logging(hass, verbose, log_rotate_days, log_file,
                             log_no_color)

    core_config = config.get(core.DOMAIN, {})
    has_api_password = bool((config.get('http') or {}).get('api_password'))
    has_trusted_networks = bool((config.get('http') or {})
                                .get('trusted_networks'))

    try:
        await conf_util.async_process_ha_core_config(
            hass, core_config, has_api_password, has_trusted_networks)
    except vol.Invalid as config_err:
        conf_util.async_log_exception(
            config_err, 'homeassistant', core_config, hass)
        return None
    except HomeAssistantError:
        _LOGGER.error("Home Assistant core failed to initialize. "
                      "Further initialization aborted")
        return None

    await hass.async_add_executor_job(
        conf_util.process_ha_config_upgrade, hass)

    hass.config.skip_pip = skip_pip
    if skip_pip:
        _LOGGER.warning("Skipping pip installation of required modules. "
                        "This may cause issues")

    # Make a copy because we are mutating it.
    config = OrderedDict(config)

    # Merge packages
    conf_util.merge_packages_config(
        hass, config, core_config.get(conf_util.CONF_PACKAGES, {}))

    hass.config_entries = config_entries.ConfigEntries(hass, config)
    await hass.config_entries.async_load()

    # Filter out the repeating and common config section [homeassistant]
    components = set(key.split(' ')[0] for key in config.keys()
                     if key != core.DOMAIN)
    components.update(hass.config_entries.async_domains())

    # Resolve all dependencies of all components.
    for component in list(components):
        try:
            components.update(loader.component_dependencies(hass, component))
        except loader.LoaderError:
            # Ignore it, or we'll break startup
            # It will be properly handled during setup.
            pass

    # setup components
    res = await core_components.async_setup(hass, config)
    if not res:
        _LOGGER.error("Home Assistant core failed to initialize. "
                      "Further initialization aborted")
        return hass

    await persistent_notification.async_setup(hass, config)

    _LOGGER.info("Home Assistant core initialized")

    # stage 1
    for component in components:
        if component not in FIRST_INIT_COMPONENT:
            continue
        hass.async_create_task(async_setup_component(hass, component, config))

    await hass.async_block_till_done()

    # stage 2
    for component in components:
        if component in FIRST_INIT_COMPONENT:
            continue
        hass.async_create_task(async_setup_component(hass, component, config))

    await hass.async_block_till_done()

    stop = time()
    _LOGGER.info("Home Assistant initialized in %.2fs", stop-start)

    # TEMP: warn users for invalid slugs
    # Remove after 0.94 or 1.0
    if cv.INVALID_SLUGS_FOUND or cv.INVALID_ENTITY_IDS_FOUND:
        msg = []

        if cv.INVALID_ENTITY_IDS_FOUND:
            msg.append(
                "Your configuration contains invalid entity ID references. "
                "Please find and update the following. "
                "This will become a breaking change."
            )
            msg.append('\n'.join('- {} -> {}'.format(*item)
                                 for item
                                 in cv.INVALID_ENTITY_IDS_FOUND.items()))

        if cv.INVALID_SLUGS_FOUND:
            msg.append(
                "Your configuration contains invalid slugs. "
                "Please find and update the following. "
                "This will become a breaking change."
            )
            msg.append('\n'.join('- {} -> {}'.format(*item)
                                 for item in cv.INVALID_SLUGS_FOUND.items()))

        hass.components.persistent_notification.async_create(
            '\n\n'.join(msg), "Config Warning", "config_warning"
        )

    # TEMP: warn users of invalid extra keys
    # Remove after 0.92
    if cv.INVALID_EXTRA_KEYS_FOUND:
        msg = []
        msg.append(
            "Your configuration contains extra keys "
            "that the platform does not support (but were silently "
            "accepted before 0.88). Please find and remove the following."
            "This will become a breaking change."
        )
        msg.append('\n'.join('- {}'.format(it)
                             for it in cv.INVALID_EXTRA_KEYS_FOUND))

        hass.components.persistent_notification.async_create(
            '\n\n'.join(msg), "Config Warning", "config_warning"
        )

    return hass
Пример #3
0
async def _async_setup_component(hass: core.HomeAssistant,
                                 domain: str, config: Dict) -> bool:
    """Set up a component for Home Assistant.

    This method is a coroutine.
    """
    def log_error(msg: str, link: bool = True) -> None:
        """Log helper."""
        _LOGGER.error("Setup failed for %s: %s", domain, msg)
        async_notify_setup_error(hass, domain, link)

    component = loader.get_component(hass, domain)

    if not component:
        log_error("Component not found.", False)
        return False

    # Validate all dependencies exist and there are no circular dependencies
    try:
        loader.component_dependencies(hass, domain)
    except loader.ComponentNotFound as err:
        _LOGGER.error(
            "Not setting up %s because we are unable to resolve "
            "(sub)dependency %s", domain, err.domain)
        return False
    except loader.CircularDependency as err:
        _LOGGER.error(
            "Not setting up %s because it contains a circular dependency: "
            "%s -> %s", domain, err.from_domain, err.to_domain)
        return False

    processed_config = \
        conf_util.async_process_component_config(hass, config, domain)

    if processed_config is None:
        log_error("Invalid config.")
        return False

    try:
        await async_process_deps_reqs(hass, config, domain, component)
    except HomeAssistantError as err:
        log_error(str(err))
        return False

    start = timer()
    _LOGGER.info("Setting up %s", domain)

    if hasattr(component, 'PLATFORM_SCHEMA'):
        # Entity components have their own warning
        warn_task = None
    else:
        warn_task = hass.loop.call_later(
            SLOW_SETUP_WARNING, _LOGGER.warning,
            "Setup of %s is taking over %s seconds.",
            domain, SLOW_SETUP_WARNING)

    try:
        if hasattr(component, 'async_setup'):
            result = await component.async_setup(  # type: ignore
                hass, processed_config)
        else:
            result = await hass.async_add_executor_job(
                component.setup, hass, processed_config)  # type: ignore
    except Exception:  # pylint: disable=broad-except
        _LOGGER.exception("Error during setup of component %s", domain)
        async_notify_setup_error(hass, domain, True)
        return False
    finally:
        end = timer()
        if warn_task:
            warn_task.cancel()
    _LOGGER.info("Setup of domain %s took %.1f seconds.", domain, end - start)

    if result is False:
        log_error("Component failed to initialize.")
        return False
    if result is not True:
        log_error("Component {!r} did not return boolean if setup was "
                  "successful. Disabling component.".format(domain))
        loader.set_component(hass, domain, None)
        return False

    if hass.config_entries:
        for entry in hass.config_entries.async_entries(domain):
            await entry.async_setup(hass, component=component)

    hass.config.components.add(component.DOMAIN)  # type: ignore

    # Cleanup
    if domain in hass.data[DATA_SETUP]:
        hass.data[DATA_SETUP].pop(domain)

    hass.bus.async_fire(
        EVENT_COMPONENT_LOADED,
        {ATTR_COMPONENT: component.DOMAIN}  # type: ignore
    )

    return True
Пример #4
0
async def _async_setup_component(hass: core.HomeAssistant, domain: str,
                                 config: Dict) -> bool:
    """Set up a component for Home Assistant.

    This method is a coroutine.
    """
    def log_error(msg: str, link: bool = True) -> None:
        """Log helper."""
        _LOGGER.error("Setup failed for %s: %s", domain, msg)
        async_notify_setup_error(hass, domain, link)

    component = loader.get_component(hass, domain)

    if not component:
        log_error("Component not found.", False)
        return False

    # Validate all dependencies exist and there are no circular dependencies
    try:
        loader.component_dependencies(hass, domain)
    except loader.ComponentNotFound as err:
        _LOGGER.error(
            "Not setting up %s because we are unable to resolve "
            "(sub)dependency %s", domain, err.domain)
        return False
    except loader.CircularDependency as err:
        _LOGGER.error(
            "Not setting up %s because it contains a circular dependency: "
            "%s -> %s", domain, err.from_domain, err.to_domain)
        return False

    processed_config = \
        conf_util.async_process_component_config(hass, config, domain)

    if processed_config is None:
        log_error("Invalid config.")
        return False

    try:
        await async_process_deps_reqs(hass, config, domain, component)
    except HomeAssistantError as err:
        log_error(str(err))
        return False

    start = timer()
    _LOGGER.info("Setting up %s", domain)

    if hasattr(component, 'PLATFORM_SCHEMA'):
        # Entity components have their own warning
        warn_task = None
    else:
        warn_task = hass.loop.call_later(
            SLOW_SETUP_WARNING, _LOGGER.warning,
            "Setup of %s is taking over %s seconds.", domain,
            SLOW_SETUP_WARNING)

    try:
        if hasattr(component, 'async_setup'):
            result = await component.async_setup(  # type: ignore
                hass, processed_config)
        else:
            result = await hass.async_add_executor_job(component.setup, hass,
                                                       processed_config
                                                       )  # type: ignore
    except Exception:  # pylint: disable=broad-except
        _LOGGER.exception("Error during setup of component %s", domain)
        async_notify_setup_error(hass, domain, True)
        return False
    finally:
        end = timer()
        if warn_task:
            warn_task.cancel()
    _LOGGER.info("Setup of domain %s took %.1f seconds.", domain, end - start)

    if result is False:
        log_error("Component failed to initialize.")
        return False
    if result is not True:
        log_error("Component {!r} did not return boolean if setup was "
                  "successful. Disabling component.".format(domain))
        loader.set_component(hass, domain, None)
        return False

    if hass.config_entries:
        for entry in hass.config_entries.async_entries(domain):
            await entry.async_setup(hass, component=component)

    hass.config.components.add(component.DOMAIN)  # type: ignore

    # Cleanup
    if domain in hass.data[DATA_SETUP]:
        hass.data[DATA_SETUP].pop(domain)

    hass.bus.async_fire(
        EVENT_COMPONENT_LOADED,
        {ATTR_COMPONENT: component.DOMAIN}  # type: ignore
    )

    return True