Exemple #1
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Logbook setup."""
    hass.data[DOMAIN] = {}

    @callback
    def log_message(service: ServiceCall) -> None:
        """Handle sending notification message service calls."""
        message = service.data[ATTR_MESSAGE]
        name = service.data[ATTR_NAME]
        domain = service.data.get(ATTR_DOMAIN)
        entity_id = service.data.get(ATTR_ENTITY_ID)

        if entity_id is None and domain is None:
            # If there is no entity_id or
            # domain, the event will get filtered
            # away so we use the "logbook" domain
            domain = DOMAIN

        message.hass = hass
        message = message.async_render(parse_result=False)
        async_log_entry(hass, name, message, domain, entity_id)

    frontend.async_register_built_in_panel(hass, "logbook", "logbook",
                                           "hass:format-list-bulleted-type")

    if conf := config.get(DOMAIN, {}):
        filters = sqlalchemy_filter_from_include_exclude_conf(conf)
        entities_filter = convert_include_exclude_filter(conf)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the config component."""
    frontend.async_register_built_in_panel(
        hass, "config", "config", "hass:cog", require_admin=True
    )

    async def setup_panel(panel_name):
        """Set up a panel."""
        panel = importlib.import_module(f".{panel_name}", __name__)

        if not panel:
            return

        success = await panel.async_setup(hass)

        if success:
            key = f"{DOMAIN}.{panel_name}"
            hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: key})

    tasks = [asyncio.create_task(setup_panel(panel_name)) for panel_name in SECTIONS]

    if tasks:
        await asyncio.wait(tasks)

    return True
Exemple #3
0
async def async_register_panel(
    hass: HomeAssistant,
    # The url to serve the panel
    frontend_url_path: str,
    # The webcomponent name that loads your panel
    webcomponent_name: str,
    # Title/icon for sidebar
    sidebar_title: str | None = None,
    sidebar_icon: str | None = None,
    # JS source of your panel
    js_url: str | None = None,
    # JS module of your panel
    module_url: str | None = None,
    # If your panel should be run inside an iframe
    embed_iframe: bool = DEFAULT_EMBED_IFRAME,
    # Should user be asked for confirmation when loading external source
    trust_external: bool = DEFAULT_TRUST_EXTERNAL,
    # Configuration to be passed to the panel
    config: ConfigType | None = None,
    # If your panel should only be shown to admin users
    require_admin: bool = False,
) -> None:
    """Register a new custom panel."""
    if js_url is None and module_url is None:
        raise ValueError("Either js_url, module_url or html_url is required.")
    if config is not None and not isinstance(config, dict):
        raise ValueError("Config needs to be a dictionary.")

    custom_panel_config = {
        "name": webcomponent_name,
        "embed_iframe": embed_iframe,
        "trust_external": trust_external,
    }

    if js_url is not None:
        custom_panel_config["js_url"] = js_url

    if module_url is not None:
        custom_panel_config["module_url"] = module_url

    if config is not None:
        # Make copy because we're mutating it
        config = dict(config)
    else:
        config = {}

    config["_panel_custom"] = custom_panel_config

    frontend.async_register_built_in_panel(
        hass,
        component_name="custom",
        sidebar_title=sidebar_title,
        sidebar_icon=sidebar_icon,
        frontend_url_path=frontend_url_path,
        config=config,
        require_admin=require_admin,
    )
Exemple #4
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Logbook setup."""
    hass.data[DOMAIN] = {}

    @callback
    def log_message(service: ServiceCall) -> None:
        """Handle sending notification message service calls."""
        message = service.data[ATTR_MESSAGE]
        name = service.data[ATTR_NAME]
        domain = service.data.get(ATTR_DOMAIN)
        entity_id = service.data.get(ATTR_ENTITY_ID)

        if entity_id is None and domain is None:
            # If there is no entity_id or
            # domain, the event will get filtered
            # away so we use the "logbook" domain
            domain = DOMAIN

        message.hass = hass
        message = message.async_render(parse_result=False)
        async_log_entry(hass, name, message, domain, entity_id,
                        service.context)

    frontend.async_register_built_in_panel(hass, "logbook", "logbook",
                                           "hass:format-list-bulleted-type")

    recorder_conf = config.get(RECORDER_DOMAIN, {})
    logbook_conf = config.get(DOMAIN, {})
    recorder_filter = extract_include_exclude_filter_conf(recorder_conf)
    logbook_filter = extract_include_exclude_filter_conf(logbook_conf)
    merged_filter = merge_include_exclude_filters(recorder_filter,
                                                  logbook_filter)

    possible_merged_entities_filter = convert_include_exclude_filter(
        merged_filter)
    if not possible_merged_entities_filter.empty_filter:
        filters = sqlalchemy_filter_from_include_exclude_conf(merged_filter)
        entities_filter = possible_merged_entities_filter
    else:
        filters = None
        entities_filter = None
    hass.data[LOGBOOK_FILTERS] = filters
    hass.data[LOGBOOK_ENTITIES_FILTER] = entities_filter
    websocket_api.async_setup(hass)
    rest_api.async_setup(hass, config, filters, entities_filter)
    hass.services.async_register(DOMAIN,
                                 "log",
                                 log_message,
                                 schema=LOG_MESSAGE_SCHEMA)

    await async_process_integration_platforms(hass, DOMAIN,
                                              _process_logbook_platform)

    return True
Exemple #5
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the media_source component."""
    hass.data[DOMAIN] = {}
    websocket_api.async_register_command(hass, websocket_browse_media)
    websocket_api.async_register_command(hass, websocket_resolve_media)
    frontend.async_register_built_in_panel(hass, "media-browser",
                                           "media_browser",
                                           "hass:play-box-multiple")
    local_source.async_setup(hass)
    await async_process_integration_platforms(hass, DOMAIN,
                                              _process_media_source_platform)
    return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Track states and offer events for calendars."""
    component = hass.data[DOMAIN] = EntityComponent(_LOGGER, DOMAIN, hass,
                                                    SCAN_INTERVAL)

    hass.http.register_view(CalendarListView(component))
    hass.http.register_view(CalendarEventView(component))

    frontend.async_register_built_in_panel(hass, "calendar", "calendar",
                                           "hass:calendar")

    await component.async_setup(config)
    return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up Energy."""
    websocket_api.async_setup(hass)
    frontend.async_register_built_in_panel(hass, DOMAIN, DOMAIN,
                                           "mdi:lightning-bolt")

    hass.async_create_task(
        discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config))
    hass.data[DOMAIN] = {
        "cost_sensors": {},
    }

    return True
Exemple #8
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the iFrame frontend panels."""
    for url_path, info in config[DOMAIN].items():
        frontend.async_register_built_in_panel(
            hass,
            "iframe",
            info.get(CONF_TITLE),
            info.get(CONF_ICON),
            url_path,
            {"url": info[CONF_URL]},
            require_admin=info[CONF_REQUIRE_ADMIN],
        )

    return True
def _register_panel(hass, url_path, mode, config, update):
    """Register a panel."""
    kwargs = {
        "frontend_url_path": url_path,
        "require_admin": config[CONF_REQUIRE_ADMIN],
        "config": {"mode": mode},
        "update": update,
    }

    if config[CONF_SHOW_IN_SIDEBAR]:
        kwargs["sidebar_title"] = config[CONF_TITLE]
        kwargs["sidebar_icon"] = config.get(CONF_ICON, DEFAULT_ICON)

    frontend.async_register_built_in_panel(hass, DOMAIN, **kwargs)
async def update_dashboards() -> None:
    """Add this integrations Lovelace dashboard."""

    hass = get_base().hass

    config = {
        CONF_MODE:
        MODE_YAML,
        CONF_TITLE:
        TITLE,
        CONF_ICON:
        LOVELACE_DASHBOARD_ICON,
        CONF_SHOW_IN_SIDEBAR:
        True,
        CONF_REQUIRE_ADMIN:
        False,
        CONF_FILENAME:
        os.path.abspath(
            os.path.join(
                os.path.dirname(__file__),
                os.pardir,
                LOVELACE_DIR,
                LOVELACE_FILENAME_SOURCE,
            )),
    }

    yaml_config = LovelaceYAML(hass, LOVELACE_DASHBOARD_URL_PATH, config)
    hass.data[LOVELACE_DOMAIN][CONF_DASHBOARDS][
        LOVELACE_DASHBOARD_URL_PATH] = yaml_config

    kwargs = {
        "frontend_url_path": LOVELACE_DASHBOARD_URL_PATH,
        "require_admin": config[CONF_REQUIRE_ADMIN],
        "config": {
            CONF_MODE: config[CONF_MODE]
        },
        "update": False,
        "sidebar_title": config[CONF_TITLE],
        "sidebar_icon": config[CONF_ICON],
    }

    if LOVELACE_DASHBOARD_URL_PATH in hass.data.get(DATA_PANELS, {}):
        async_remove_panel(hass, LOVELACE_DASHBOARD_URL_PATH)

    async_register_built_in_panel(hass, LOVELACE_DOMAIN, **kwargs)

    # Refresh lovelace using browser_mod
    hass.async_create_task(
        hass.services.async_call("browser_mod", "lovelace_reload"))
Exemple #11
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the history hooks."""
    conf = config.get(DOMAIN, {})

    filters = sqlalchemy_filter_from_include_exclude_conf(conf)

    use_include_order = conf.get(CONF_ORDER)

    hass.http.register_view(HistoryPeriodView(filters, use_include_order))
    frontend.async_register_built_in_panel(hass, "history", "history",
                                           "hass:chart-box")
    websocket_api.async_register_command(hass, ws_get_statistics_during_period)
    websocket_api.async_register_command(hass, ws_get_list_statistic_ids)

    return True
Exemple #12
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the config component."""
    frontend.async_register_built_in_panel(
        hass, "config", "config", "hass:cog", require_admin=True
    )

    async def setup_panel(panel_name):
        """Set up a panel."""
        panel = importlib.import_module(f".{panel_name}", __name__)

        if not panel:
            return

        success = await panel.async_setup(hass)

        if success:
            key = f"{DOMAIN}.{panel_name}"
            hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: key})

    @callback
    def component_loaded(event):
        """Respond to components being loaded."""
        panel_name = event.data.get(ATTR_COMPONENT)
        if panel_name in ON_DEMAND:
            hass.async_create_task(setup_panel(panel_name))

    hass.bus.async_listen(EVENT_COMPONENT_LOADED, component_loaded)

    tasks = [asyncio.create_task(setup_panel(panel_name)) for panel_name in SECTIONS]

    for panel_name in ON_DEMAND:
        if panel_name in hass.config.components:
            tasks.append(asyncio.create_task(setup_panel(panel_name)))

    if tasks:
        await asyncio.wait(tasks)

    return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Lovelace commands."""
    mode = config[DOMAIN][CONF_MODE]
    yaml_resources = config[DOMAIN].get(CONF_RESOURCES)

    frontend.async_register_built_in_panel(hass, DOMAIN, config={"mode": mode})

    async def reload_resources_service_handler(service_call: ServiceCall) -> None:
        """Reload yaml resources."""
        try:
            conf = await async_hass_config_yaml(hass)
        except HomeAssistantError as err:
            _LOGGER.error(err)
            return

        integration = await async_get_integration(hass, DOMAIN)

        config = await async_process_component_config(hass, conf, integration)

        resource_collection = await create_yaml_resource_col(
            hass, config[DOMAIN].get(CONF_RESOURCES)
        )
        hass.data[DOMAIN]["resources"] = resource_collection

    if mode == MODE_YAML:
        default_config = dashboard.LovelaceYAML(hass, None, None)
        resource_collection = await create_yaml_resource_col(hass, yaml_resources)

        async_register_admin_service(
            hass,
            DOMAIN,
            SERVICE_RELOAD_RESOURCES,
            reload_resources_service_handler,
            schema=RESOURCE_RELOAD_SERVICE_SCHEMA,
        )

    else:
        default_config = dashboard.LovelaceStorage(hass, None)

        if yaml_resources is not None:
            _LOGGER.warning(
                "Lovelace is running in storage mode. Define resources via user interface"
            )

        resource_collection = resources.ResourceStorageCollection(hass, default_config)

        collection.StorageCollectionWebsocket(
            resource_collection,
            "lovelace/resources",
            "resource",
            RESOURCE_CREATE_FIELDS,
            RESOURCE_UPDATE_FIELDS,
        ).async_setup(hass, create_list=False)

    websocket_api.async_register_command(hass, websocket.websocket_lovelace_config)
    websocket_api.async_register_command(hass, websocket.websocket_lovelace_save_config)
    websocket_api.async_register_command(
        hass, websocket.websocket_lovelace_delete_config
    )
    websocket_api.async_register_command(hass, websocket.websocket_lovelace_resources)

    websocket_api.async_register_command(hass, websocket.websocket_lovelace_dashboards)

    hass.data[DOMAIN] = {
        # We store a dictionary mapping url_path: config. None is the default.
        "mode": mode,
        "dashboards": {None: default_config},
        "resources": resource_collection,
        "yaml_dashboards": config[DOMAIN].get(CONF_DASHBOARDS, {}),
    }

    if hass.config.safe_mode:
        return True

    async def storage_dashboard_changed(change_type, item_id, item):
        """Handle a storage dashboard change."""
        url_path = item[CONF_URL_PATH]

        if change_type == collection.CHANGE_REMOVED:
            frontend.async_remove_panel(hass, url_path)
            await hass.data[DOMAIN]["dashboards"].pop(url_path).async_delete()
            return

        if change_type == collection.CHANGE_ADDED:

            existing = hass.data[DOMAIN]["dashboards"].get(url_path)

            if existing:
                _LOGGER.warning(
                    "Cannot register panel at %s, it is already defined in %s",
                    url_path,
                    existing,
                )
                return

            hass.data[DOMAIN]["dashboards"][url_path] = dashboard.LovelaceStorage(
                hass, item
            )

            update = False
        else:
            hass.data[DOMAIN]["dashboards"][url_path].config = item
            update = True

        try:
            _register_panel(hass, url_path, MODE_STORAGE, item, update)
        except ValueError:
            _LOGGER.warning("Failed to %s panel %s from storage", change_type, url_path)

    # Process YAML dashboards
    for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items():
        # For now always mode=yaml
        config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf)
        hass.data[DOMAIN]["dashboards"][url_path] = config

        try:
            _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False)
        except ValueError:
            _LOGGER.warning("Panel url path %s is not unique", url_path)

    # Process storage dashboards
    dashboards_collection = dashboard.DashboardsCollection(hass)

    dashboards_collection.async_add_listener(storage_dashboard_changed)
    await dashboards_collection.async_load()

    collection.StorageCollectionWebsocket(
        dashboards_collection,
        "lovelace/dashboards",
        "dashboard",
        STORAGE_DASHBOARD_CREATE_FIELDS,
        STORAGE_DASHBOARD_UPDATE_FIELDS,
    ).async_setup(hass, create_list=False)

    return True
Exemple #14
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Register hidden _my_redirect panel."""
    frontend.async_register_built_in_panel(hass,
                                           DOMAIN,
                                           frontend_url_path=URL_PATH)
    return True
Exemple #15
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Register the built-in map panel."""
    frontend.async_register_built_in_panel(hass, "map", "map",
                                           "hass:tooltip-account")
    return True
Exemple #16
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Track states and offer events for mailboxes."""
    mailboxes: list[Mailbox] = []
    frontend.async_register_built_in_panel(hass, "mailbox", "mailbox",
                                           "mdi:mailbox")
    hass.http.register_view(MailboxPlatformsView(mailboxes))
    hass.http.register_view(MailboxMessageView(mailboxes))
    hass.http.register_view(MailboxMediaView(mailboxes))
    hass.http.register_view(MailboxDeleteView(mailboxes))

    async def async_setup_platform(
        p_type: str,
        p_config: ConfigType | None = None,
        discovery_info: DiscoveryInfoType | None = None,
    ) -> None:
        """Set up a mailbox platform."""
        if p_config is None:
            p_config = {}
        if discovery_info is None:
            discovery_info = {}

        platform = await async_prepare_setup_platform(hass, config, DOMAIN,
                                                      p_type)

        if platform is None:
            _LOGGER.error("Unknown mailbox platform specified")
            return

        _LOGGER.info("Setting up %s.%s", DOMAIN, p_type)
        mailbox = None
        try:
            if hasattr(platform, "async_get_handler"):
                mailbox = await platform.async_get_handler(
                    hass, p_config, discovery_info)
            elif hasattr(platform, "get_handler"):
                mailbox = await hass.async_add_executor_job(
                    platform.get_handler, hass, p_config, discovery_info)
            else:
                raise HomeAssistantError("Invalid mailbox platform.")

            if mailbox is None:
                _LOGGER.error("Failed to initialize mailbox platform %s",
                              p_type)
                return

        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception("Error setting up platform %s", p_type)
            return

        mailboxes.append(mailbox)
        mailbox_entity = MailboxEntity(mailbox)
        component = EntityComponent(logging.getLogger(__name__), DOMAIN, hass,
                                    SCAN_INTERVAL)
        await component.async_add_entities([mailbox_entity])

    setup_tasks = [
        asyncio.create_task(async_setup_platform(p_type, p_config))
        for p_type, p_config in config_per_platform(config, DOMAIN)
        if p_type is not None
    ]

    if setup_tasks:
        await asyncio.wait(setup_tasks)

    async def async_platform_discovered(platform, info):
        """Handle for discovered platform."""
        await async_setup_platform(platform, discovery_info=info)

    discovery.async_listen_platform(hass, DOMAIN, async_platform_discovered)

    return True
Exemple #17
0
        schema=SERVICE_LIST_SCHEMA,
    )
    hass.services.async_register(
        DOMAIN,
        SERVICE_CLEAR_COMPLETED_ITEMS,
        clear_completed_items_service,
        schema=SERVICE_LIST_SCHEMA,
    )

    hass.http.register_view(ShoppingListView)
    hass.http.register_view(CreateShoppingListItemView)
    hass.http.register_view(UpdateShoppingListItemView)
    hass.http.register_view(ClearCompletedItemsView)

    frontend.async_register_built_in_panel(
        hass, "shopping-list", "shopping_list", "mdi:cart"
    )

    websocket_api.async_register_command(
        hass,
        WS_TYPE_SHOPPING_LIST_ITEMS,
        websocket_handle_items,
        SCHEMA_WEBSOCKET_ITEMS,
    )
    websocket_api.async_register_command(
        hass,
        WS_TYPE_SHOPPING_LIST_ADD_ITEM,
        websocket_handle_add,
        SCHEMA_WEBSOCKET_ADD_ITEM,
    )
    websocket_api.async_register_command(
Exemple #18
0
async def async_setup(hass: HomeAssistantType, config: ConfigType):
    """Set up the Lovelace commands."""
    mode = config[DOMAIN][CONF_MODE]
    yaml_resources = config[DOMAIN].get(CONF_RESOURCES)

    frontend.async_register_built_in_panel(hass, DOMAIN, config={"mode": mode})

    # ais fix migration dashboards - remove in version 1.0
    # run this only if lovelace_dashboards not exist and lovelace exists
    storage_path = "/data/data/pl.sviete.dom/files/home/AIS/.storage/"
    import os
    import shutil

    if os.path.exists(storage_path + "lovelace") and not os.path.exists(
            storage_path + "lovelace_dashboards"):
        ais_dom_lovelace_path = (str(os.path.dirname(__file__)) +
                                 "/ais_dom_lovelace_full")
        ais_dom_dashboards_path = (str(os.path.dirname(__file__)) +
                                   "/lovelace_dashboards")
        # 1. move current lovelace
        shutil.move(storage_path + "lovelace",
                    storage_path + "lovelace.lovelace_dom")
        # 2. copy ais dom lovelace as default lovelace
        shutil.copy(ais_dom_lovelace_path, storage_path + "lovelace")
        # 3. copy lovelace_dashboards
        shutil.copy(ais_dom_dashboards_path,
                    storage_path + "lovelace_dashboards")

    async def reload_resources_service_handler(
            service_call: ServiceCallType) -> None:
        """Reload yaml resources."""
        try:
            conf = await async_hass_config_yaml(hass)
        except HomeAssistantError as err:
            _LOGGER.error(err)
            return

        integration = await async_get_integration(hass, DOMAIN)

        config = await async_process_component_config(hass, conf, integration)

        resource_collection = await create_yaml_resource_col(
            hass, config[DOMAIN].get(CONF_RESOURCES))
        hass.data[DOMAIN]["resources"] = resource_collection

    if mode == MODE_YAML:
        default_config = dashboard.LovelaceYAML(hass, None, None)
        resource_collection = await create_yaml_resource_col(
            hass, yaml_resources)

        async_register_admin_service(
            hass,
            DOMAIN,
            SERVICE_RELOAD_RESOURCES,
            reload_resources_service_handler,
            schema=RESOURCE_RELOAD_SERVICE_SCHEMA,
        )

    else:
        default_config = dashboard.LovelaceStorage(hass, None)

        if yaml_resources is not None:
            _LOGGER.warning(
                "Lovelace is running in storage mode. Define resources via user interface"
            )

        resource_collection = resources.ResourceStorageCollection(
            hass, default_config)

        collection.StorageCollectionWebsocket(
            resource_collection,
            "lovelace/resources",
            "resource",
            RESOURCE_CREATE_FIELDS,
            RESOURCE_UPDATE_FIELDS,
        ).async_setup(hass, create_list=False)

    hass.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_config)
    hass.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_save_config)
    hass.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_delete_config)
    hass.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_resources)

    hass.components.websocket_api.async_register_command(
        websocket.websocket_lovelace_dashboards)

    hass.components.system_health.async_register_info(DOMAIN,
                                                      system_health_info)

    hass.data[DOMAIN] = {
        # We store a dictionary mapping url_path: config. None is the default.
        "dashboards": {
            None: default_config
        },
        "resources": resource_collection,
        "yaml_dashboards": config[DOMAIN].get(CONF_DASHBOARDS, {}),
    }

    if hass.config.safe_mode:
        return True

    async def storage_dashboard_changed(change_type, item_id, item):
        """Handle a storage dashboard change."""
        url_path = item[CONF_URL_PATH]

        if change_type == collection.CHANGE_REMOVED:
            frontend.async_remove_panel(hass, url_path)
            await hass.data[DOMAIN]["dashboards"].pop(url_path).async_delete()
            return

        if change_type == collection.CHANGE_ADDED:

            existing = hass.data[DOMAIN]["dashboards"].get(url_path)

            if existing:
                _LOGGER.warning(
                    "Cannot register panel at %s, it is already defined in %s",
                    url_path,
                    existing,
                )
                return

            hass.data[DOMAIN]["dashboards"][
                url_path] = dashboard.LovelaceStorage(hass, item)

            update = False
        else:
            hass.data[DOMAIN]["dashboards"][url_path].config = item
            update = True

        try:
            _register_panel(hass, url_path, MODE_STORAGE, item, update)
        except ValueError:
            _LOGGER.warning("Failed to %s panel %s from storage", change_type,
                            url_path)

    # Process YAML dashboards
    for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items(
    ):
        # For now always mode=yaml
        config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf)
        hass.data[DOMAIN]["dashboards"][url_path] = config

        try:
            _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False)
        except ValueError:
            _LOGGER.warning("Panel url path %s is not unique", url_path)

    # Process storage dashboards
    dashboards_collection = dashboard.DashboardsCollection(hass)

    dashboards_collection.async_add_listener(storage_dashboard_changed)
    await dashboards_collection.async_load()

    collection.StorageCollectionWebsocket(
        dashboards_collection,
        "lovelace/dashboards",
        "dashboard",
        STORAGE_DASHBOARD_CREATE_FIELDS,
        STORAGE_DASHBOARD_UPDATE_FIELDS,
    ).async_setup(hass, create_list=False)

    return True