Config panel routing (#2928)

* Config panel routing

* Abstract routing

* Convert partial-panel-resolver

* decorator

* Remove showMenu

* Tweaks to make it faster

* Rename update method

* Less aggressive loading
This commit is contained in:
Paulus Schoutsen 2019-03-14 15:16:46 -07:00 committed by GitHub
parent 8785b03fd8
commit 2349e2f251
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 495 additions and 481 deletions

View File

@ -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);
};

View File

@ -47,6 +47,9 @@ export interface CloudWebhook {
managed?: boolean; managed?: boolean;
} }
export const fetchCloudStatus = (hass: HomeAssistant) =>
hass.callWS<CloudStatus>({ type: "cloud/status" });
export const createCloudhook = (hass: HomeAssistant, webhookId: string) => export const createCloudhook = (hass: HomeAssistant, webhookId: string) =>
hass.callWS<CloudWebhook>({ hass.callWS<CloudWebhook>({
type: "cloud/cloudhook/create", type: "cloud/cloudhook/create",

View File

@ -7,16 +7,28 @@ import {
CSSResultArray, CSSResultArray,
css, css,
customElement, customElement,
property,
} from "lit-element"; } from "lit-element";
import "../components/ha-menu-button"; import "../components/ha-menu-button";
import "../components/ha-paper-icon-button-arrow-prev";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
@customElement("hass-loading-screen") @customElement("hass-loading-screen")
class HassLoadingScreen extends LitElement { class HassLoadingScreen extends LitElement {
@property() public isRoot? = false;
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
return html` return html`
<app-toolbar> <app-toolbar>
<ha-menu-button></ha-menu-button> ${this.isRoot
? html`
<ha-menu-button></ha-menu-button>
`
: html`
<ha-paper-icon-button-arrow-prev
@click=${this._handleBack}
></ha-paper-icon-button-arrow-prev>
`}
</app-toolbar> </app-toolbar>
<div class="content"> <div class="content">
<paper-spinner-lite active></paper-spinner-lite> <paper-spinner-lite active></paper-spinner-lite>
@ -24,6 +36,10 @@ class HassLoadingScreen extends LitElement {
`; `;
} }
private _handleBack() {
history.back();
}
static get styles(): CSSResultArray { static get styles(): CSSResultArray {
return [ return [
haStyle, haStyle,

View File

@ -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<unknown>;
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),
};
}
}

View File

@ -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 { HomeAssistant } from "../types";
import "./hass-error-screen"; import { HassRouterPage, RouterOptions } from "./hass-router-page";
import { HomeAssistant, Panel, PanelElement, Route } from "../types";
// Cache of panel loading promises. @customElement("partial-panel-resolver")
const LOADED: { [panel: string]: Promise<void> } = {}; class PartialPanelResolver extends HassRouterPage {
protected static routerOptions: RouterOptions = {
// Which panel elements we will cache. isRoot: true,
// Maybe we can cache them all eventually, but not sure yet about showLoading: true,
// unknown side effects (like history taking a lot of memory, reset needed) routes: {
const CACHED_EL = ["lovelace", "states"]; calendar: {
tag: "ha-panel-calendar",
function ensureLoaded(panel): Promise<void> | null { load: () =>
if (panel in LOADED) { import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
return LOADED[panel]; },
} config: {
tag: "ha-panel-config",
let imported; load: () =>
// Name each panel we support here, that way Webpack knows about it. import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
switch (panel) { },
case "config": custom: {
imported = import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"); tag: "ha-panel-custom",
break; load: () =>
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
case "custom": },
imported = import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"); "dev-event": {
break; tag: "ha-panel-dev-event",
load: () =>
case "dev-event": import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
imported = import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"); },
break; "dev-info": {
tag: "ha-panel-dev-info",
case "dev-info": load: () =>
imported = import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"); import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
break; },
"dev-mqtt": {
case "dev-mqtt": tag: "ha-panel-dev-mqtt",
imported = import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"); load: () =>
break; import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
},
case "dev-service": "dev-service": {
imported = import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"); tag: "ha-panel-dev-service",
break; load: () =>
import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
case "dev-state": },
imported = import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"); "dev-state": {
break; tag: "ha-panel-dev-state",
load: () =>
case "dev-template": import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
imported = import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"); },
break; "dev-template": {
tag: "ha-panel-dev-template",
case "lovelace": load: () =>
imported = import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"); import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
break; },
lovelace: {
case "states": cache: true,
imported = import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"); tag: "ha-panel-lovelace",
break; load: () =>
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
case "history": },
imported = import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"); states: {
break; cache: true,
tag: "ha-panel-states",
case "iframe": load: () =>
imported = import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"); import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
break; },
history: {
case "kiosk": tag: "ha-panel-history",
imported = import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"); load: () =>
break; import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
},
case "logbook": iframe: {
imported = import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"); tag: "ha-panel-iframe",
break; load: () =>
import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
case "mailbox": },
imported = import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"); kiosk: {
break; tag: "ha-panel-kiosk",
load: () =>
case "map": import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
imported = import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"); },
break; logbook: {
tag: "ha-panel-logbook",
case "profile": load: () =>
imported = import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"); import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
break; },
mailbox: {
case "shopping-list": tag: "ha-panel-mailbox",
imported = import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"); load: () =>
break; import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
},
case "calendar": map: {
imported = import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"); tag: "ha-panel-map",
break; load: () =>
import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
default: },
imported = null; profile: {
} tag: "ha-panel-profile",
load: () =>
if (imported != null) { import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
LOADED[panel] = imported; },
} "shopping-list": {
tag: "ha-panel-shopping-list",
return imported; load: () =>
} import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
},
class PartialPanelResolver extends LitElement { },
};
@property() public hass?: HomeAssistant; @property() public hass?: HomeAssistant;
@property() public narrow?: boolean; @property() public narrow?: boolean;
@property() public showMenu?: boolean;
@property() public route?: Route | null;
@property() private _routeTail?: Route | null; protected _updatePageEl(el) {
@property() private _panelEl?: PanelElement; const hass = this.hass!;
@property() private _error?: boolean;
private _panel?: Panel;
private _cache: { [name: string]: PanelElement } = {};
protected render() {
if (this._error) {
return html`
<hass-error-screen
error="Error while loading this panel."
.narrow=${this.narrow}
.showMenu=${this.showMenu}
></hass-error-screen>
`;
}
if (!this._panelEl) {
return html`
<hass-loading-screen
.narrow=${this.narrow}
.showMenu=${this.showMenu}
></hass-loading-screen>
`;
}
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;
}
if ("setProperties" in el) { if ("setProperties" in el) {
// As long as we have Polymer panels // As long as we have Polymer panels
(el as any).setProperties({ (el as PolymerElement).setProperties({
hass: this.hass, hass: this.hass,
narrow: this.narrow, narrow: this.narrow,
showMenu: this.showMenu, route: this.routeTail,
route: this._routeTail, panel: hass.panels[hass.panelUrl],
panel: this._panel,
}); });
} else { } else {
el.hass = this.hass; el.hass = hass;
el.narrow = this.narrow; el.narrow = this.narrow;
el.showMenu = this.showMenu; el.route = this.routeTail;
el.route = this._routeTail; el.panel = hass.panels[hass.panelUrl];
el.panel = this._panel;
} }
} }
} }
customElements.define("partial-panel-resolver", PartialPanelResolver); declare global {
interface HTMLElementTagNameMap {
"partial-panel-resolver": PartialPanelResolver;
}
}

View File

@ -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`
<app-route
route="[[route]]"
pattern="/:page"
data="{{_routeData}}"
></app-route>
<iron-media-query query="(min-width: 1040px)" query-matches="{{wide}}">
</iron-media-query>
<iron-media-query
query="(min-width: 1296px)"
query-matches="{{wideSidebar}}"
>
</iron-media-query>
<template
is="dom-if"
if='[[_equals(_routeData.page, "area_registry")]]'
restamp
>
<ha-config-area-registry
page-name="area_registry"
route="[[route]]"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-area-registry>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "core")]]' restamp>
<ha-config-core
page-name="core"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-core>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "cloud")]]' restamp>
<ha-config-cloud
page-name="cloud"
route="[[route]]"
hass="[[hass]]"
is-wide="[[isWide]]"
cloud-status="[[_cloudStatus]]"
></ha-config-cloud>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "dashboard")]]'>
<ha-config-dashboard
page-name="dashboard"
hass="[[hass]]"
is-wide="[[isWide]]"
cloud-status="[[_cloudStatus]]"
narrow="[[narrow]]"
show-menu="[[showMenu]]"
></ha-config-dashboard>
</template>
<template
is="dom-if"
if='[[_equals(_routeData.page, "automation")]]'
restamp
>
<ha-config-automation
page-name="automation"
route="[[route]]"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-automation>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "script")]]' restamp>
<ha-config-script
page-name="script"
route="[[route]]"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-script>
</template>
<template
is="dom-if"
if='[[_equals(_routeData.page, "entity_registry")]]'
restamp
>
<ha-config-entity-registry
page-name="entity_registry"
route="[[route]]"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-entity-registry>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "zha")]]' restamp>
<ha-config-zha
page-name="zha"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-zha>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "zwave")]]' restamp>
<ha-config-zwave
page-name="zwave"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-zwave>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "person")]]' restamp>
<ha-config-person
page-name="person"
route="[[route]]"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-person>
</template>
<template
is="dom-if"
if='[[_equals(_routeData.page, "customize")]]'
restamp
>
<ha-config-customize
page-name="customize"
hass="[[hass]]"
is-wide="[[isWide]]"
></ha-config-customize>
</template>
<template
is="dom-if"
if='[[_equals(_routeData.page, "integrations")]]'
restamp
>
<ha-config-entries
route="[[route]]"
page-name="integrations"
hass="[[hass]]"
is-wide="[[isWide]]"
narrow="[[narrow]]"
></ha-config-entries>
</template>
<template is="dom-if" if='[[_equals(_routeData.page, "users")]]' restamp>
<ha-config-users
page-name="users"
route="[[route]]"
hass="[[hass]]"
></ha-config-users>
</template>
`;
}
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);

View File

@ -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;
}
}

View File

@ -10,7 +10,7 @@ import NavigateMixin from "../../../mixins/navigate-mixin";
import compare from "../../../common/string/compare"; import compare from "../../../common/string/compare";
import { fetchAreaRegistry } from "../../../data/area_registry"; import { fetchAreaRegistry } from "../../../data/area_registry";
class HaConfigEntries extends NavigateMixin(PolymerElement) { class HaConfigIntegrations extends NavigateMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<app-route <app-route
@ -158,4 +158,4 @@ class HaConfigEntries extends NavigateMixin(PolymerElement) {
} }
} }
customElements.define("ha-config-entries", HaConfigEntries); customElements.define("ha-config-integrations", HaConfigIntegrations);

View File

@ -18,6 +18,7 @@ export const haStyle = css`
} }
app-toolbar ha-menu-button + [main-title], app-toolbar ha-menu-button + [main-title],
app-toolbar ha-paper-icon-button-arrow-prev + [main-title],
app-toolbar paper-icon-button + [main-title] { app-toolbar paper-icon-button + [main-title] {
margin-left: 24px; margin-left: 24px;
} }