mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-10 02:46:38 +00:00
Add developer tools panel (#3313)
This commit is contained in:
parent
618d25ce48
commit
58e6be12af
@ -22,6 +22,7 @@ const BUILT_IN_PANEL_ICONS = [
|
||||
"mailbox", // Mailbox
|
||||
"tooltip-account", // Map
|
||||
"cart", // Shopping List
|
||||
"hammer", // developer-tools
|
||||
];
|
||||
|
||||
// Given an icon name, load the SVG file
|
||||
|
@ -14,7 +14,6 @@ import "@polymer/paper-listbox/paper-listbox";
|
||||
import "./ha-icon";
|
||||
|
||||
import "../components/user/ha-user-badge";
|
||||
import isComponentLoaded from "../common/config/is_component_loaded";
|
||||
import { HomeAssistant, PanelInfo } from "../types";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { DEFAULT_PANEL } from "../common/const";
|
||||
@ -23,47 +22,64 @@ import {
|
||||
ExternalConfig,
|
||||
} from "../external_app/external_config";
|
||||
|
||||
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
|
||||
|
||||
const computeUrl = (urlPath) => `/${urlPath}`;
|
||||
|
||||
const computePanels = (hass: HomeAssistant) => {
|
||||
const SORT_VALUE = {
|
||||
map: 1,
|
||||
logbook: 2,
|
||||
history: 3,
|
||||
"developer-tools": 9,
|
||||
configuration: 10,
|
||||
};
|
||||
|
||||
const panelSorter = (a, b) => {
|
||||
const aBuiltIn = a.component_name in SORT_VALUE;
|
||||
const bBuiltIn = b.component_name in SORT_VALUE;
|
||||
|
||||
if (aBuiltIn && bBuiltIn) {
|
||||
return SORT_VALUE[a.component_name] - SORT_VALUE[b.component_name];
|
||||
}
|
||||
if (aBuiltIn) {
|
||||
return -1;
|
||||
}
|
||||
if (bBuiltIn) {
|
||||
return 1;
|
||||
}
|
||||
// both not built in, sort by title
|
||||
if (a.title! < b.title!) {
|
||||
return -1;
|
||||
}
|
||||
if (a.title! > b.title!) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const computePanels = (hass: HomeAssistant): [PanelInfo[], PanelInfo[]] => {
|
||||
const panels = hass.panels;
|
||||
if (!panels) {
|
||||
return [];
|
||||
return [[], []];
|
||||
}
|
||||
|
||||
const sortValue = {
|
||||
map: 1,
|
||||
logbook: 2,
|
||||
history: 3,
|
||||
};
|
||||
const result: PanelInfo[] = Object.values(panels).filter(
|
||||
(panel) => panel.title
|
||||
);
|
||||
const beforeSpacer: PanelInfo[] = [];
|
||||
const afterSpacer: PanelInfo[] = [];
|
||||
|
||||
result.sort((a, b) => {
|
||||
const aBuiltIn = a.component_name in sortValue;
|
||||
const bBuiltIn = b.component_name in sortValue;
|
||||
|
||||
if (aBuiltIn && bBuiltIn) {
|
||||
return sortValue[a.component_name] - sortValue[b.component_name];
|
||||
Object.values(panels).forEach((panel) => {
|
||||
if (!panel.title) {
|
||||
return;
|
||||
}
|
||||
if (aBuiltIn) {
|
||||
return -1;
|
||||
}
|
||||
if (bBuiltIn) {
|
||||
return 1;
|
||||
}
|
||||
// both not built in, sort by title
|
||||
if (a.title! < b.title!) {
|
||||
return -1;
|
||||
}
|
||||
if (a.title! > b.title!) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
(SHOW_AFTER_SPACER.includes(panel.component_name)
|
||||
? afterSpacer
|
||||
: beforeSpacer
|
||||
).push(panel);
|
||||
});
|
||||
|
||||
return result;
|
||||
beforeSpacer.sort(panelSorter);
|
||||
afterSpacer.sort(panelSorter);
|
||||
|
||||
return [beforeSpacer, afterSpacer];
|
||||
};
|
||||
|
||||
const renderPanel = (hass, panel) => html`
|
||||
@ -100,12 +116,7 @@ class HaSidebar extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const panels = computePanels(hass);
|
||||
const configPanelIdx = panels.findIndex(
|
||||
(panel) => panel.component_name === "config"
|
||||
);
|
||||
const configPanel =
|
||||
configPanelIdx === -1 ? undefined : panels.splice(configPanelIdx, 1)[0];
|
||||
const [beforeSpacer, afterSpacer] = computePanels(hass);
|
||||
|
||||
return html`
|
||||
${this.expanded
|
||||
@ -137,69 +148,11 @@ class HaSidebar extends LitElement {
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
|
||||
${panels.map((panel) => renderPanel(hass, panel))}
|
||||
${beforeSpacer.map((panel) => renderPanel(hass, panel))}
|
||||
|
||||
<div class="spacer" disabled></div>
|
||||
|
||||
${this.expanded && hass.user && hass.user.is_admin
|
||||
? html`
|
||||
<div class="divider" disabled></div>
|
||||
|
||||
<div class="subheader" disabled>
|
||||
${hass.localize("ui.sidebar.developer_tools")}
|
||||
</div>
|
||||
|
||||
<div class="dev-tools" disabled>
|
||||
<a href="/dev-service" tabindex="-1">
|
||||
<paper-icon-button
|
||||
icon="hass:remote"
|
||||
alt="${hass.localize("panel.dev-services")}"
|
||||
title="${hass.localize("panel.dev-services")}"
|
||||
></paper-icon-button>
|
||||
</a>
|
||||
<a href="/dev-state" tabindex="-1">
|
||||
<paper-icon-button
|
||||
icon="hass:code-tags"
|
||||
alt="${hass.localize("panel.dev-states")}"
|
||||
title="${hass.localize("panel.dev-states")}"
|
||||
></paper-icon-button>
|
||||
</a>
|
||||
<a href="/dev-event" tabindex="-1">
|
||||
<paper-icon-button
|
||||
icon="hass:radio-tower"
|
||||
alt="${hass.localize("panel.dev-events")}"
|
||||
title="${hass.localize("panel.dev-events")}"
|
||||
></paper-icon-button>
|
||||
</a>
|
||||
<a href="/dev-template" tabindex="-1">
|
||||
<paper-icon-button
|
||||
icon="hass:file-xml"
|
||||
alt="${hass.localize("panel.dev-templates")}"
|
||||
title="${hass.localize("panel.dev-templates")}"
|
||||
></paper-icon-button>
|
||||
</a>
|
||||
${isComponentLoaded(hass, "mqtt")
|
||||
? html`
|
||||
<a href="/dev-mqtt" tabindex="-1">
|
||||
<paper-icon-button
|
||||
icon="hass:altimeter"
|
||||
alt="${hass.localize("panel.dev-mqtt")}"
|
||||
title="${hass.localize("panel.dev-mqtt")}"
|
||||
></paper-icon-button>
|
||||
</a>
|
||||
`
|
||||
: html``}
|
||||
<a href="/dev-info" tabindex="-1">
|
||||
<paper-icon-button
|
||||
icon="hass:information-outline"
|
||||
alt="${hass.localize("panel.dev-info")}"
|
||||
title="${hass.localize("panel.dev-info")}"
|
||||
></paper-icon-button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="divider" disabled></div>
|
||||
`
|
||||
: ""}
|
||||
${afterSpacer.map((panel) => renderPanel(hass, panel))}
|
||||
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
||||
? html`
|
||||
<a
|
||||
@ -223,7 +176,6 @@ class HaSidebar extends LitElement {
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${configPanel ? renderPanel(hass, configPanel) : ""}
|
||||
${hass.user
|
||||
? html`
|
||||
<a
|
||||
@ -280,7 +232,6 @@ class HaSidebar extends LitElement {
|
||||
return (
|
||||
hass.panels !== oldHass.panels ||
|
||||
hass.panelUrl !== oldHass.panelUrl ||
|
||||
hass.config.components !== oldHass.config.components ||
|
||||
hass.user !== oldHass.user ||
|
||||
hass.localize !== oldHass.localize
|
||||
);
|
||||
@ -458,12 +409,6 @@ class HaSidebar extends LitElement {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background-color: var(--divider-color);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.subheader {
|
||||
color: var(--sidebar-text-color);
|
||||
font-weight: 500;
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
} from "./hass-router-page";
|
||||
import { removeInitSkeleton } from "../util/init-skeleton";
|
||||
|
||||
const CACHE_COMPONENTS = ["lovelace", "states"];
|
||||
const CACHE_COMPONENTS = ["lovelace", "states", "developer-tools"];
|
||||
const COMPONENTS = {
|
||||
calendar: () =>
|
||||
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
|
||||
@ -17,18 +17,8 @@ const COMPONENTS = {
|
||||
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
|
||||
custom: () =>
|
||||
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
|
||||
"dev-event": () =>
|
||||
import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
|
||||
"dev-info": () =>
|
||||
import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
|
||||
"dev-mqtt": () =>
|
||||
import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
|
||||
"dev-service": () =>
|
||||
import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
|
||||
"dev-state": () =>
|
||||
import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
|
||||
"dev-template": () =>
|
||||
import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
|
||||
"developer-tools": () =>
|
||||
import(/* webpackChunkName: "panel-developer-tools" */ "../panels/developer-tools/ha-panel-developer-tools"),
|
||||
lovelace: () =>
|
||||
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
|
||||
states: () =>
|
||||
@ -52,14 +42,17 @@ const COMPONENTS = {
|
||||
};
|
||||
|
||||
const getRoutes = (panels: Panels): RouterOptions => {
|
||||
const routes: { [route: string]: RouteOptions } = {};
|
||||
const routes: RouterOptions["routes"] = {};
|
||||
|
||||
Object.values(panels).forEach((panel) => {
|
||||
routes[panel.url_path] = {
|
||||
load: COMPONENTS[panel.component_name],
|
||||
const data: RouteOptions = {
|
||||
tag: `ha-panel-${panel.component_name}`,
|
||||
cache: CACHE_COMPONENTS.includes(panel.component_name),
|
||||
};
|
||||
if (panel.component_name in COMPONENTS) {
|
||||
data.load = COMPONENTS[panel.component_name];
|
||||
}
|
||||
routes[panel.url_path] = data;
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -1,221 +0,0 @@
|
||||
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/styles";
|
||||
|
||||
import "./system-log-card";
|
||||
import "./error-log-card";
|
||||
import "./system-health-card";
|
||||
|
||||
const JS_VERSION = __BUILD__;
|
||||
const OPT_IN_PANEL = "states";
|
||||
|
||||
class HaPanelDevInfo extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
};
|
||||
}
|
||||
|
||||
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></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" target="_blank">
|
||||
<img
|
||||
src="/static/icons/favicon-192x192.png"
|
||||
height="192"
|
||||
alt="Home Assistant logo"
|
||||
/>
|
||||
</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
|
||||
>
|
||||
—
|
||||
<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 />
|
||||
<mwc-button @click="${this._toggleDefaultPage}" raised>
|
||||
${defaultPageText}
|
||||
</mwc-button>
|
||||
</p>
|
||||
</div>
|
||||
<system-health-card .hass=${this.hass}></system-health-card>
|
||||
<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);
|
||||
}
|
||||
|
||||
system-health-card {
|
||||
display: block;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-panel-dev-info": HaPanelDevInfo;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-dev-info", HaPanelDevInfo);
|
68
src/panels/developer-tools/developer-tools-router.ts
Normal file
68
src/panels/developer-tools/developer-tools-router.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { PolymerElement } from "@polymer/polymer";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
@customElement("developer-tools-router")
|
||||
class DeveloperToolsRouter extends HassRouterPage {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
// defaultPage: "info",
|
||||
beforeRender: (page) => {
|
||||
if (!page || page === "not_found") {
|
||||
// If we can, we are going to restore the last visited page.
|
||||
return this._currentPage ? this._currentPage : "info";
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
cacheAll: true,
|
||||
showLoading: true,
|
||||
routes: {
|
||||
event: {
|
||||
tag: "developer-tools-event",
|
||||
load: () => import("./event/developer-tools-event"),
|
||||
},
|
||||
info: {
|
||||
tag: "developer-tools-info",
|
||||
load: () => import("./info/developer-tools-info"),
|
||||
},
|
||||
mqtt: {
|
||||
tag: "developer-tools-mqtt",
|
||||
load: () => import("./mqtt/developer-tools-mqtt"),
|
||||
},
|
||||
service: {
|
||||
tag: "developer-tools-service",
|
||||
load: () => import("./service/developer-tools-service"),
|
||||
},
|
||||
state: {
|
||||
tag: "developer-tools-state",
|
||||
load: () => import("./state/developer-tools-state"),
|
||||
},
|
||||
template: {
|
||||
tag: "developer-tools-template",
|
||||
load: () => import("./template/developer-tools-template"),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
protected updatePageEl(el) {
|
||||
if ("setProperties" in el) {
|
||||
// As long as we have Polymer pages
|
||||
(el as PolymerElement).setProperties({
|
||||
hass: this.hass,
|
||||
narrow: this.narrow,
|
||||
});
|
||||
} else {
|
||||
el.hass = this.hass;
|
||||
el.narrow = this.narrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"developer-tools-router": DeveloperToolsRouter;
|
||||
}
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
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/iron-flex-layout/iron-flex-layout-classes";
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
@ -8,11 +5,10 @@ import "@polymer/paper-input/paper-textarea";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
import "../../../resources/ha-style";
|
||||
import "./events-list";
|
||||
import "./event-subscribe-card";
|
||||
import { EventsMixin } from "../../mixins/events-mixin";
|
||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
@ -26,12 +22,10 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
@apply --paper-font-body1;
|
||||
padding: 16px;
|
||||
direction: ltr;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ha-form {
|
||||
@ -49,45 +43,34 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout has-scrolling-region>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-menu-button></ha-menu-button>
|
||||
<div main-title>Events</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div class$="[[computeFormClasses(narrow)]]">
|
||||
<div class="flex">
|
||||
<p>Fire an event on the event bus.</p>
|
||||
|
||||
<div class="content">
|
||||
<div class$="[[computeFormClasses(narrow)]]">
|
||||
<div class="flex">
|
||||
<p>Fire an event on the event bus.</p>
|
||||
|
||||
<div class="ha-form">
|
||||
<paper-input
|
||||
label="Event Type"
|
||||
autofocus
|
||||
required
|
||||
value="{{eventType}}"
|
||||
></paper-input>
|
||||
<paper-textarea
|
||||
label="Event Data (JSON, optional)"
|
||||
value="{{eventData}}"
|
||||
></paper-textarea>
|
||||
<mwc-button on-click="fireEvent" raised>Fire Event</mwc-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="header">Available Events</div>
|
||||
<events-list
|
||||
on-event-selected="eventSelected"
|
||||
hass="[[hass]]"
|
||||
></events-list>
|
||||
</div>
|
||||
<div class="ha-form">
|
||||
<paper-input
|
||||
label="Event Type"
|
||||
autofocus
|
||||
required
|
||||
value="{{eventType}}"
|
||||
></paper-input>
|
||||
<paper-textarea
|
||||
label="Event Data (JSON, optional)"
|
||||
value="{{eventData}}"
|
||||
></paper-textarea>
|
||||
<mwc-button on-click="fireEvent" raised>Fire Event</mwc-button>
|
||||
</div>
|
||||
<event-subscribe-card hass="[[hass]]"></event-subscribe-card>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
|
||||
<div>
|
||||
<div class="header">Available Events</div>
|
||||
<events-list
|
||||
on-event-selected="eventSelected"
|
||||
hass="[[hass]]"
|
||||
></events-list>
|
||||
</div>
|
||||
</div>
|
||||
<event-subscribe-card hass="[[hass]]"></event-subscribe-card>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -139,4 +122,4 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-dev-event", HaPanelDevEvent);
|
||||
customElements.define("developer-tools-event", HaPanelDevEvent);
|
@ -10,10 +10,10 @@ import {
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { HassEvent } from "home-assistant-js-websocket";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { PolymerChangedEvent } from "../../polymer-types";
|
||||
import "../../components/ha-card";
|
||||
import format_time from "../../common/datetime/format_time";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||
import "../../../components/ha-card";
|
||||
import format_time from "../../../common/datetime/format_time";
|
||||
|
||||
@customElement("event-subscribe-card")
|
||||
class EventSubscribeCard extends LitElement {
|
@ -1,7 +1,7 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import { EventsMixin } from "../../mixins/events-mixin";
|
||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
121
src/panels/developer-tools/ha-panel-developer-tools.ts
Normal file
121
src/panels/developer-tools/ha-panel-developer-tools.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import {
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
CSSResultArray,
|
||||
css,
|
||||
customElement,
|
||||
property,
|
||||
} 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 "@polymer/paper-icon-button/paper-icon-button";
|
||||
import "@polymer/paper-tabs/paper-tab";
|
||||
import "@polymer/paper-tabs/paper-tabs";
|
||||
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
import "./developer-tools-router";
|
||||
|
||||
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../types";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import isComponentLoaded from "../../common/config/is_component_loaded";
|
||||
|
||||
@customElement("ha-panel-developer-tools")
|
||||
class PanelDeveloperTools extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public route!: Route;
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const page = this._page;
|
||||
return html`
|
||||
<app-header-layout has-scrolling-region>
|
||||
<app-header fixed slot="header">
|
||||
<app-toolbar>
|
||||
<ha-menu-button></ha-menu-button>
|
||||
<div main-title>Developer Tools</div>
|
||||
</app-toolbar>
|
||||
<paper-tabs
|
||||
scrollable
|
||||
attr-for-selected="page-name"
|
||||
.selected=${page}
|
||||
@iron-activate=${this.handlePageSelected}
|
||||
>
|
||||
<paper-tab page-name="info">
|
||||
${this.hass.localize("panel.dev-info")}
|
||||
</paper-tab>
|
||||
<paper-tab page-name="event">
|
||||
${this.hass.localize("panel.dev-events")}
|
||||
</paper-tab>
|
||||
${isComponentLoaded(this.hass, "mqtt")
|
||||
? html`
|
||||
<paper-tab page-name="mqtt">
|
||||
${this.hass.localize("panel.dev-mqtt")}
|
||||
</paper-tab>
|
||||
`
|
||||
: ""}
|
||||
<paper-tab page-name="service">
|
||||
${this.hass.localize("panel.dev-services")}
|
||||
</paper-tab>
|
||||
<paper-tab page-name="state">
|
||||
${this.hass.localize("panel.dev-states")}
|
||||
</paper-tab>
|
||||
<paper-tab page-name="template">
|
||||
${this.hass.localize("panel.dev-templates")}
|
||||
</paper-tab>
|
||||
</paper-tabs>
|
||||
</app-header>
|
||||
<developer-tools-router
|
||||
.route=${this.route}
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></developer-tools-router>
|
||||
</app-header-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
private handlePageSelected(ev) {
|
||||
const newPage = ev.detail.item.getAttribute("page-name");
|
||||
if (newPage !== this._page) {
|
||||
navigate(this, `/developer-tools/${newPage}`);
|
||||
}
|
||||
|
||||
scrollToTarget(
|
||||
this,
|
||||
// @ts-ignore
|
||||
this.shadowRoot!.querySelector("app-header-layout").header.scrollTarget
|
||||
);
|
||||
}
|
||||
|
||||
private get _page() {
|
||||
return this.route.path.substr(1);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
color: var(--primary-text-color);
|
||||
--paper-card-header-color: var(--primary-text-color);
|
||||
}
|
||||
paper-tabs {
|
||||
margin-left: 12px;
|
||||
--paper-tabs-selection-bar-color: #fff;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-panel-developer-tools": PanelDeveloperTools;
|
||||
}
|
||||
}
|
194
src/panels/developer-tools/info/developer-tools-info.ts
Normal file
194
src/panels/developer-tools/info/developer-tools-info.ts
Normal file
@ -0,0 +1,194 @@
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
CSSResult,
|
||||
css,
|
||||
TemplateResult,
|
||||
property,
|
||||
} from "lit-element";
|
||||
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
|
||||
import "./system-log-card";
|
||||
import "./error-log-card";
|
||||
import "./system-health-card";
|
||||
|
||||
const JS_VERSION = __BUILD__;
|
||||
const OPT_IN_PANEL = "states";
|
||||
|
||||
class HaPanelDevInfo extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const hass = this.hass;
|
||||
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`
|
||||
<div class="about">
|
||||
<p class="version">
|
||||
<a href="https://www.home-assistant.io" target="_blank"
|
||||
><img
|
||||
src="/static/icons/favicon-192x192.png"
|
||||
height="192"
|
||||
alt="Home Assistant logo"
|
||||
/></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
|
||||
>
|
||||
—
|
||||
<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 />
|
||||
<mwc-button @click="${this._toggleDefaultPage}" raised>
|
||||
${defaultPageText}
|
||||
</mwc-button>
|
||||
</p>
|
||||
</div>
|
||||
<system-health-card .hass=${this.hass}></system-health-card>
|
||||
<system-log-card .hass=${this.hass}></system-log-card>
|
||||
<error-log-card .hass=${this.hass}></error-log-card>
|
||||
`;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
system-health-card {
|
||||
display: block;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"developer-tools-info": HaPanelDevInfo;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("developer-tools-info", HaPanelDevInfo);
|
@ -8,11 +8,11 @@ import {
|
||||
} from "lit-element";
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
|
||||
import "../../components/dialog/ha-paper-dialog";
|
||||
import "../../../components/dialog/ha-paper-dialog";
|
||||
|
||||
import { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
|
||||
import { PolymerChangedEvent } from "../../polymer-types";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
|
||||
class DialogSystemLogDetail extends LitElement {
|
||||
private _params?: SystemLogDetailDialogParams;
|
@ -9,8 +9,8 @@ import {
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import "@material/mwc-button";
|
||||
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { fetchErrorLog } from "../../data/error_log";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { fetchErrorLog } from "../../../data/error_log";
|
||||
|
||||
class ErrorLogCard extends LitElement {
|
||||
public hass?: HomeAssistant;
|
@ -1,5 +1,5 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { LoggedError } from "../../data/system_log";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { LoggedError } from "../../../data/system_log";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
@ -7,13 +7,13 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "@polymer/paper-spinner/paper-spinner";
|
||||
import "../../components/ha-card";
|
||||
import "../../../components/ha-card";
|
||||
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
SystemHealthInfo,
|
||||
fetchSystemHealthInfo,
|
||||
} from "../../data/system_health";
|
||||
} from "../../../data/system_health";
|
||||
|
||||
const sortKeys = (a: string, b: string) => {
|
||||
if (a === "homeassistant") {
|
@ -10,13 +10,13 @@ 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/ha-card";
|
||||
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 "../../../components/ha-card";
|
||||
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) => {
|
@ -7,10 +7,10 @@ import "@polymer/paper-input/paper-textarea";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../../components/ha-card";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
import "../../util/app-localstorage-document";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-menu-button";
|
||||
import "../../../resources/ha-style";
|
||||
import "../../../util/app-localstorage-document";
|
||||
|
||||
class HaPanelDevMqtt extends PolymerElement {
|
||||
static get template() {
|
||||
@ -86,4 +86,4 @@ class HaPanelDevMqtt extends PolymerElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-dev-mqtt", HaPanelDevMqtt);
|
||||
customElements.define("developer-tools-mqtt", HaPanelDevMqtt);
|
@ -1,17 +1,13 @@
|
||||
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 "@material/mwc-button";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import { ENTITY_COMPONENT_DOMAINS } from "../../data/entity";
|
||||
import "../../components/entity/ha-entity-picker";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../components/ha-service-picker";
|
||||
import "../../resources/ha-style";
|
||||
import "../../util/app-localstorage-document";
|
||||
import { ENTITY_COMPONENT_DOMAINS } from "../../../data/entity";
|
||||
import "../../../components/entity/ha-entity-picker";
|
||||
import "../../../components/ha-service-picker";
|
||||
import "../../../resources/ha-style";
|
||||
import "../../../util/app-localstorage-document";
|
||||
|
||||
const ERROR_SENTINEL = {};
|
||||
class HaPanelDevService extends PolymerElement {
|
||||
@ -22,9 +18,7 @@ class HaPanelDevService extends PolymerElement {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
direction: ltr;
|
||||
}
|
||||
@ -81,100 +75,87 @@ class HaPanelDevService extends PolymerElement {
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout has-scrolling-region>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-menu-button></ha-menu-button>
|
||||
<div main-title>Services</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<app-localstorage-document
|
||||
key="panel-dev-service-state-domain-service"
|
||||
data="{{domainService}}"
|
||||
>
|
||||
</app-localstorage-document>
|
||||
<app-localstorage-document
|
||||
key="[[_computeServicedataKey(domainService)]]"
|
||||
data="{{serviceData}}"
|
||||
>
|
||||
</app-localstorage-document>
|
||||
|
||||
<app-localstorage-document
|
||||
key="panel-dev-service-state-domain-service"
|
||||
data="{{domainService}}"
|
||||
>
|
||||
</app-localstorage-document>
|
||||
<app-localstorage-document
|
||||
key="[[_computeServicedataKey(domainService)]]"
|
||||
data="{{serviceData}}"
|
||||
>
|
||||
</app-localstorage-document>
|
||||
<div class="content">
|
||||
<p>
|
||||
The service dev tool allows you to call any available service in Home
|
||||
Assistant.
|
||||
</p>
|
||||
|
||||
<div class="content">
|
||||
<p>
|
||||
The service dev tool allows you to call any available service in
|
||||
Home Assistant.
|
||||
</p>
|
||||
|
||||
<div class="ha-form">
|
||||
<ha-service-picker
|
||||
<div class="ha-form">
|
||||
<ha-service-picker
|
||||
hass="[[hass]]"
|
||||
value="{{domainService}}"
|
||||
></ha-service-picker>
|
||||
<template is="dom-if" if="[[_computeHasEntity(_attributes)]]">
|
||||
<ha-entity-picker
|
||||
hass="[[hass]]"
|
||||
value="{{domainService}}"
|
||||
></ha-service-picker>
|
||||
<template is="dom-if" if="[[_computeHasEntity(_attributes)]]">
|
||||
<ha-entity-picker
|
||||
hass="[[hass]]"
|
||||
value="[[_computeEntityValue(parsedJSON)]]"
|
||||
on-change="_entityPicked"
|
||||
disabled="[[!validJSON]]"
|
||||
domain-filter="[[_computeEntityDomainFilter(_domain)]]"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</template>
|
||||
<paper-textarea
|
||||
always-float-label
|
||||
label="Service Data (JSON, optional)"
|
||||
value="{{serviceData}}"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
></paper-textarea>
|
||||
<mwc-button on-click="_callService" raised disabled="[[!validJSON]]"
|
||||
>Call Service</mwc-button
|
||||
>
|
||||
<template is="dom-if" if="[[!validJSON]]">
|
||||
<span class="error">Invalid JSON</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template is="dom-if" if="[[!domainService]]">
|
||||
<h1>Select a service to see the description</h1>
|
||||
value="[[_computeEntityValue(parsedJSON)]]"
|
||||
on-change="_entityPicked"
|
||||
disabled="[[!validJSON]]"
|
||||
domain-filter="[[_computeEntityDomainFilter(_domain)]]"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[domainService]]">
|
||||
<template is="dom-if" if="[[!_description]]">
|
||||
<h1>No description is available</h1>
|
||||
</template>
|
||||
<template is="dom-if" if="[[_description]]">
|
||||
<h3>[[_description]]</h3>
|
||||
|
||||
<table class="attributes">
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Description</th>
|
||||
<th>Example</th>
|
||||
</tr>
|
||||
<template is="dom-if" if="[[!_attributes.length]]">
|
||||
<tr>
|
||||
<td colspan="3">This service takes no parameters.</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template
|
||||
is="dom-repeat"
|
||||
items="[[_attributes]]"
|
||||
as="attribute"
|
||||
>
|
||||
<tr>
|
||||
<td><pre>[[attribute.key]]</pre></td>
|
||||
<td>[[attribute.description]]</td>
|
||||
<td>[[attribute.example]]</td>
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
</template>
|
||||
<paper-textarea
|
||||
always-float-label
|
||||
label="Service Data (JSON, optional)"
|
||||
value="{{serviceData}}"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
></paper-textarea>
|
||||
<mwc-button on-click="_callService" raised disabled="[[!validJSON]]"
|
||||
>Call Service</mwc-button
|
||||
>
|
||||
<template is="dom-if" if="[[!validJSON]]">
|
||||
<span class="error">Invalid JSON</span>
|
||||
</template>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
|
||||
<template is="dom-if" if="[[!domainService]]">
|
||||
<h1>Select a service to see the description</h1>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[domainService]]">
|
||||
<template is="dom-if" if="[[!_description]]">
|
||||
<h1>No description is available</h1>
|
||||
</template>
|
||||
<template is="dom-if" if="[[_description]]">
|
||||
<h3>[[_description]]</h3>
|
||||
|
||||
<table class="attributes">
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Description</th>
|
||||
<th>Example</th>
|
||||
</tr>
|
||||
<template is="dom-if" if="[[!_attributes.length]]">
|
||||
<tr>
|
||||
<td colspan="3">This service takes no parameters.</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template is="dom-repeat" items="[[_attributes]]" as="attribute">
|
||||
<tr>
|
||||
<td><pre>[[attribute.key]]</pre></td>
|
||||
<td>[[attribute.description]]</td>
|
||||
<td>[[attribute.example]]</td>
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -304,4 +285,4 @@ class HaPanelDevService extends PolymerElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-dev-service", HaPanelDevService);
|
||||
customElements.define("developer-tools-service", HaPanelDevService);
|
@ -1,6 +1,3 @@
|
||||
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 "@material/mwc-button";
|
||||
import "@polymer/paper-checkbox/paper-checkbox";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
@ -8,10 +5,9 @@ import "@polymer/paper-input/paper-textarea";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../../components/entity/ha-entity-picker";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
import { EventsMixin } from "../../mixins/events-mixin";
|
||||
import "../../../components/entity/ha-entity-picker";
|
||||
import "../../../resources/ha-style";
|
||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
@ -24,9 +20,7 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
direction: ltr;
|
||||
}
|
||||
@ -70,107 +64,96 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout has-scrolling-region>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-menu-button></ha-menu-button>
|
||||
<div main-title>States</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div>
|
||||
<p>
|
||||
Set the representation of a device within Home Assistant.<br />
|
||||
This will not communicate with the actual device.
|
||||
</p>
|
||||
|
||||
<div class="content">
|
||||
<div>
|
||||
<p>
|
||||
Set the representation of a device within Home Assistant.<br />
|
||||
This will not communicate with the actual device.
|
||||
</p>
|
||||
<ha-entity-picker
|
||||
autofocus
|
||||
hass="[[hass]]"
|
||||
value="{{_entityId}}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<paper-input
|
||||
label="State"
|
||||
required
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
value="{{_state}}"
|
||||
class="state-input"
|
||||
></paper-input>
|
||||
<paper-textarea
|
||||
label="State attributes (JSON, optional)"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
value="{{_stateAttributes}}"
|
||||
></paper-textarea>
|
||||
<mwc-button on-click="handleSetState" raised>Set State</mwc-button>
|
||||
</div>
|
||||
|
||||
<ha-entity-picker
|
||||
autofocus
|
||||
hass="[[hass]]"
|
||||
value="{{_entityId}}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<h1>Current entities</h1>
|
||||
<table class="entities">
|
||||
<tr>
|
||||
<th>Entity</th>
|
||||
<th>State</th>
|
||||
<th hidden$="[[narrow]]">
|
||||
Attributes
|
||||
<paper-checkbox checked="{{_showAttributes}}"></paper-checkbox>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<paper-input
|
||||
label="State"
|
||||
required
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
value="{{_state}}"
|
||||
class="state-input"
|
||||
label="Filter entities"
|
||||
type="search"
|
||||
value="{{_entityFilter}}"
|
||||
></paper-input>
|
||||
<paper-textarea
|
||||
label="State attributes (JSON, optional)"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
value="{{_stateAttributes}}"
|
||||
></paper-textarea>
|
||||
<mwc-button on-click="handleSetState" raised>Set State</mwc-button>
|
||||
</div>
|
||||
|
||||
<h1>Current entities</h1>
|
||||
<table class="entities">
|
||||
<tr>
|
||||
<th>Entity</th>
|
||||
<th>State</th>
|
||||
<th hidden$="[[narrow]]">
|
||||
Attributes
|
||||
<paper-checkbox checked="{{_showAttributes}}"></paper-checkbox>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<paper-input
|
||||
label="Filter entities"
|
||||
type="search"
|
||||
value="{{_entityFilter}}"
|
||||
></paper-input>
|
||||
</th>
|
||||
<th>
|
||||
<paper-input
|
||||
label="Filter states"
|
||||
type="search"
|
||||
value="{{_stateFilter}}"
|
||||
></paper-input>
|
||||
</th>
|
||||
<th hidden$="[[!computeShowAttributes(narrow, _showAttributes)]]">
|
||||
<paper-input
|
||||
label="Filter attributes"
|
||||
type="search"
|
||||
value="{{_attributeFilter}}"
|
||||
></paper-input>
|
||||
</th>
|
||||
</tr>
|
||||
<tr hidden$="[[!computeShowEntitiesPlaceholder(_entities)]]">
|
||||
<td colspan="3">No entities</td>
|
||||
</tr>
|
||||
<template is="dom-repeat" items="[[_entities]]" as="entity">
|
||||
<tr>
|
||||
<td>
|
||||
<paper-icon-button
|
||||
on-click="entityMoreInfo"
|
||||
icon="hass:open-in-new"
|
||||
alt="More Info"
|
||||
title="More Info"
|
||||
>
|
||||
</paper-icon-button>
|
||||
<a href="#" on-click="entitySelected">[[entity.entity_id]]</a>
|
||||
</td>
|
||||
<td>[[entity.state]]</td>
|
||||
<template
|
||||
is="dom-if"
|
||||
if="[[computeShowAttributes(narrow, _showAttributes)]]"
|
||||
>
|
||||
<td>[[attributeString(entity)]]</td>
|
||||
</template>
|
||||
</tr>
|
||||
</th>
|
||||
<th>
|
||||
<paper-input
|
||||
label="Filter states"
|
||||
type="search"
|
||||
value="{{_stateFilter}}"
|
||||
></paper-input>
|
||||
</th>
|
||||
<th hidden$="[[!computeShowAttributes(narrow, _showAttributes)]]">
|
||||
<paper-input
|
||||
label="Filter attributes"
|
||||
type="search"
|
||||
value="{{_attributeFilter}}"
|
||||
></paper-input>
|
||||
</th>
|
||||
</tr>
|
||||
<tr hidden$="[[!computeShowEntitiesPlaceholder(_entities)]]">
|
||||
<td colspan="3">No entities</td>
|
||||
</tr>
|
||||
<template is="dom-repeat" items="[[_entities]]" as="entity">
|
||||
<tr>
|
||||
<td>
|
||||
<paper-icon-button
|
||||
on-click="entityMoreInfo"
|
||||
icon="hass:open-in-new"
|
||||
alt="More Info"
|
||||
title="More Info"
|
||||
>
|
||||
</paper-icon-button>
|
||||
<a href="#" on-click="entitySelected">[[entity.entity_id]]</a>
|
||||
</td>
|
||||
<td>[[entity.state]]</td>
|
||||
<template
|
||||
is="dom-if"
|
||||
if="[[computeShowAttributes(narrow, _showAttributes)]]"
|
||||
>
|
||||
<td>[[attributeString(entity)]]</td>
|
||||
</template>
|
||||
</table>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -351,4 +334,4 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-dev-state", HaPanelDevState);
|
||||
customElements.define("developer-tools-state", HaPanelDevState);
|
@ -1,6 +1,3 @@
|
||||
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-input/paper-textarea";
|
||||
import "@polymer/paper-spinner/paper-spinner";
|
||||
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
||||
@ -8,8 +5,7 @@ import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
import "../../../resources/ha-style";
|
||||
|
||||
class HaPanelDevTemplate extends PolymerElement {
|
||||
static get template() {
|
||||
@ -67,52 +63,43 @@ class HaPanelDevTemplate extends PolymerElement {
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout has-scrolling-region>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-menu-button></ha-menu-button>
|
||||
<div main-title>Templates</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<div class$="[[computeFormClasses(narrow)]]">
|
||||
<div class="edit-pane">
|
||||
<p>
|
||||
Templates are rendered using the Jinja2 template engine with some
|
||||
Home Assistant specific extensions.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="http://jinja.pocoo.org/docs/dev/templates/"
|
||||
target="_blank"
|
||||
>Jinja2 template documentation</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://home-assistant.io/docs/configuration/templating/"
|
||||
target="_blank"
|
||||
>Home Assistant template extensions</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<paper-textarea
|
||||
label="Template editor"
|
||||
value="{{template}}"
|
||||
autofocus
|
||||
></paper-textarea>
|
||||
</div>
|
||||
|
||||
<div class="render-pane">
|
||||
<paper-spinner
|
||||
class="render-spinner"
|
||||
active="[[rendering]]"
|
||||
></paper-spinner>
|
||||
<pre class$="[[computeRenderedClasses(error)]]">[[processed]]</pre>
|
||||
</div>
|
||||
<div class$="[[computeFormClasses(narrow)]]">
|
||||
<div class="edit-pane">
|
||||
<p>
|
||||
Templates are rendered using the Jinja2 template engine with some
|
||||
Home Assistant specific extensions.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="http://jinja.pocoo.org/docs/dev/templates/"
|
||||
target="_blank"
|
||||
>Jinja2 template documentation</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://home-assistant.io/docs/configuration/templating/"
|
||||
target="_blank"
|
||||
>Home Assistant template extensions</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<paper-textarea
|
||||
label="Template editor"
|
||||
value="{{template}}"
|
||||
autofocus
|
||||
></paper-textarea>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
|
||||
<div class="render-pane">
|
||||
<paper-spinner
|
||||
class="render-spinner"
|
||||
active="[[rendering]]"
|
||||
></paper-spinner>
|
||||
<pre class$="[[computeRenderedClasses(error)]]">[[processed]]</pre>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -207,4 +194,4 @@ For loop example:
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-dev-template", HaPanelDevTemplate);
|
||||
customElements.define("developer-tools-template", HaPanelDevTemplate);
|
Loading…
x
Reference in New Issue
Block a user