Convert dev tools template to Lit and store last used template (#6669)

This commit is contained in:
Bram Kragten 2020-08-24 18:26:41 +02:00 committed by GitHub
parent f4ca94f2e1
commit 3ba31483f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 281 additions and 204 deletions

View File

@ -1,204 +0,0 @@
import "../../../components/ha-circular-progress";
import { timeOut } from "@polymer/polymer/lib/utils/async";
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../components/ha-code-editor";
import LocalizeMixin from "../../../mixins/localize-mixin";
import "../../../styles/polymer-ha-style";
class HaPanelDevTemplate extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style include="ha-style iron-flex iron-positioning"></style>
<style>
:host {
-ms-user-select: initial;
-webkit-user-select: initial;
-moz-user-select: initial;
}
.content {
padding: 16px;
direction: ltr;
}
.edit-pane {
margin-right: 16px;
}
.edit-pane a {
color: var(--primary-color);
}
.horizontal .edit-pane {
max-width: 50%;
}
.render-pane {
position: relative;
max-width: 50%;
}
.render-spinner {
position: absolute;
top: 8px;
right: 8px;
}
.rendered {
@apply --paper-font-code1;
clear: both;
white-space: pre-wrap;
}
.rendered.error {
color: red;
}
</style>
<div class$="[[computeFormClasses(narrow)]]">
<div class="edit-pane">
<p>
[[localize('ui.panel.developer-tools.tabs.templates.description')]]
</p>
<ul>
<li>
<a
href="http://jinja.pocoo.org/docs/dev/templates/"
target="_blank"
rel="noreferrer"
>[[localize('ui.panel.developer-tools.tabs.templates.jinja_documentation')]]</a
>
</li>
<li>
<a
href="https://home-assistant.io/docs/configuration/templating/"
target="_blank"
rel="noreferrer"
>[[localize('ui.panel.developer-tools.tabs.templates.template_extensions')]]</a
>
</li>
</ul>
<p>[[localize('ui.panel.developer-tools.tabs.templates.editor')]]</p>
<ha-code-editor
mode="jinja2"
value="[[template]]"
error="[[error]]"
autofocus
on-value-changed="templateChanged"
></ha-code-editor>
</div>
<div class="render-pane">
<ha-circular-progress
class="render-spinner"
active="[[rendering]]"
size="small"
></ha-circular-progress>
<pre class$="[[computeRenderedClasses(error)]]">[[processed]]</pre>
</div>
</div>
`;
}
static get properties() {
return {
hass: {
type: Object,
},
error: {
type: Boolean,
value: false,
},
rendering: {
type: Boolean,
value: false,
},
template: {
type: String,
/* eslint-disable max-len */
value: `Imitate available variables:
{% set my_test_json = {
"temperature": 25,
"unit": "°C"
} %}
The temperature is {{ my_test_json.temperature }} {{ my_test_json.unit }}.
{% if is_state("device_tracker.paulus", "home") and
is_state("device_tracker.anne_therese", "home") -%}
You are both home, you silly
{%- else -%}
Anne Therese is at {{ states("device_tracker.anne_therese") }}
Paulus is at {{ states("device_tracker.paulus") }}
{%- endif %}
For loop example:
{% for state in states.sensor -%}
{%- if loop.first %}The {% elif loop.last %} and the {% else %}, the {% endif -%}
{{ state.name | lower }} is {{state.state_with_unit}}
{%- endfor %}.`,
/* eslint-enable max-len */
},
processed: {
type: String,
value: "",
},
};
}
ready() {
super.ready();
this.renderTemplate();
}
computeFormClasses(narrow) {
return narrow ? "content" : "content layout horizontal";
}
computeRenderedClasses(error) {
return error ? "error rendered" : "rendered";
}
templateChanged(ev) {
this.template = ev.detail.value;
if (this.error) {
this.error = false;
}
this._debouncer = Debouncer.debounce(
this._debouncer,
timeOut.after(500),
() => {
this.renderTemplate();
}
);
}
renderTemplate() {
this.rendering = true;
this.hass.callApi("POST", "template", { template: this.template }).then(
function (processed) {
this.processed = processed;
this.rendering = false;
}.bind(this),
function (error) {
this.processed =
(error && error.body && error.body.message) ||
this.hass.localize(
"ui.panel.developer-tools.tabs.templates.unknown_error_template"
);
this.error = true;
this.rendering = false;
}.bind(this)
);
}
}
customElements.define("developer-tools-template", HaPanelDevTemplate);

View File

@ -0,0 +1,280 @@
import "@material/mwc-button/mwc-button";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResultArray,
customElement,
html,
internalProperty,
LitElement,
property,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-circular-progress";
import "../../../components/ha-code-editor";
import { subscribeRenderTemplate } from "../../../data/ws-templates";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
const DEMO_TEMPLATE = `{## Imitate available variables: ##}
{% set my_test_json = {
"temperature": 25,
"unit": "°C"
} %}
The temperature is {{ my_test_json.temperature }} {{ my_test_json.unit }}.
{% if is_state("sun.sun", "above_horizon") -%}
The sun rose {{ relative_time(states.sun.sun.last_changed) }} ago.
{%- else -%}
The sun will rise at {{ as_timestamp(strptime(state_attr("sun.sun", "next_rising"), "")) | timestamp_local }}.
{%- endif %}
For loop example getting 3 entity values:
{% for states in states | slice(3) -%}
{% set state = states | first %}
{%- if loop.first %}The {% elif loop.last %} and the {% else %}, the {% endif -%}
{{ state.name | lower }} is {{state.state_with_unit}}
{%- endfor %}.`;
@customElement("developer-tools-template")
class HaPanelDevTemplate extends LitElement {
@property() public hass!: HomeAssistant;
@property() public narrow!: boolean;
@internalProperty() private _error = false;
@internalProperty() private _rendering = false;
@internalProperty() private _processed = "";
@internalProperty() private _unsubRenderTemplate?: Promise<UnsubscribeFunc>;
private _template = "";
private _inited = false;
public connectedCallback() {
super.connectedCallback();
if (this._template && !this._unsubRenderTemplate) {
this._subscribeTemplate();
}
}
public disconnectedCallback() {
this._unsubscribeTemplate();
}
protected firstUpdated() {
if (localStorage && localStorage["panel-dev-template-template"]) {
this._template = localStorage["panel-dev-template-template"];
} else {
this._template = DEMO_TEMPLATE;
}
this._subscribeTemplate();
this._inited = true;
}
protected render() {
return html`
<div
class="content ${classMap({
layout: !this.narrow,
horizontal: !this.narrow,
})}"
>
<div class="edit-pane">
<p>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.description"
)}
</p>
<ul>
<li>
<a
href="http://jinja.pocoo.org/docs/dev/templates/"
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.jinja_documentation"
)}
</a>
</li>
<li>
<a
href="https://home-assistant.io/docs/configuration/templating/"
target="_blank"
rel="noreferrer"
>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.template_extensions"
)}</a
>
</li>
</ul>
<p>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.editor"
)}
</p>
<ha-code-editor
mode="jinja2"
.value=${this._template}
.error=${this._error}
autofocus
@value-changed=${this._templateChanged}
></ha-code-editor>
<mwc-button @click=${this._restoreDemo}>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.reset"
)}
</mwc-button>
</div>
<div class="render-pane">
<ha-circular-progress
class="render-spinner"
.active=${this._rendering}
size="small"
></ha-circular-progress>
<pre class="rendered ${classMap({ error: this._error })}">
${this._processed}</pre
>
</div>
</div>
`;
}
static get styles(): CSSResultArray {
return [
haStyle,
css`
:host {
-ms-user-select: initial;
-webkit-user-select: initial;
-moz-user-select: initial;
}
.content {
padding: 16px;
direction: ltr;
}
.edit-pane {
margin-right: 16px;
}
.edit-pane a {
color: var(--primary-color);
}
.horizontal .edit-pane {
max-width: 50%;
}
.render-pane {
position: relative;
max-width: 50%;
}
.render-spinner {
position: absolute;
top: 8px;
right: 8px;
}
.rendered {
@apply --paper-font-code1;
clear: both;
white-space: pre-wrap;
}
.rendered.error {
color: var(--error-color);
}
`,
];
}
private _debounceRender = debounce(
() => {
this._subscribeTemplate();
this._storeTemplate();
},
500,
false
);
private _templateChanged(ev) {
this._template = ev.detail.value;
if (this._error) {
this._error = false;
}
this._debounceRender();
}
private async _subscribeTemplate() {
this._rendering = true;
await this._unsubscribeTemplate();
try {
this._unsubRenderTemplate = subscribeRenderTemplate(
this.hass.connection,
(result) => {
this._processed = result;
},
{
template: this._template,
}
);
await this._unsubRenderTemplate;
} catch (err) {
this._error = true;
if (err.message) {
this._processed = err.message;
}
this._unsubRenderTemplate = undefined;
} finally {
this._rendering = false;
}
}
private async _unsubscribeTemplate(): Promise<void> {
if (!this._unsubRenderTemplate) {
return;
}
try {
const unsub = await this._unsubRenderTemplate;
unsub();
this._unsubRenderTemplate = undefined;
} catch (e) {
if (e.code === "not_found") {
// If we get here, the connection was probably already closed. Ignore.
} else {
throw e;
}
}
}
private _storeTemplate() {
if (!this._inited) {
return;
}
localStorage["panel-dev-template-template"] = this._template;
}
private _restoreDemo() {
this._template = DEMO_TEMPLATE;
this._subscribeTemplate();
delete localStorage["panel-dev-template-template"];
}
}
declare global {
interface HTMLElementTagNameMap {
"developer-tools-template": HaPanelDevTemplate;
}
}

View File

@ -2592,6 +2592,7 @@
"title": "Template", "title": "Template",
"description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.", "description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.",
"editor": "Template editor", "editor": "Template editor",
"reset": "Reset to demo template",
"jinja_documentation": "Jinja2 template documentation", "jinja_documentation": "Jinja2 template documentation",
"template_extensions": "Home Assistant template extensions", "template_extensions": "Home Assistant template extensions",
"unknown_error_template": "Unknown error rendering template" "unknown_error_template": "Unknown error rendering template"