diff --git a/src/common/dom/media_query.ts b/src/common/dom/media_query.ts new file mode 100644 index 0000000000..df8ba93668 --- /dev/null +++ b/src/common/dom/media_query.ts @@ -0,0 +1,16 @@ +/** + * Attach a media query. Listener is called right away and when it matches. + * @param mediaQuery media query to match. + * @param listener listener to call when media query changes between match/unmatch + * @returns function to remove the listener. + */ +export const listenMediaQuery = ( + mediaQuery: string, + matchesChanged: (matches: boolean) => void +) => { + const mql = matchMedia(mediaQuery); + const listener = (e) => matchesChanged(e.matches); + mql.addListener(listener); + matchesChanged(mql.matches); + return () => mql.removeListener(listener); +}; diff --git a/src/data/cloud.ts b/src/data/cloud.ts index 75348bc94a..b15f8b3066 100644 --- a/src/data/cloud.ts +++ b/src/data/cloud.ts @@ -47,6 +47,9 @@ export interface CloudWebhook { managed?: boolean; } +export const fetchCloudStatus = (hass: HomeAssistant) => + hass.callWS({ type: "cloud/status" }); + export const createCloudhook = (hass: HomeAssistant, webhookId: string) => hass.callWS({ type: "cloud/cloudhook/create", diff --git a/src/layouts/hass-loading-screen.ts b/src/layouts/hass-loading-screen.ts index cb52f12dba..9926a9517c 100644 --- a/src/layouts/hass-loading-screen.ts +++ b/src/layouts/hass-loading-screen.ts @@ -7,16 +7,28 @@ import { CSSResultArray, css, customElement, + property, } from "lit-element"; import "../components/ha-menu-button"; +import "../components/ha-paper-icon-button-arrow-prev"; import { haStyle } from "../resources/styles"; @customElement("hass-loading-screen") class HassLoadingScreen extends LitElement { + @property() public isRoot? = false; + protected render(): TemplateResult | void { return html` - + ${this.isRoot + ? html` + + ` + : html` + + `}
@@ -24,6 +36,10 @@ class HassLoadingScreen extends LitElement { `; } + private _handleBack() { + history.back(); + } + static get styles(): CSSResultArray { return [ haStyle, diff --git a/src/layouts/hass-router-page.ts b/src/layouts/hass-router-page.ts new file mode 100644 index 0000000000..b46119c0e3 --- /dev/null +++ b/src/layouts/hass-router-page.ts @@ -0,0 +1,192 @@ +import { UpdatingElement, property, PropertyValues } from "lit-element"; +import "./hass-error-screen"; +import { Route } from "../types"; +import { navigate } from "../common/navigate"; + +const extractPage = (path: string, defaultPage: string) => { + if (path === "") { + return defaultPage; + } + const subpathStart = path.indexOf("/", 1); + return subpathStart === -1 + ? path.substr(1) + : path.substr(1, subpathStart - 1); +}; + +interface RouteOptions { + tag: string; + load: () => Promise; + cache?: boolean; +} + +export interface RouterOptions { + isRoot?: boolean; + defaultPage?: string; + preloadAll?: boolean; + cacheAll?: boolean; + showLoading?: boolean; + routes: { + [route: string]: RouteOptions; + }; +} + +// Time to wait for code to load before we show loading screen. +const LOADING_SCREEN_THRESHOLD = 400; // ms + +export class HassRouterPage extends UpdatingElement { + protected static routerOptions: RouterOptions = { routes: {} }; + + protected static finalize() { + super.finalize(); + this._routerOptions = this.routerOptions; + } + + private static _routerOptions: RouterOptions; + + @property() public route!: Route; + private _currentPage = ""; + private _cache = {}; + + protected update(changedProps: PropertyValues) { + super.update(changedProps); + + if (!changedProps.has("route")) { + if (this.lastChild) { + this._updatePageEl(this.lastChild, changedProps); + } + return; + } + + const route = this.route; + + const routerOptions = (this.constructor as typeof HassRouterPage) + ._routerOptions; + const defaultPage = routerOptions.defaultPage || ""; + + if (route && route.path === "") { + navigate(this, `${route.prefix}/${defaultPage}`, true); + } + + const newPage = route ? extractPage(route.path, defaultPage) : "not_found"; + + if (this._currentPage === newPage) { + if (this.lastChild) { + this._updatePageEl(this.lastChild, changedProps); + } + return; + } + + this._currentPage = newPage; + + const routeOptions = routerOptions.routes[newPage]; + + if (!routeOptions) { + if (this.lastChild) { + this._updatePageEl(this.lastChild, changedProps); + } + return; + } + + const loadProm = routeOptions.load(); + + // Check when loading the page source failed. + loadProm.catch(() => { + // Verify that we're still trying to show the same page. + if (this._currentPage !== newPage) { + return; + } + + // Removes either loading screen or the panel + this.removeChild(this.lastChild!); + + // Show error screen + const errorEl = document.createElement("hass-error-screen"); + errorEl.error = `Error while loading page ${newPage}.`; + this.appendChild(errorEl); + }); + + // If we don't show loading screen, just show the panel. + // It will be automatically upgraded when loading done. + if (!routerOptions.showLoading) { + this._createPanel(routerOptions, newPage, routeOptions); + return; + } + + // We are only going to show the loading screen after some time. + // That way we won't have a double fast flash on fast connections. + let created = false; + + setTimeout(() => { + if (created || this._currentPage !== newPage) { + return; + } + + // Show a loading screen. + if (this.lastChild) { + this.removeChild(this.lastChild); + } + + const loadingEl = document.createElement("hass-loading-screen"); + loadingEl.isRoot = routerOptions.isRoot; + this.appendChild(loadingEl); + }, LOADING_SCREEN_THRESHOLD); + + loadProm.then(() => { + // Check if we're still trying to show the same page. + if (this._currentPage !== newPage) { + return; + } + + created = true; + this._createPanel(routerOptions, newPage, routeOptions); + }); + } + + protected firstUpdated(changedProps: PropertyValues) { + super.firstUpdated(changedProps); + + const options = (this.constructor as typeof HassRouterPage)._routerOptions; + + if (options.preloadAll) { + Object.values(options.routes).forEach((route) => route.load()); + return; + } + } + + protected _updatePageEl(_pageEl, _changedProps?: PropertyValues) { + // default we do nothing + } + + private _createPanel( + routerOptions: RouterOptions, + page: string, + routeOptions: RouteOptions + ) { + if (this.lastChild) { + this.removeChild(this.lastChild); + } + + const panelEl = + this._cache[page] || document.createElement(routeOptions.tag); + this._updatePageEl(panelEl); + this.appendChild(panelEl); + + if (routerOptions.cacheAll || routeOptions.cache) { + this._cache[page] = panelEl; + } + } + + protected get routeTail(): Route { + const route = this.route!; + const dividerPos = route.path.indexOf("/", 1); + return dividerPos === -1 + ? { + prefix: route.path, + path: "", + } + : { + prefix: route.path.substr(0, dividerPos), + path: route.path.substr(dividerPos), + }; + } +} diff --git a/src/layouts/partial-panel-resolver.ts b/src/layouts/partial-panel-resolver.ts index 3503c79a2c..7d5628fe82 100644 --- a/src/layouts/partial-panel-resolver.ts +++ b/src/layouts/partial-panel-resolver.ts @@ -1,252 +1,139 @@ -import { LitElement, html, PropertyValues, property } from "lit-element"; +import { property, customElement } from "lit-element"; +import { PolymerElement } from "@polymer/polymer"; -import "./hass-loading-screen"; -import "./hass-error-screen"; -import { HomeAssistant, Panel, PanelElement, Route } from "../types"; +import { HomeAssistant } from "../types"; +import { HassRouterPage, RouterOptions } from "./hass-router-page"; -// Cache of panel loading promises. -const LOADED: { [panel: string]: Promise } = {}; - -// Which panel elements we will cache. -// Maybe we can cache them all eventually, but not sure yet about -// unknown side effects (like history taking a lot of memory, reset needed) -const CACHED_EL = ["lovelace", "states"]; - -function ensureLoaded(panel): Promise | null { - if (panel in LOADED) { - return LOADED[panel]; - } - - let imported; - // Name each panel we support here, that way Webpack knows about it. - switch (panel) { - case "config": - imported = import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"); - break; - - case "custom": - imported = import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"); - break; - - case "dev-event": - imported = import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"); - break; - - case "dev-info": - imported = import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"); - break; - - case "dev-mqtt": - imported = import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"); - break; - - case "dev-service": - imported = import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"); - break; - - case "dev-state": - imported = import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"); - break; - - case "dev-template": - imported = import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"); - break; - - case "lovelace": - imported = import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"); - break; - - case "states": - imported = import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"); - break; - - case "history": - imported = import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"); - break; - - case "iframe": - imported = import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"); - break; - - case "kiosk": - imported = import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"); - break; - - case "logbook": - imported = import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"); - break; - - case "mailbox": - imported = import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"); - break; - - case "map": - imported = import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"); - break; - - case "profile": - imported = import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"); - break; - - case "shopping-list": - imported = import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"); - break; - - case "calendar": - imported = import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"); - break; - - default: - imported = null; - } - - if (imported != null) { - LOADED[panel] = imported; - } - - return imported; -} - -class PartialPanelResolver extends LitElement { +@customElement("partial-panel-resolver") +class PartialPanelResolver extends HassRouterPage { + protected static routerOptions: RouterOptions = { + isRoot: true, + showLoading: true, + routes: { + calendar: { + tag: "ha-panel-calendar", + load: () => + import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"), + }, + config: { + tag: "ha-panel-config", + load: () => + import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"), + }, + custom: { + tag: "ha-panel-custom", + load: () => + import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"), + }, + "dev-event": { + tag: "ha-panel-dev-event", + load: () => + import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"), + }, + "dev-info": { + tag: "ha-panel-dev-info", + load: () => + import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"), + }, + "dev-mqtt": { + tag: "ha-panel-dev-mqtt", + load: () => + import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"), + }, + "dev-service": { + tag: "ha-panel-dev-service", + load: () => + import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"), + }, + "dev-state": { + tag: "ha-panel-dev-state", + load: () => + import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"), + }, + "dev-template": { + tag: "ha-panel-dev-template", + load: () => + import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"), + }, + lovelace: { + cache: true, + tag: "ha-panel-lovelace", + load: () => + import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"), + }, + states: { + cache: true, + tag: "ha-panel-states", + load: () => + import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"), + }, + history: { + tag: "ha-panel-history", + load: () => + import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"), + }, + iframe: { + tag: "ha-panel-iframe", + load: () => + import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"), + }, + kiosk: { + tag: "ha-panel-kiosk", + load: () => + import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"), + }, + logbook: { + tag: "ha-panel-logbook", + load: () => + import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"), + }, + mailbox: { + tag: "ha-panel-mailbox", + load: () => + import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"), + }, + map: { + tag: "ha-panel-map", + load: () => + import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"), + }, + profile: { + tag: "ha-panel-profile", + load: () => + import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"), + }, + "shopping-list": { + tag: "ha-panel-shopping-list", + load: () => + import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"), + }, + }, + }; @property() public hass?: HomeAssistant; @property() public narrow?: boolean; - @property() public showMenu?: boolean; - @property() public route?: Route | null; - @property() private _routeTail?: Route | null; - @property() private _panelEl?: PanelElement; - @property() private _error?: boolean; - private _panel?: Panel; - private _cache: { [name: string]: PanelElement } = {}; - - protected render() { - if (this._error) { - return html` - - `; - } - - if (!this._panelEl) { - return html` - - `; - } - - return html` - ${this._panelEl} - `; - } - - protected updated(changedProps: PropertyValues) { - super.updated(changedProps); - if (!this.hass) { - return; - } - - if (changedProps.has("route")) { - // Manual splitting - const route = this.route!; - const dividerPos = route.path.indexOf("/", 1); - this._routeTail = - dividerPos === -1 - ? { - prefix: route.path, - path: "", - } - : { - prefix: route.path.substr(0, dividerPos), - path: route.path.substr(dividerPos), - }; - - // If just route changed, no need to process further. - if (changedProps.size === 1) { - return; - } - } - - if (changedProps.has("hass")) { - const panel = this.hass.panels[this.hass.panelUrl]; - - if (panel !== this._panel) { - this._panel = panel; - this._panelEl = undefined; - - // Found cached one, use that - if (panel.component_name in this._cache) { - this._panelEl = this._cache[panel.component_name]; - this._updatePanel(); - return; - } - - const loadingProm = ensureLoaded(panel.component_name); - - if (loadingProm === null) { - this._error = true; - return; - } - - loadingProm.then( - () => { - // If panel changed while loading. - if (this._panel !== panel) { - return; - } - - this._panelEl = (this._panelEl = document.createElement( - `ha-panel-${panel.component_name}` - )) as PanelElement; - - if (CACHED_EL.includes(panel.component_name)) { - this._cache[panel.component_name] = this._panelEl; - } - - this._error = false; - this._updatePanel(); - }, - (err) => { - // tslint:disable-next-line - console.error("Error loading panel", err); - this._error = true; - } - ); - return; - } - } - - this._updatePanel(); - } - - private _updatePanel() { - const el = this._panelEl; - - if (!el) { - return; - } + protected _updatePageEl(el) { + const hass = this.hass!; if ("setProperties" in el) { // As long as we have Polymer panels - (el as any).setProperties({ + (el as PolymerElement).setProperties({ hass: this.hass, narrow: this.narrow, - showMenu: this.showMenu, - route: this._routeTail, - panel: this._panel, + route: this.routeTail, + panel: hass.panels[hass.panelUrl], }); } else { - el.hass = this.hass; + el.hass = hass; el.narrow = this.narrow; - el.showMenu = this.showMenu; - el.route = this._routeTail; - el.panel = this._panel; + el.route = this.routeTail; + el.panel = hass.panels[hass.panelUrl]; } } } -customElements.define("partial-panel-resolver", PartialPanelResolver); +declare global { + interface HTMLElementTagNameMap { + "partial-panel-resolver": PartialPanelResolver; + } +} diff --git a/src/panels/config/ha-panel-config.js b/src/panels/config/ha-panel-config.js deleted file mode 100644 index 726e46ae85..0000000000 --- a/src/panels/config/ha-panel-config.js +++ /dev/null @@ -1,243 +0,0 @@ -import "@polymer/app-route/app-route"; -import "@polymer/iron-media-query/iron-media-query"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; - -import isComponentLoaded from "../../common/config/is_component_loaded"; -import EventsMixin from "../../mixins/events-mixin"; -import NavigateMixin from "../../mixins/navigate-mixin"; - -/* - * @appliesMixin EventsMixin - * @appliesMixin NavigateMixin - */ -class HaPanelConfig extends EventsMixin(NavigateMixin(PolymerElement)) { - static get template() { - return html` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - `; - } - - static get properties() { - return { - hass: Object, - narrow: Boolean, - showMenu: Boolean, - _cloudStatus: { - type: Object, - value: null, - }, - - route: { - type: Object, - observer: "_routeChanged", - }, - - _routeData: Object, - - wide: Boolean, - wideSidebar: Boolean, - - isWide: { - type: Boolean, - computed: "computeIsWide(showMenu, wideSidebar, wide)", - }, - }; - } - - ready() { - super.ready(); - if (isComponentLoaded(this.hass, "cloud")) { - this._updateCloudStatus(); - } - this.addEventListener("ha-refresh-cloud-status", () => - this._updateCloudStatus() - ); - import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry"); - import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"); - import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud"); - import(/* webpackChunkName: "panel-config-config" */ "./config-entries/ha-config-entries"); - import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core"); - import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize"); - import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard"); - import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script"); - import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry"); - import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"); - import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha"); - import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"); - import(/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person"); - } - - async _updateCloudStatus() { - this._cloudStatus = await this.hass.callWS({ type: "cloud/status" }); - - if (this._cloudStatus.cloud === "connecting") { - setTimeout(() => this._updateCloudStatus(), 5000); - } - } - - computeIsWide(showMenu, wideSidebar, wide) { - return showMenu ? wideSidebar : wide; - } - - _routeChanged(route) { - if (route.path === "" && route.prefix === "/config") { - this.navigate("/config/dashboard", true); - } - this.fire("iron-resize"); - } - - _equals(a, b) { - return a === b; - } -} - -customElements.define("ha-panel-config", HaPanelConfig); diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts new file mode 100644 index 0000000000..aad87ca75a --- /dev/null +++ b/src/panels/config/ha-panel-config.ts @@ -0,0 +1,142 @@ +import { property, PropertyValues, customElement } from "lit-element"; +import "../../layouts/hass-loading-screen"; +import isComponentLoaded from "../../common/config/is_component_loaded"; +import { HomeAssistant } from "../../types"; +import { CloudStatus, fetchCloudStatus } from "../../data/cloud"; +import { listenMediaQuery } from "../../common/dom/media_query"; +import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page"; + +@customElement("ha-panel-config") +class HaPanelConfig extends HassRouterPage { + protected static routerOptions: RouterOptions = { + defaultPage: "dashboard", + cacheAll: true, + preloadAll: true, + routes: { + area_registry: { + tag: "ha-config-area-registry", + load: () => + import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry"), + }, + automation: { + tag: "ha-config-automation", + load: () => + import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"), + }, + cloud: { + tag: "ha-config-cloud", + load: () => + import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud"), + }, + core: { + tag: "ha-config-core", + load: () => + import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core"), + }, + customize: { + tag: "ha-config-customize", + load: () => + import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize"), + }, + dashboard: { + tag: "ha-config-dashboard", + load: () => + import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard"), + }, + entity_registry: { + tag: "ha-config-entity-registry", + load: () => + import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry"), + }, + integrations: { + tag: "ha-config-integrations", + load: () => + import(/* webpackChunkName: "panel-config-integrations" */ "./integrations/ha-config-integrations"), + }, + person: { + tag: "ha-config-person", + load: () => + import(/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person"), + }, + script: { + tag: "ha-config-script", + load: () => + import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script"), + }, + users: { + tag: "ha-config-users", + load: () => + import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"), + }, + zha: { + tag: "ha-config-zha", + load: () => + import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha"), + }, + zwave: { + tag: "ha-config-zwave", + load: () => + import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"), + }, + }, + }; + + @property() public hass!: HomeAssistant; + @property() public _wideSidebar: boolean = false; + @property() public _wide: boolean = false; + @property() private _cloudStatus?: CloudStatus; + + private _listeners: Array<() => void> = []; + + public connectedCallback() { + super.connectedCallback(); + this._listeners.push( + listenMediaQuery("(min-width: 1040px)", (matches) => { + this._wide = matches; + }) + ); + this._listeners.push( + listenMediaQuery("(min-width: 1296px)", (matches) => { + this._wideSidebar = matches; + }) + ); + } + + public disconnectedCallback() { + super.disconnectedCallback(); + while (this._listeners.length) { + this._listeners.pop()!(); + } + } + + protected firstUpdated(changedProps: PropertyValues) { + super.firstUpdated(changedProps); + if (isComponentLoaded(this.hass, "cloud")) { + this._updateCloudStatus(); + } + this.addEventListener("ha-refresh-cloud-status", () => + this._updateCloudStatus() + ); + } + + protected _updatePageEl(el) { + el.route = this.route; + el.hass = this.hass; + el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide; + el.cloudStatus = this._cloudStatus; + } + + private async _updateCloudStatus() { + this._cloudStatus = await fetchCloudStatus(this.hass); + + if (this._cloudStatus.cloud === "connecting") { + setTimeout(() => this._updateCloudStatus(), 5000); + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-panel-config": HaPanelConfig; + } +} diff --git a/src/panels/config/config-entries/ha-ce-entities-card.js b/src/panels/config/integrations/ha-ce-entities-card.js similarity index 100% rename from src/panels/config/config-entries/ha-ce-entities-card.js rename to src/panels/config/integrations/ha-ce-entities-card.js diff --git a/src/panels/config/config-entries/ha-config-entries-dashboard.js b/src/panels/config/integrations/ha-config-entries-dashboard.js similarity index 100% rename from src/panels/config/config-entries/ha-config-entries-dashboard.js rename to src/panels/config/integrations/ha-config-entries-dashboard.js diff --git a/src/panels/config/config-entries/ha-config-entry-page.js b/src/panels/config/integrations/ha-config-entry-page.js similarity index 100% rename from src/panels/config/config-entries/ha-config-entry-page.js rename to src/panels/config/integrations/ha-config-entry-page.js diff --git a/src/panels/config/config-entries/ha-config-entries.js b/src/panels/config/integrations/ha-config-integrations.js similarity index 96% rename from src/panels/config/config-entries/ha-config-entries.js rename to src/panels/config/integrations/ha-config-integrations.js index ec69a18267..035ff0dbca 100644 --- a/src/panels/config/config-entries/ha-config-entries.js +++ b/src/panels/config/integrations/ha-config-integrations.js @@ -10,7 +10,7 @@ import NavigateMixin from "../../../mixins/navigate-mixin"; import compare from "../../../common/string/compare"; import { fetchAreaRegistry } from "../../../data/area_registry"; -class HaConfigEntries extends NavigateMixin(PolymerElement) { +class HaConfigIntegrations extends NavigateMixin(PolymerElement) { static get template() { return html`