Refactor dev-info page (#2624)

* Refactor dev-info page

* What

* Fix custom UI check

* Apply suggestions from code review

Co-Authored-By: balloob <paulus@home-assistant.io>

* Apply suggestions from code review

Co-Authored-By: balloob <paulus@home-assistant.io>

* Address comments

* TSC

* Apply suggestions from code review

Co-Authored-By: balloob <paulus@home-assistant.io>

* TSC
This commit is contained in:
Paulus Schoutsen 2019-01-29 15:34:49 -08:00 committed by GitHub
parent 73b500db64
commit c964ea30e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 574 additions and 476 deletions

4
src/data/error_log.ts Normal file
View File

@ -0,0 +1,4 @@
import { HomeAssistant } from "../types";
export const fetchErrorLog = (hass: HomeAssistant) =>
hass.callApi<string>("GET", "error_log");

13
src/data/system_log.ts Normal file
View File

@ -0,0 +1,13 @@
import { HomeAssistant } from "../types";
export interface LoggedError {
message: string;
level: string;
source: string;
// unix timestamp in seconds
timestamp: number;
exception: string;
}
export const fetchSystemLog = (hass: HomeAssistant) =>
hass.callApi<LoggedError[]>("GET", "error/all");

View File

@ -0,0 +1,77 @@
import {
LitElement,
html,
css,
PropertyDeclarations,
CSSResult,
TemplateResult,
} from "lit-element";
import "@polymer/paper-dialog/paper-dialog";
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
import { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
import { PolymerChangedEvent } from "../../polymer-types";
import { haStyleDialog } from "../../resources/ha-style";
class DialogSystemLogDetail extends LitElement {
private _params?: SystemLogDetailDialogParams;
static get properties(): PropertyDeclarations {
return {
_params: {},
};
}
public async showDialog(params: SystemLogDetailDialogParams): Promise<void> {
this._params = params;
await this.updateComplete;
}
protected render(): TemplateResult | void {
if (!this._params) {
return html``;
}
const item = this._params.item;
return html`
<paper-dialog
with-backdrop
opened
@opened-changed="${this._openedChanged}"
>
<h2>Log Details (${item.level})</h2>
<paper-dialog-scrollable>
<p>${new Date(item.timestamp * 1000)}</p>
${item.message
? html`
<pre>${item.message}</pre>
`
: html``}
${item.exception
? html`
<pre>${item.exception}</pre>
`
: html``}
</paper-dialog-scrollable>
</paper-dialog>
`;
}
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
if (!(ev.detail as any).value) {
this._params = undefined;
}
}
static get styles(): CSSResult[] {
return [haStyleDialog, css``];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-system-log-detail": DialogSystemLogDetail;
}
}
customElements.define("dialog-system-log-detail", DialogSystemLogDetail);

View File

@ -0,0 +1,73 @@
import {
LitElement,
html,
CSSResult,
css,
PropertyDeclarations,
TemplateResult,
} from "lit-element";
import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-button/paper-button";
import { HomeAssistant } from "../../types";
import { fetchErrorLog } from "../../data/error_log";
class ErrorLogCard extends LitElement {
public hass?: HomeAssistant;
private _errorLog?: string;
static get properties(): PropertyDeclarations {
return {
hass: {},
_errorLog: {},
};
}
protected render(): TemplateResult | void {
return html`
<p class="error-log-intro">
${this._errorLog
? html`
<paper-icon-button
icon="hass:refresh"
@click=${this._refreshErrorLog}
></paper-icon-button>
`
: html`
<paper-button raised @click=${this._refreshErrorLog}>
Load Full Home Assistant Log
</paper-button>
`}
</p>
<div class="error-log">${this._errorLog}</div>
`;
}
static get styles(): CSSResult {
return css`
.error-log-intro {
text-align: center;
margin: 16px;
}
paper-icon-button {
float: right;
}
.error-log {
@apply --paper-font-code)
clear: both;
white-space: pre-wrap;
margin: 16px;
}
`;
}
private async _refreshErrorLog(): Promise<void> {
this._errorLog = "Loading error log…";
const log = await fetchErrorLog(this.hass!);
this._errorLog = log || "No errors have been reported.";
}
}
customElements.define("error-log-card", ErrorLogCard);

View File

@ -1,59 +0,0 @@
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
import "@polymer/paper-dialog/paper-dialog";
import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../resources/ha-style";
import EventsMixin from "../../mixins/events-mixin";
/*
* @appliesMixin EventsMixin
*/
class HaLoadedComponents extends EventsMixin(PolymerElement) {
static get template() {
return html`
<style include="ha-style-dialog">
paper-dialog {
max-width: 500px;
}
</style>
<paper-dialog id="dialog" with-backdrop="" opened="{{_opened}}">
<h2>Loaded Components</h2>
<paper-dialog-scrollable id="scrollable">
<p>The following components are currently loaded:</p>
<ul>
<template is="dom-repeat" items="[[_components]]">
<li>[[item]]</li>
</template>
</ul>
</paper-dialog-scrollable>
</paper-dialog>
`;
}
static get properties() {
return {
_hass: Object,
_components: Array,
_opened: {
type: Boolean,
value: false,
},
};
}
ready() {
super.ready();
}
showDialog({ hass }) {
this.hass = hass;
this._opened = true;
this._components = this.hass.config.components.sort();
setTimeout(() => this.$.dialog.center(), 0);
}
}
customElements.define("ha-loaded-components", HaLoadedComponents);

View File

@ -1,416 +0,0 @@
import "@polymer/app-layout/app-header-layout/app-header-layout";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-card/paper-card";
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
import "@polymer/paper-dialog/paper-dialog";
import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-item/paper-item";
import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../components/buttons/ha-call-service-button";
import "../../components/ha-menu-button";
import "../../resources/ha-style";
import formatDateTime from "../../common/datetime/format_date_time";
import formatTime from "../../common/datetime/format_time";
import EventsMixin from "../../mixins/events-mixin";
import LocalizeMixin from "../../mixins/localize-mixin";
const OPT_IN_PANEL = "states";
let registeredDialog = false;
class HaPanelDevInfo extends EventsMixin(LocalizeMixin(PolymerElement)) {
static get template() {
return html`
<style include="iron-positioning ha-style">
:host {
-ms-user-select: initial;
-webkit-user-select: initial;
-moz-user-select: initial;
}
.content {
padding: 16px 0px 16px 0;
direction: ltr;
}
.about {
text-align: center;
line-height: 2em;
}
.version {
@apply --paper-font-headline;
}
.develop {
@apply --paper-font-subhead;
}
.about a {
color: var(--dark-primary-color);
}
.error-log-intro {
text-align: center;
margin: 16px;
}
paper-icon-button {
float: right;
}
.error-log {
@apply --paper-font-code)
clear: both;
white-space: pre-wrap;
margin: 16px;
}
.system-log-intro {
margin: 16px;
border-top: 1px solid var(--light-primary-color);
padding-top: 16px;
}
paper-card {
display: block;
padding-top: 16px;
}
paper-item {
cursor: pointer;
}
.header {
@apply --paper-font-title;
}
paper-dialog {
border-radius: 2px;
direction: ltr;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
paper-dialog {
margin: 0;
width: 100%;
max-height: calc(100% - 64px);
position: fixed !important;
bottom: 0px;
left: 0px;
right: 0px;
overflow: scroll;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
}
.loading-container {
@apply --layout-vertical;
@apply --layout-center-center;
height: 100px;
}
</style>
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
<div main-title>About</div>
</app-toolbar>
</app-header>
<div class='content'>
<div class='about'>
<p class='version'>
<a href='https://www.home-assistant.io'><img src="/static/icons/favicon-192x192.png" height="192" /></a><br />
Home Assistant<br />
[[hass.config.version]]
</p>
<p>
Path to configuration.yaml: [[hass.config.config_dir]]
<br><a href="#" on-click="_showComponents">[[loadedComponents.length]] Loaded Components</a>
</p>
<p class='develop'>
<a href='https://www.home-assistant.io/developers/credits/' target='_blank'>
Developed by a bunch of awesome people.
</a>
</p>
<p>
Published under the Apache 2.0 license<br />
Source:
<a href='https://github.com/home-assistant/home-assistant' target='_blank'>server</a> &mdash;
<a href='https://github.com/home-assistant/home-assistant-polymer' target='_blank'>frontend-ui</a>
</p>
<p>
Built using
<a href='https://www.python.org'>Python 3</a>,
<a href='https://www.polymer-project.org' target='_blank'>Polymer</a>,
Icons by <a href='https://www.google.com/design/icons/' target='_blank'>Google</a> and <a href='https://MaterialDesignIcons.com' target='_blank'>MaterialDesignIcons.com</a>.
</p>
<p>
Frontend JavaScript version: [[jsVersion]]
<template is='dom-if' if='[[customUiList.length]]'>
<div>
Custom UIs:
<template is='dom-repeat' items='[[customUiList]]'>
<div>
<a href='[[item.url]]' target='_blank'>[[item.name]]</a>: [[item.version]]
</div>
</template>
</div>
</template>
</p>
<p>
<a href="[[_nonDefaultLink()]]">[[_nonDefaultLinkText()]]</a>
<div id="love" style="cursor:pointer;" on-click="_toggleDefaultPage">[[_defaultPageText()]]</div
</p>
</div>
<div class="system-log-intro">
<paper-card>
<template is='dom-if' if='[[updating]]'>
<div class='loading-container'>
<paper-spinner active></paper-spinner>
</div>
</template>
<template is='dom-if' if='[[!updating]]'>
<template is='dom-if' if='[[!items.length]]'>
<div class='card-content'>There are no new issues!</div>
</template>
<template is='dom-repeat' items='[[items]]'>
<paper-item on-click='openLog'>
<paper-item-body two-line>
<div class="row">
[[item.message]]
</div>
<div secondary>
[[formatTime(item.timestamp)]] [[item.source]] ([[item.level]])
</div>
</paper-item-body>
</paper-item>
</template>
<div class='card-actions'>
<ha-call-service-button
hass='[[hass]]'
domain='system_log'
service='clear'
>Clear</ha-call-service-button>
<ha-progress-button
on-click='_fetchData'
>Refresh</ha-progress-button>
</div>
</template>
</paper-card>
</div>
<p class='error-log-intro'>
<template is='dom-if' if='[[!errorLog]]'>
<paper-button raised on-click='refreshErrorLog'>Load Full Home Assistant Log</paper-button>
</template>
<template is='dom-if' if='[[errorLog]]'>
<paper-icon-button icon='hass:refresh' on-click='refreshErrorLog'></paper-icon-button>
</template>
</p>
<div class='error-log'>[[errorLog]]</div>
</div>
</app-header-layout>
<paper-dialog with-backdrop id="showlog">
<h2>Log Details ([[selectedItem.level]])</h2>
<paper-dialog-scrollable id="scrollable">
<p>[[fullTimeStamp(selectedItem.timestamp)]]</p>
<template is='dom-if' if='[[selectedItem.message]]'>
<pre>[[selectedItem.message]]</pre>
</template>
<template is='dom-if' if='[[selectedItem.exception]]'>
<pre>[[selectedItem.exception]]</pre>
</template>
</paper-dialog-scrollable>
</paper-dialog>
`;
}
static get properties() {
return {
hass: Object,
narrow: {
type: Boolean,
value: false,
},
showMenu: {
type: Boolean,
value: false,
},
errorLog: {
type: String,
value: "",
},
updating: {
type: Boolean,
value: true,
},
items: {
type: Array,
value: [],
},
selectedItem: Object,
jsVersion: {
type: String,
value: __BUILD__,
},
customUiList: {
type: Array,
value: window.CUSTOM_UI_LIST || [],
},
loadedComponents: {
type: Array,
value: [],
},
};
}
ready() {
super.ready();
this.addEventListener("hass-service-called", (ev) =>
this.serviceCalled(ev)
);
// Fix for overlay showing on top of dialog.
this.$.showlog.addEventListener("iron-overlay-opened", (ev) => {
if (ev.target.withBackdrop) {
ev.target.parentNode.insertBefore(ev.target.backdropElement, ev.target);
}
});
}
serviceCalled(ev) {
// Check if this is for us
if (ev.detail.success && ev.detail.domain === "system_log") {
// Do the right thing depending on service
if (ev.detail.service === "clear") {
this.items = [];
}
}
}
connectedCallback() {
super.connectedCallback();
this.$.scrollable.dialogElement = this.$.showlog;
this._fetchData();
this.loadedComponents = this.hass.config.components;
if (!registeredDialog) {
registeredDialog = true;
this.fire("register-dialog", {
dialogShowEvent: "show-loaded-components",
dialogTag: "ha-loaded-components",
dialogImport: () =>
import(/* webpackChunkName: "ha-loaded-components" */ "./ha-loaded-components"),
});
}
if (!window.CUSTOM_UI_LIST) {
// Give custom UI an opportunity to load.
setTimeout(() => {
this.customUiList = window.CUSTOM_UI_LIST || [];
}, 1000);
} else {
this.customUiList = window.CUSTOM_UI_LIST;
}
}
refreshErrorLog(ev) {
if (ev) ev.preventDefault();
this.errorLog = "Loading error log…";
this.hass.callApi("GET", "error_log").then((log) => {
this.errorLog = log || "No errors have been reported.";
});
}
fullTimeStamp(date) {
return new Date(date * 1000);
}
formatTime(date) {
const today = new Date().setHours(0, 0, 0, 0);
const dateTime = new Date(date * 1000);
const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0);
return dateTimeDay < today
? formatDateTime(dateTime, this.hass.language)
: formatTime(dateTime, this.hass.language);
}
openLog(event) {
this.selectedItem = event.model.item;
this.$.showlog.open();
}
_fetchData() {
this.updating = true;
this.hass.callApi("get", "error/all").then((items) => {
this.items = items;
this.updating = false;
});
}
_nonDefaultLink() {
if (
localStorage.defaultPage === OPT_IN_PANEL &&
OPT_IN_PANEL === "states"
) {
return "/lovelace";
}
return "/states";
}
_nonDefaultLinkText() {
if (
localStorage.defaultPage === OPT_IN_PANEL &&
OPT_IN_PANEL === "states"
) {
return "Go to the Lovelace UI";
}
return "Go to the states UI";
}
_defaultPageText() {
return `>> ${
localStorage.defaultPage === OPT_IN_PANEL ? "Remove" : "Set"
} ${OPT_IN_PANEL} as default page on this device <<`;
}
_toggleDefaultPage() {
if (localStorage.defaultPage === OPT_IN_PANEL) {
delete localStorage.defaultPage;
} else {
localStorage.defaultPage = OPT_IN_PANEL;
}
this.$.love.innerText = this._defaultPageText();
}
_showComponents() {
this.fire("show-loaded-components", {
hass: this.hass,
});
}
}
customElements.define("ha-panel-dev-info", HaPanelDevInfo);

View File

@ -0,0 +1,215 @@
import {
LitElement,
html,
PropertyDeclarations,
CSSResult,
css,
TemplateResult,
} from "lit-element";
import "@polymer/app-layout/app-header-layout/app-header-layout";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "../../components/ha-menu-button";
import { HomeAssistant } from "../../types";
import { haStyle } from "../../resources/ha-style";
import "./system-log-card";
import "./error-log-card";
const JS_VERSION = __BUILD__;
const OPT_IN_PANEL = "states";
class HaPanelDevInfo extends LitElement {
public hass?: HomeAssistant;
public narrow?: boolean;
public showMenu?: boolean;
static get properties(): PropertyDeclarations {
return {
hass: {},
narrow: {},
showMenu: {},
};
}
protected render(): TemplateResult | void {
const hass = this.hass;
if (!hass) {
return html``;
}
const customUiList: Array<{ name: string; url: string; version: string }> =
(window as any).CUSTOM_UI_LIST || [];
const nonDefaultLink =
localStorage.defaultPage === OPT_IN_PANEL && OPT_IN_PANEL === "states"
? "/lovelace"
: "/states";
const nonDefaultLinkText =
localStorage.defaultPage === OPT_IN_PANEL && OPT_IN_PANEL === "states"
? "Go to the Lovelace UI"
: "Go to the states UI";
const defaultPageText = `${
localStorage.defaultPage === OPT_IN_PANEL ? "Remove" : "Set"
} ${OPT_IN_PANEL} as default page on this device`;
return html`
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button
.narrow="${this.narrow}"
showMenu="${this.showMenu}"
></ha-menu-button>
<div main-title>About</div>
</app-toolbar>
</app-header>
<div class="content">
<div class="about">
<p class="version">
<a href="https://www.home-assistant.io"
><img src="/static/icons/favicon-192x192.png" height="192"/></a
><br />
Home Assistant<br />
${hass.config.version}
</p>
<p>
Path to configuration.yaml: ${hass.config.config_dir}
</p>
<p class="develop">
<a
href="https://www.home-assistant.io/developers/credits/"
target="_blank"
>
Developed by a bunch of awesome people.
</a>
</p>
<p>
Published under the Apache 2.0 license<br />
Source:
<a
href="https://github.com/home-assistant/home-assistant"
target="_blank"
>server</a
>
&mdash;
<a
href="https://github.com/home-assistant/home-assistant-polymer"
target="_blank"
>frontend-ui</a
>
</p>
<p>
Built using
<a href="https://www.python.org">Python 3</a>,
<a href="https://www.polymer-project.org" target="_blank"
>Polymer</a
>, Icons by
<a href="https://www.google.com/design/icons/" target="_blank"
>Google</a
>
and
<a href="https://MaterialDesignIcons.com" target="_blank"
>MaterialDesignIcons.com</a
>.
</p>
<p>
Frontend JavaScript version: ${JS_VERSION}
${customUiList.length > 0
? html`
<div>
Custom UIs:
${customUiList.map(
(item) => html`
<div>
<a href="${item.url}" target="_blank">
${item.name}</a
>: ${item.version}
</div>
`
)}
</div>
`
: ""}
</p>
<p>
<a href="${nonDefaultLink}">${nonDefaultLinkText}</a><br />
<paper-button @click="${this._toggleDefaultPage}" raised>
${defaultPageText}
</paper-button>
</p>
</div>
<system-log-card .hass=${this.hass}></system-log-card>
<error-log-card .hass=${this.hass}></error-log-card>
</div>
</app-header-layout>
`;
}
protected firstUpdated(changedProps): void {
super.firstUpdated(changedProps);
// Legacy custom UI can be slow to register, give them time.
const customUI = ((window as any).CUSTOM_UI_LIST || []).length;
setTimeout(() => {
if (((window as any).CUSTOM_UI_LIST || []).length !== customUI.length) {
this.requestUpdate();
}
}, 1000);
}
protected _toggleDefaultPage(): void {
if (localStorage.defaultPage === OPT_IN_PANEL) {
delete localStorage.defaultPage;
} else {
localStorage.defaultPage = OPT_IN_PANEL;
}
this.requestUpdate();
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
:host {
-ms-user-select: initial;
-webkit-user-select: initial;
-moz-user-select: initial;
}
.content {
padding: 16px 0px 16px 0;
direction: ltr;
}
.about {
text-align: center;
line-height: 2em;
}
.version {
@apply --paper-font-headline;
}
.develop {
@apply --paper-font-subhead;
}
.about a {
color: var(--dark-primary-color);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-panel-dev-info": HaPanelDevInfo;
}
}
customElements.define("ha-panel-dev-info", HaPanelDevInfo);

View File

@ -0,0 +1,36 @@
import { fireEvent } from "../../common/dom/fire_event";
import { LoggedError } from "../../data/system_log";
declare global {
// for fire event
interface HASSDomEvents {
"show-dialog-system-log-detail": SystemLogDetailDialogParams;
}
}
let registeredDialog = false;
const dialogShowEvent = "show-dialog-system-log-detail";
const dialogTag = "dialog-system-log-detail";
export interface SystemLogDetailDialogParams {
item: LoggedError;
}
const registerDialog = (element: HTMLElement) =>
fireEvent(element, "register-dialog", {
dialogShowEvent,
dialogTag,
dialogImport: () =>
import(/* webpackChunkName: "system-log-detail-dialog" */ "./dialog-system-log-detail"),
});
export const showSystemLogDetailDialog = (
element: HTMLElement,
systemLogDetailParams: SystemLogDetailDialogParams
): void => {
if (!registeredDialog) {
registeredDialog = true;
registerDialog(element);
}
fireEvent(element, dialogShowEvent, systemLogDetailParams);
};

View File

@ -0,0 +1,148 @@
import {
LitElement,
html,
CSSResult,
css,
PropertyDeclarations,
TemplateResult,
} from "lit-element";
import "@polymer/paper-card/paper-card";
import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-spinner/paper-spinner";
import "../../components/buttons/ha-call-service-button";
import "../../components/buttons/ha-progress-button";
import { HomeAssistant } from "../../types";
import { LoggedError, fetchSystemLog } from "../../data/system_log";
import formatDateTime from "../../common/datetime/format_date_time";
import formatTime from "../../common/datetime/format_time";
import { showSystemLogDetailDialog } from "./show-dialog-system-log-detail";
const formatLogTime = (date, language: string) => {
const today = new Date().setHours(0, 0, 0, 0);
const dateTime = new Date(date * 1000);
const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0);
return dateTimeDay < today
? formatDateTime(dateTime, language)
: formatTime(dateTime, language);
};
class SystemLogCard extends LitElement {
public hass?: HomeAssistant;
private _items?: LoggedError[];
static get properties(): PropertyDeclarations {
return {
hass: {},
_items: {},
};
}
protected render(): TemplateResult | void {
return html`
<div class="system-log-intro">
<paper-card>
${this._items === undefined
? html`
<div class="loading-container">
<paper-spinner active></paper-spinner>
</div>
`
: html`
${this._items.length === 0
? html`
<div class="card-content">There are no new issues!</div>
`
: this._items.map(
(item) => html`
<paper-item @click=${this._openLog} .logItem=${item}>
<paper-item-body two-line>
<div class="row">
${item.message}
</div>
<div secondary>
${formatLogTime(
item.timestamp,
this.hass!.language
)}
${item.source} (${item.level})
</div>
</paper-item-body>
</paper-item>
`
)}
<div class="card-actions">
<ha-call-service-button
.hass=${this.hass}
domain="system_log"
service="clear"
>Clear</ha-call-service-button
>
<ha-progress-button @click=${this._fetchData}
>Refresh</ha-progress-button
>
</div>
`}
</paper-card>
</div>
`;
}
protected firstUpdated(changedProps): void {
super.firstUpdated(changedProps);
this._fetchData();
this.addEventListener("hass-service-called", (ev) =>
this.serviceCalled(ev)
);
}
protected serviceCalled(ev): void {
// Check if this is for us
if (ev.detail.success && ev.detail.domain === "system_log") {
// Do the right thing depending on service
if (ev.detail.service === "clear") {
this._items = [];
}
}
}
private async _fetchData(): Promise<void> {
this._items = undefined;
this._items = await fetchSystemLog(this.hass!);
}
private _openLog(ev: Event): void {
const item = (ev.currentTarget as any).logItem;
showSystemLogDetailDialog(this, { item });
}
static get styles(): CSSResult {
return css`
paper-card {
display: block;
padding-top: 16px;
}
paper-item {
cursor: pointer;
}
.system-log-intro {
margin: 16px;
border-top: 1px solid var(--light-primary-color);
padding-top: 16px;
}
.loading-container {
@apply --layout-vertical;
@apply --layout-center-center;
height: 100px;
}
`;
}
}
customElements.define("system-log-card", SystemLogCard);

View File

@ -11,6 +11,7 @@ import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
import { moveCard } from "../config-util"; import { moveCard } from "../config-util";
import { MoveCardViewDialogParams } from "./show-move-card-view-dialog"; import { MoveCardViewDialogParams } from "./show-move-card-view-dialog";
import { PolymerChangedEvent } from "../../../../polymer-types";
export class HuiDialogMoveCardView extends LitElement { export class HuiDialogMoveCardView extends LitElement {
private _params?: MoveCardViewDialogParams; private _params?: MoveCardViewDialogParams;
@ -91,7 +92,7 @@ export class HuiDialogMoveCardView extends LitElement {
this._dialog.close(); this._dialog.close();
} }
private _openedChanged(ev: MouseEvent) { private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
if (!(ev.detail as any).value) { if (!(ev.detail as any).value) {
this._params = undefined; this._params = undefined;
} }

View File

@ -1,6 +1,12 @@
// Force file to be a module to augment global scope. // Force file to be a module to augment global scope.
export {}; export {};
export interface PolymerChangedEvent<T> extends Event {
detail: {
value: T;
};
}
declare global { declare global {
// for fire event // for fire event
interface HASSDomEvents { interface HASSDomEvents {