Example #1
0
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'),
            constraints=os.path.join(os.path.dirname(__file__),
                                     CONSTRAINT_FILE))

    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
Example #2
0
 def log_error(msg, link=True):
     """Log helper."""
     _LOGGER.error("Setup failed for %s: %s", domain, msg)
     async_notify_setup_error(hass, domain, link)
Example #3
0
def _async_setup_component(hass: core.HomeAssistant,
                           domain: str, config) -> bool:
    """Set up a component for Home Assistant.

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

    component = loader.get_component(domain)

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

    # Validate no circular dependencies
    components = loader.load_order_component(domain)

    # OrderedSet is empty if component or dependencies could not be resolved
    if not components:
        log_error("Unable to resolve component or dependencies.")
        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:
        yield from 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 = yield from component.async_setup(hass, processed_config)
        else:
            result = yield from hass.async_add_job(
                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
    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
    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

    for entry in hass.config_entries.async_entries(domain):
        yield from entry.async_setup(hass, component=component)

    hass.config.components.add(component.DOMAIN)

    # 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}
    )

    return True
Example #4
0
 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)
Example #5
0
 def log_error(msg, link=True):
     """Log helper."""
     _LOGGER.error("Setup failed for %s: %s", domain, msg)
     async_notify_setup_error(hass, domain, link)
Example #6
0
def _async_setup_component(hass: core.HomeAssistant, domain: str,
                           config) -> bool:
    """Set up a component for Home Assistant.

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

    component = loader.get_component(domain)

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

    # Validate no circular dependencies
    components = loader.load_order_component(domain)

    # OrderedSet is empty if component or dependencies could not be resolved
    if not components:
        log_error("Unable to resolve component or dependencies.")
        return False

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

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

    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

    if hasattr(component, 'DEPENDENCIES'):
        dep_success = yield from _async_process_dependencies(
            hass, config, domain, component.DEPENDENCIES)

        if not dep_success:
            log_error("Could not setup all dependencies.")
            return False

    async_comp = hasattr(component, 'async_setup')

    start = timer()
    _LOGGER.info("Setting up %s", domain)
    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 async_comp:
            result = yield from component.async_setup(hass, processed_config)
        else:
            result = yield from hass.async_add_job(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
    finally:
        end = timer()
        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
    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

    hass.config.components.add(component.DOMAIN)

    # 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})

    return True
Example #7
0
 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)
Example #8
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 no circular dependencies
    components = loader.load_order_component(hass, domain)

    # OrderedSet is empty if component or dependencies could not be resolved
    if not components:
        log_error("Unable to resolve component or dependencies.")
        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
Example #9
0
 def log_error(msg: str) -> None:
     """Log helper."""
     _LOGGER.error("Unable to prepare setup for platform %s: %s",
                   platform_path, msg)
     async_notify_setup_error(hass, platform_path)
Example #10
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)

    try:
        integration = await loader.async_get_integration(hass, domain)
    except loader.IntegrationNotFound:
        log_error("Integration not found.", False)
        return False

    # Validate all dependencies exist and there are no circular dependencies
    try:
        await loader.async_component_dependencies(hass, domain)
    except loader.IntegrationNotFound 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

    # Process requirements as soon as possible, so we can import the component
    # without requiring imports to be in functions.
    try:
        await async_process_deps_reqs(hass, config, integration)
    except HomeAssistantError as err:
        log_error(str(err))
        return False

    processed_config = await conf_util.async_process_component_config(
        hass, config, integration)

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

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

    try:
        component = integration.get_component()
    except ImportError:
        log_error("Unable to import component", False)
        return False

    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)
        elif hasattr(component, 'setup'):
            result = await hass.async_add_executor_job(
                component.setup, hass, processed_config)  # type: ignore
        else:
            log_error("No setup function defined.")
            return False
    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("Integration failed to initialize.")
        return False
    if result is not True:
        log_error("Integration {!r} did not return boolean if setup was "
                  "successful. Disabling component.".format(domain))
        return False

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

    hass.config.components.add(domain)

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

    hass.bus.async_fire(
        EVENT_COMPONENT_LOADED,
        {ATTR_COMPONENT: domain}
    )

    return True
Example #11
0
async def _async_setup_component(
    hass: core.HomeAssistant, domain: str, config: ConfigType
) -> bool:
    """Set up a component for Home Assistant.

    This method is a coroutine.
    """

    def log_error(msg: str, link: str | None = None) -> None:
        """Log helper."""
        _LOGGER.error("Setup failed for %s: %s", domain, msg)
        async_notify_setup_error(hass, domain, link)

    try:
        integration = await loader.async_get_integration(hass, domain)
    except loader.IntegrationNotFound:
        log_error("Integration not found.")
        return False

    if integration.disabled:
        log_error(f"Dependency is disabled - {integration.disabled}")
        return False

    # Validate all dependencies exist and there are no circular dependencies
    if not await integration.resolve_dependencies():
        return False

    # Process requirements as soon as possible, so we can import the component
    # without requiring imports to be in functions.
    try:
        await async_process_deps_reqs(hass, config, integration)
    except HomeAssistantError as err:
        log_error(str(err), integration.documentation)
        return False

    # Some integrations fail on import because they call functions incorrectly.
    # So we do it before validating config to catch these errors.
    try:
        component = integration.get_component()
    except ImportError as err:
        log_error(f"Unable to import component: {err}", integration.documentation)
        return False
    except Exception:  # pylint: disable=broad-except
        _LOGGER.exception("Setup failed for %s: unknown error", domain)
        return False

    processed_config = await conf_util.async_process_component_config(
        hass, config, integration
    )

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

    start = timer()
    _LOGGER.info("Setting up %s", domain)
    with async_start_setup(hass, [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,
            )

        task = None
        result = True
        try:
            if hasattr(component, "async_setup"):
                task = component.async_setup(hass, processed_config)  # type: ignore
            elif hasattr(component, "setup"):
                # This should not be replaced with hass.async_add_executor_job because
                # we don't want to track this task in case it blocks startup.
                task = hass.loop.run_in_executor(
                    None, component.setup, hass, processed_config  # type: ignore
                )
            elif not hasattr(component, "async_setup_entry"):
                log_error("No setup or config entry setup function defined.")
                return False

            if task:
                async with hass.timeout.async_timeout(SLOW_SETUP_MAX_WAIT, domain):
                    result = await task
        except asyncio.TimeoutError:
            _LOGGER.error(
                "Setup of %s is taking longer than %s seconds."
                " Startup will proceed without waiting any longer",
                domain,
                SLOW_SETUP_MAX_WAIT,
            )
            return False
        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception("Error during setup of component %s", domain)
            async_notify_setup_error(hass, domain, integration.documentation)
            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("Integration failed to initialize.")
            return False
        if result is not True:
            log_error(
                f"Integration {domain!r} did not return boolean if setup was "
                "successful. Disabling component."
            )
            return False

        # Flush out async_setup calling create_task. Fragile but covered by test.
        await asyncio.sleep(0)
        await hass.config_entries.flow.async_wait_init_flow_finish(domain)

        await asyncio.gather(
            *(
                entry.async_setup(hass, integration=integration)
                for entry in hass.config_entries.async_entries(domain)
            )
        )

        hass.config.components.add(domain)

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

    hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: domain})

    return True
Example #12
0
 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)
Example #13
0
def _async_setup_component(hass: core.HomeAssistant,
                           domain: str, config) -> bool:
    """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.
    """
    def log_error(msg, link=True):
        """Log helper."""
        _LOGGER.error('Setup failed for %s: %s', domain, msg)
        async_notify_setup_error(hass, domain, link)

    component = loader.get_component(domain)

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

    # Validate no circular dependencies
    components = loader.load_order_component(domain)

    # OrderedSet is empty if component or dependencies could not be resolved
    if not components:
        log_error('Unable to resolve component or dependencies.')
        return False

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

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

    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

    if hasattr(component, 'DEPENDENCIES'):
        dep_success = yield from _async_process_dependencies(
            hass, config, domain, component.DEPENDENCIES)

        if not dep_success:
            log_error('Could not setup all dependencies.')
            return False

    async_comp = hasattr(component, 'async_setup')

    _LOGGER.info("Setting up %s", domain)
    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 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
    finally:
        warn_task.cancel()

    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

    hass.config.components.add(component.DOMAIN)

    # 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}
    )

    return True
Example #14
0
 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)
Example #15
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