mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 19:56:42 +00:00
WIP initial Blueprint UI (#7695)
* WIP initial Blueprint UI * Review comments * localize
This commit is contained in:
parent
3aff4c96c4
commit
4973d8f629
119
src/components/ha-blueprint-picker.ts
Normal file
119
src/components/ha-blueprint-picker.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { compare } from "../common/string/compare";
|
||||
import { Blueprints, fetchBlueprints } from "../data/blueprint";
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
@customElement("ha-blueprint-picker")
|
||||
class HaBluePrintPicker extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public value = "";
|
||||
|
||||
@property() public domain = "automation";
|
||||
|
||||
@property() public blueprints?: Blueprints;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
private _processedBlueprints = memoizeOne((blueprints?: Blueprints) => {
|
||||
if (!blueprints) {
|
||||
return [];
|
||||
}
|
||||
const result = Object.entries(blueprints).map(([path, blueprint]) => ({
|
||||
...blueprint.metadata,
|
||||
path,
|
||||
}));
|
||||
return result.sort((a, b) => compare(a.name, b.name));
|
||||
});
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<paper-dropdown-menu-light
|
||||
.label=${this.label ||
|
||||
this.hass.localize("ui.components.blueprint-picker.label")}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
.selected=${this.value}
|
||||
attr-for-selected="data-blueprint-path"
|
||||
@iron-select=${this._blueprintChanged}
|
||||
>
|
||||
<paper-item data-blueprint-path="">
|
||||
${this.hass.localize(
|
||||
"ui.components.blueprint-picker.select_blueprint"
|
||||
)}
|
||||
</paper-item>
|
||||
${this._processedBlueprints(this.blueprints).map(
|
||||
(blueprint) => html`
|
||||
<paper-item data-blueprint-path=${blueprint.path}>
|
||||
${blueprint.name}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
if (this.blueprints === undefined) {
|
||||
fetchBlueprints(this.hass!, this.domain).then((blueprints) => {
|
||||
this.blueprints = blueprints;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _blueprintChanged(ev) {
|
||||
const newValue = ev.detail.item.dataset.blueprintPath;
|
||||
|
||||
if (newValue !== this.value) {
|
||||
this.value = ev.detail.value;
|
||||
setTimeout(() => {
|
||||
fireEvent(this, "value-changed", { value: newValue });
|
||||
fireEvent(this, "change");
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
paper-dropdown-menu-light {
|
||||
width: 100%;
|
||||
min-width: 200px;
|
||||
display: block;
|
||||
}
|
||||
paper-listbox {
|
||||
min-width: 200px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-blueprint-picker": HaBluePrintPicker;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import {
|
||||
} from "home-assistant-js-websocket";
|
||||
import { navigate } from "../common/navigate";
|
||||
import { Context, HomeAssistant } from "../types";
|
||||
import { BlueprintInput } from "./blueprint";
|
||||
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
||||
import { Action } from "./script";
|
||||
|
||||
@ -14,10 +15,14 @@ export interface AutomationEntity extends HassEntityBase {
|
||||
};
|
||||
}
|
||||
|
||||
export interface AutomationConfig {
|
||||
export type AutomationConfig =
|
||||
| ManualAutomationConfig
|
||||
| BlueprintAutomationConfig;
|
||||
|
||||
export interface ManualAutomationConfig {
|
||||
id?: string;
|
||||
alias: string;
|
||||
description: string;
|
||||
alias?: string;
|
||||
description?: string;
|
||||
trigger: Trigger[];
|
||||
condition?: Condition[];
|
||||
action: Action[];
|
||||
@ -25,6 +30,10 @@ export interface AutomationConfig {
|
||||
max?: number;
|
||||
}
|
||||
|
||||
export interface BlueprintAutomationConfig extends ManualAutomationConfig {
|
||||
use_blueprint: { path: string; input?: BlueprintInput };
|
||||
}
|
||||
|
||||
export interface ForDict {
|
||||
hours?: number | string;
|
||||
minutes?: number | string;
|
||||
|
54
src/data/blueprint.ts
Normal file
54
src/data/blueprint.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export type Blueprints = Record<string, Blueprint>;
|
||||
|
||||
export interface Blueprint {
|
||||
metadata: BlueprintMetaData;
|
||||
}
|
||||
|
||||
export interface BlueprintMetaData {
|
||||
domain: string;
|
||||
name: string;
|
||||
input: BlueprintInput;
|
||||
}
|
||||
|
||||
export type BlueprintInput = Record<string, any>;
|
||||
|
||||
export interface BlueprintImportResult {
|
||||
url: string;
|
||||
suggested_filename: string;
|
||||
raw_data: string;
|
||||
blueprint: Blueprint;
|
||||
}
|
||||
|
||||
export const fetchBlueprints = (hass: HomeAssistant, domain: string) =>
|
||||
hass.callWS<Blueprints>({ type: "blueprint/list", domain });
|
||||
|
||||
export const importBlueprint = (hass: HomeAssistant, url: string) =>
|
||||
hass.callWS<BlueprintImportResult>({ type: "blueprint/import", url });
|
||||
|
||||
export const saveBlueprint = (
|
||||
hass: HomeAssistant,
|
||||
domain: string,
|
||||
path: string,
|
||||
yaml: string,
|
||||
source_url?: string
|
||||
) =>
|
||||
hass.callWS({
|
||||
type: "blueprint/save",
|
||||
domain,
|
||||
path,
|
||||
yaml,
|
||||
source_url,
|
||||
});
|
||||
|
||||
export const deleteBlueprint = (
|
||||
hass: HomeAssistant,
|
||||
domain: string,
|
||||
path: string
|
||||
) =>
|
||||
hass.callWS<BlueprintImportResult>({
|
||||
type: "blueprint/delete",
|
||||
domain,
|
||||
path,
|
||||
});
|
306
src/panels/config/automation/blueprint-automation-editor.ts
Normal file
306
src/panels/config/automation/blueprint-automation-editor.ts
Normal file
@ -0,0 +1,306 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import { html } from "lit-html";
|
||||
import {
|
||||
BlueprintAutomationConfig,
|
||||
triggerAutomation,
|
||||
} from "../../../data/automation";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
import "../../../components/ha-card";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||
import "../../../components/entity/ha-entity-toggle";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "./trigger/ha-automation-trigger";
|
||||
import "./condition/ha-automation-condition";
|
||||
import "./action/ha-automation-action";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import {
|
||||
Blueprint,
|
||||
Blueprints,
|
||||
fetchBlueprints,
|
||||
} from "../../../data/blueprint";
|
||||
import "../../../components/ha-blueprint-picker";
|
||||
import "../../../components/ha-circular-progress";
|
||||
|
||||
@customElement("blueprint-automation-editor")
|
||||
export class HaBlueprintAutomationEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public isWide!: boolean;
|
||||
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
@property() public config!: BlueprintAutomationConfig;
|
||||
|
||||
@property() public stateObj?: HassEntity;
|
||||
|
||||
@internalProperty() private _blueprints?: Blueprints;
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._getBlueprints();
|
||||
}
|
||||
|
||||
private get _blueprint(): Blueprint | undefined {
|
||||
if (!this._blueprints) {
|
||||
return undefined;
|
||||
}
|
||||
return this._blueprints[this.config.use_blueprint.path];
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const blueprint = this._blueprint;
|
||||
return html`<ha-config-section .isWide=${this.isWide}>
|
||||
${!this.narrow
|
||||
? html` <span slot="header">${this.config.alias}</span> `
|
||||
: ""}
|
||||
<span slot="introduction">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.introduction"
|
||||
)}
|
||||
</span>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<paper-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.alias"
|
||||
)}
|
||||
name="alias"
|
||||
.value=${this.config.alias}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.description.label"
|
||||
)}
|
||||
.placeholder=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.description.placeholder"
|
||||
)}
|
||||
name="description"
|
||||
.value=${this.config.description}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-textarea>
|
||||
</div>
|
||||
${this.stateObj
|
||||
? html`
|
||||
<div class="card-actions layout horizontal justified center">
|
||||
<div class="layout horizontal center">
|
||||
<ha-entity-toggle
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj!}
|
||||
></ha-entity-toggle>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.enable_disable"
|
||||
)}
|
||||
</div>
|
||||
<mwc-button
|
||||
@click=${this._excuteAutomation}
|
||||
.stateObj=${this.stateObj}
|
||||
>
|
||||
${this.hass.localize("ui.card.automation.trigger")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.header"
|
||||
)}</span
|
||||
>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="blueprint-picker-container">
|
||||
${this._blueprints
|
||||
? Object.keys(this._blueprints).length
|
||||
? html`
|
||||
<ha-blueprint-picker
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.blueprint_to_use"
|
||||
)}
|
||||
.blueprints=${this._blueprints}
|
||||
.value=${this.config.use_blueprint.path}
|
||||
@value-changed=${this._blueprintChanged}
|
||||
></ha-blueprint-picker>
|
||||
`
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.no_blueprints"
|
||||
)
|
||||
: html`<ha-circular-progress active></ha-circular-progress>`}
|
||||
<mwc-button @click=${this._navigateBlueprints}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.manage_blueprints"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
${this.config.use_blueprint.path
|
||||
? blueprint?.metadata?.input &&
|
||||
Object.keys(blueprint.metadata.input).length
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.inputs"
|
||||
)}
|
||||
</h3>
|
||||
${Object.entries(blueprint.metadata.input).map(
|
||||
([key, value]) =>
|
||||
html`<div>
|
||||
${value?.description}
|
||||
<paper-input
|
||||
.key=${key}
|
||||
.label=${value?.name || key}
|
||||
.value=${this.config.use_blueprint.input &&
|
||||
this.config.use_blueprint.input[key]}
|
||||
@value-changed=${this._inputChanged}
|
||||
></paper-input>
|
||||
</div>`
|
||||
)}`
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.no_inputs"
|
||||
)
|
||||
: ""}
|
||||
</div>
|
||||
</ha-card>
|
||||
</ha-config-section>`;
|
||||
}
|
||||
|
||||
private async _getBlueprints() {
|
||||
this._blueprints = await fetchBlueprints(this.hass, "automation");
|
||||
}
|
||||
|
||||
private _excuteAutomation(ev: Event) {
|
||||
triggerAutomation(this.hass, (ev.target as any).stateObj.entity_id);
|
||||
}
|
||||
|
||||
private _blueprintChanged(ev) {
|
||||
ev.stopPropagation();
|
||||
if (this.config.use_blueprint.path === ev.detail.value) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.config!,
|
||||
use_blueprint: {
|
||||
path: ev.detail.value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _inputChanged(ev) {
|
||||
ev.stopPropagation();
|
||||
const target = ev.target as any;
|
||||
const key = target.key;
|
||||
const value = target.value;
|
||||
if (
|
||||
(this.config.use_blueprint.input &&
|
||||
this.config.use_blueprint.input[key] === value) ||
|
||||
(!this.config.use_blueprint.input && value === "")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.config!,
|
||||
use_blueprint: {
|
||||
...this.config.use_blueprint,
|
||||
input: { ...this.config.use_blueprint.input, [key]: value },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const target = ev.target as any;
|
||||
const name = target.name;
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
let newVal = ev.detail.value;
|
||||
if (target.type === "number") {
|
||||
newVal = Number(newVal);
|
||||
}
|
||||
if ((this.config![name] || "") === newVal) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this.config!, [name]: newVal },
|
||||
});
|
||||
}
|
||||
|
||||
private _navigateBlueprints() {
|
||||
navigate(this, "/config/blueprint");
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
.errors {
|
||||
padding: 20px;
|
||||
font-weight: bold;
|
||||
color: var(--error-color);
|
||||
}
|
||||
.content {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.blueprint-picker-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
h3 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
span[slot="introduction"] a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-entity-toggle {
|
||||
margin-right: 8px;
|
||||
}
|
||||
mwc-fab {
|
||||
position: relative;
|
||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
}
|
||||
mwc-fab.dirty {
|
||||
bottom: 0;
|
||||
}
|
||||
.selected_menu_item {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
li[role="separator"] {
|
||||
border-bottom-color: var(--divider-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"blueprint-automation-editor": HaBlueprintAutomationEditor;
|
||||
}
|
||||
}
|
175
src/panels/config/automation/dialog-new-automation.ts
Normal file
175
src/panels/config/automation/dialog-new-automation.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import "@material/mwc-button";
|
||||
import "../../../components/ha-circular-progress";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../components/ha-dialog";
|
||||
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import {
|
||||
AutomationConfig,
|
||||
showAutomationEditor,
|
||||
} from "../../../data/automation";
|
||||
import { showThingtalkDialog } from "./thingtalk/show-dialog-thingtalk";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-blueprint-picker";
|
||||
|
||||
@customElement("ha-dialog-new-automation")
|
||||
class DialogNewAutomation extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _opened = false;
|
||||
|
||||
public showDialog(): void {
|
||||
this._opened = true;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._opened = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._opened) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.header"
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
${this.hass.localize("ui.panel.config.automation.dialog_new.how")}
|
||||
<div class="container">
|
||||
${isComponentLoaded(this.hass, "cloud")
|
||||
? html`<ha-card outlined>
|
||||
<div>
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.thingtalk.header"
|
||||
)}
|
||||
</h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.thingtalk.intro"
|
||||
)}
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
id="input"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.thingtalk.input_label"
|
||||
)}
|
||||
></paper-input>
|
||||
<mwc-button @click=${this._thingTalk}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.thingtalk.create"
|
||||
)}</mwc-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>`
|
||||
: html``}
|
||||
${isComponentLoaded(this.hass, "blueprint")
|
||||
? html`<ha-card outlined>
|
||||
<div>
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.blueprint.use_blueprint"
|
||||
)}
|
||||
</h3>
|
||||
<ha-blueprint-picker
|
||||
@value-changed=${this._blueprintPicked}
|
||||
.hass=${this.hass}
|
||||
></ha-blueprint-picker>
|
||||
</div>
|
||||
</ha-card>`
|
||||
: html``}
|
||||
</div>
|
||||
</div>
|
||||
<mwc-button slot="primaryAction" @click=${this._blank}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.dialog_new.start_empty"
|
||||
)}
|
||||
</mwc-button>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _thingTalk() {
|
||||
this.closeDialog();
|
||||
showThingtalkDialog(this, {
|
||||
callback: (config: Partial<AutomationConfig> | undefined) =>
|
||||
showAutomationEditor(this, config),
|
||||
input: this.shadowRoot!.querySelector("paper-input")!.value as string,
|
||||
});
|
||||
}
|
||||
|
||||
private _blueprintPicked(ev: CustomEvent) {
|
||||
showAutomationEditor(this, { use_blueprint: { path: ev.detail.value } });
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private _blank() {
|
||||
showAutomationEditor(this);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
.container {
|
||||
display: flex;
|
||||
}
|
||||
ha-card {
|
||||
width: calc(50% - 8px);
|
||||
margin: 4px;
|
||||
}
|
||||
ha-card div {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
ha-card {
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
}
|
||||
ha-blueprint-picker {
|
||||
width: 100%;
|
||||
}
|
||||
.side-by-side {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
}
|
||||
@media all and (max-width: 500px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
ha-card {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-dialog-new-automation": DialogNewAutomation;
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||
import { PaperListboxElement } from "@polymer/paper-listbox";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
@ -36,14 +35,11 @@ import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
||||
import {
|
||||
AutomationConfig,
|
||||
AutomationEntity,
|
||||
Condition,
|
||||
deleteAutomation,
|
||||
getAutomationEditorInitData,
|
||||
showAutomationEditor,
|
||||
Trigger,
|
||||
triggerAutomation,
|
||||
} from "../../../data/automation";
|
||||
import { Action } from "../../../data/script";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
@ -53,7 +49,6 @@ import "../../../layouts/hass-tabs-subpage";
|
||||
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import "../ha-config-section";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "./action/ha-automation-action";
|
||||
@ -61,11 +56,13 @@ import { HaDeviceAction } from "./action/types/ha-automation-action-device_id";
|
||||
import "./condition/ha-automation-condition";
|
||||
import "./trigger/ha-automation-trigger";
|
||||
import { HaDeviceTrigger } from "./trigger/types/ha-automation-trigger-device";
|
||||
|
||||
const MODES = ["single", "restart", "queued", "parallel"];
|
||||
const MODES_MAX = ["queued", "parallel"];
|
||||
import "./manual-automation-editor";
|
||||
import "./blueprint-automation-editor";
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-automation-editor": HaAutomationEditor;
|
||||
}
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"ui-mode-not-available": Error;
|
||||
@ -193,6 +190,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
</ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
|
||||
${this._config
|
||||
? html`
|
||||
${this.narrow
|
||||
@ -204,217 +202,19 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
: ""}
|
||||
${this._mode === "gui"
|
||||
? html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
${!this.narrow
|
||||
? html`
|
||||
<span slot="header">${this._config.alias}</span>
|
||||
`
|
||||
: ""}
|
||||
<span slot="introduction">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.introduction"
|
||||
)}
|
||||
</span>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<paper-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.alias"
|
||||
)}
|
||||
name="alias"
|
||||
.value=${this._config.alias}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.description.label"
|
||||
)}
|
||||
.placeholder=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.description.placeholder"
|
||||
)}
|
||||
name="description"
|
||||
.value=${this._config.description}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-textarea>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.modes.description",
|
||||
"documentation_link",
|
||||
html`<a
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
"/integrations/automation/#automation-modes"
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.modes.documentation"
|
||||
)}</a
|
||||
>`
|
||||
)}
|
||||
</p>
|
||||
<paper-dropdown-menu-light
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.modes.label"
|
||||
)}
|
||||
no-animations
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
.selected=${this._config.mode
|
||||
? MODES.indexOf(this._config.mode)
|
||||
: 0}
|
||||
@iron-select=${this._modeChanged}
|
||||
>
|
||||
${MODES.map(
|
||||
(mode) => html`
|
||||
<paper-item .mode=${mode}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.modes.${mode}`
|
||||
) || mode}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
${this._config.mode &&
|
||||
MODES_MAX.includes(this._config.mode)
|
||||
? html`<paper-input
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.max.${this._config.mode}`
|
||||
)}
|
||||
type="number"
|
||||
name="max"
|
||||
.value=${this._config.max || "10"}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</paper-input>`
|
||||
: html``}
|
||||
</div>
|
||||
${stateObj
|
||||
? html`
|
||||
<div
|
||||
class="card-actions layout horizontal justified center"
|
||||
>
|
||||
<div class="layout horizontal center">
|
||||
<ha-entity-toggle
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
></ha-entity-toggle>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.enable_disable"
|
||||
)}
|
||||
</div>
|
||||
<mwc-button
|
||||
@click=${this._excuteAutomation}
|
||||
.stateObj=${stateObj}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.card.automation.trigger"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.header"
|
||||
)}
|
||||
</span>
|
||||
<span slot="introduction">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.introduction"
|
||||
)}
|
||||
</p>
|
||||
<a
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
"/docs/automation/trigger/"
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.learn_more"
|
||||
)}
|
||||
</a>
|
||||
</span>
|
||||
<ha-automation-trigger
|
||||
.triggers=${this._config.trigger}
|
||||
@value-changed=${this._triggerChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-trigger>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.header"
|
||||
)}
|
||||
</span>
|
||||
<span slot="introduction">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.introduction"
|
||||
)}
|
||||
</p>
|
||||
<a
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
"/docs/scripts/conditions/"
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.learn_more"
|
||||
)}
|
||||
</a>
|
||||
</span>
|
||||
<ha-automation-condition
|
||||
.conditions=${this._config.condition || []}
|
||||
@value-changed=${this._conditionChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-condition>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.header"
|
||||
)}
|
||||
</span>
|
||||
<span slot="introduction">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.introduction"
|
||||
)}
|
||||
</p>
|
||||
<a
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
"/docs/automation/action/"
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.learn_more"
|
||||
)}
|
||||
</a>
|
||||
</span>
|
||||
<ha-automation-action
|
||||
.actions=${this._config.action}
|
||||
@value-changed=${this._actionChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
</ha-config-section>
|
||||
${"use_blueprint" in this._config
|
||||
? html`<blueprint-automation-editor
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.config=${this._config}
|
||||
@value-changed=${this._valueChanged}
|
||||
></blueprint-automation-editor>`
|
||||
: html`<manual-automation-editor
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
.config=${this._config}
|
||||
@value-changed=${this._valueChanged}
|
||||
></manual-automation-editor>`}
|
||||
`
|
||||
: this._mode === "yaml"
|
||||
? html`
|
||||
@ -531,17 +331,25 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
|
||||
if (changedProps.has("automationId") && !this.automationId && this.hass) {
|
||||
const initData = getAutomationEditorInitData();
|
||||
this._dirty = !!initData;
|
||||
this._config = {
|
||||
let baseConfig: Partial<AutomationConfig> = {
|
||||
alias: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.default_name"
|
||||
),
|
||||
description: "",
|
||||
trigger: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }],
|
||||
condition: [],
|
||||
action: [{ ...HaDeviceAction.defaultConfig }],
|
||||
...initData,
|
||||
};
|
||||
if (!initData || !("use_blueprint" in initData)) {
|
||||
baseConfig = {
|
||||
...baseConfig,
|
||||
mode: "single",
|
||||
trigger: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }],
|
||||
condition: [],
|
||||
action: [{ ...HaDeviceAction.defaultConfig }],
|
||||
};
|
||||
}
|
||||
this._config = {
|
||||
...baseConfig,
|
||||
...initData,
|
||||
} as AutomationConfig;
|
||||
}
|
||||
|
||||
if (
|
||||
@ -560,58 +368,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
this._entityId = automation?.entity_id;
|
||||
}
|
||||
|
||||
private _modeChanged(ev: CustomEvent) {
|
||||
const mode = ((ev.target as PaperListboxElement)?.selectedItem as any)
|
||||
?.mode;
|
||||
|
||||
if (mode === this._config!.mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._config = { ...this._config!, mode };
|
||||
if (!MODES_MAX.includes(mode)) {
|
||||
delete this._config.max;
|
||||
}
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
private _valueChanged(ev: CustomEvent<{ value: AutomationConfig }>) {
|
||||
ev.stopPropagation();
|
||||
const target = ev.target as any;
|
||||
const name = target.name;
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
let newVal = ev.detail.value;
|
||||
if (target.type === "number") {
|
||||
newVal = Number(newVal);
|
||||
}
|
||||
if ((this._config![name] || "") === newVal) {
|
||||
return;
|
||||
}
|
||||
this._config = { ...this._config!, [name]: newVal };
|
||||
this._config = ev.detail.value;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _triggerChanged(ev: CustomEvent): void {
|
||||
this._config = { ...this._config!, trigger: ev.detail.value as Trigger[] };
|
||||
this._errors = undefined;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _conditionChanged(ev: CustomEvent): void {
|
||||
this._config = {
|
||||
...this._config!,
|
||||
condition: ev.detail.value as Condition[],
|
||||
};
|
||||
this._errors = undefined;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _actionChanged(ev: CustomEvent): void {
|
||||
this._config = { ...this._config!, action: ev.detail.value as Action[] };
|
||||
this._errors = undefined;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _excuteAutomation(ev: Event) {
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
} from "lit-element";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { formatDateTime } from "../../../common/datetime/format_date_time";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
@ -20,19 +19,16 @@ import { DataTableColumnContainer } from "../../../components/data-table/ha-data
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../components/entity/ha-entity-toggle";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
AutomationConfig,
|
||||
AutomationEntity,
|
||||
showAutomationEditor,
|
||||
triggerAutomation,
|
||||
} from "../../../data/automation";
|
||||
import { AutomationEntity, triggerAutomation } from "../../../data/automation";
|
||||
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showThingtalkDialog } from "./show-dialog-thingtalk";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showNewAutomationDialog } from "./show-dialog-new-automation";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
|
||||
@customElement("ha-automation-picker")
|
||||
class HaAutomationPicker extends LitElement {
|
||||
@ -220,14 +216,14 @@ class HaAutomationPicker extends LitElement {
|
||||
}
|
||||
|
||||
private _createNew() {
|
||||
if (!isComponentLoaded(this.hass, "cloud")) {
|
||||
showAutomationEditor(this);
|
||||
return;
|
||||
if (
|
||||
isComponentLoaded(this.hass, "cloud") ||
|
||||
isComponentLoaded(this.hass, "blueprint")
|
||||
) {
|
||||
showNewAutomationDialog(this);
|
||||
} else {
|
||||
navigate(this, "/config/automation/edit/new");
|
||||
}
|
||||
showThingtalkDialog(this, {
|
||||
callback: (config: Partial<AutomationConfig> | undefined) =>
|
||||
showAutomationEditor(this, config),
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
|
349
src/panels/config/automation/manual-automation-editor.ts
Normal file
349
src/panels/config/automation/manual-automation-editor.ts
Normal file
@ -0,0 +1,349 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
LitElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import { html } from "lit-html";
|
||||
import {
|
||||
Condition,
|
||||
ManualAutomationConfig,
|
||||
Trigger,
|
||||
triggerAutomation,
|
||||
} from "../../../data/automation";
|
||||
import { Action, MODES, MODES_MAX } from "../../../data/script";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import "../ha-config-section";
|
||||
import "../../../components/ha-card";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||
import "../../../components/entity/ha-entity-toggle";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "./trigger/ha-automation-trigger";
|
||||
import "./condition/ha-automation-condition";
|
||||
import "./action/ha-automation-action";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { PaperListboxElement } from "@polymer/paper-listbox";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
|
||||
@customElement("manual-automation-editor")
|
||||
export class HaManualAutomationEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public isWide!: boolean;
|
||||
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
@property() public config!: ManualAutomationConfig;
|
||||
|
||||
@property() public stateObj?: HassEntity;
|
||||
|
||||
protected render() {
|
||||
return html`<ha-config-section .isWide=${this.isWide}>
|
||||
${!this.narrow
|
||||
? html` <span slot="header">${this.config.alias}</span> `
|
||||
: ""}
|
||||
<span slot="introduction">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.introduction"
|
||||
)}
|
||||
</span>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<paper-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.alias"
|
||||
)}
|
||||
name="alias"
|
||||
.value=${this.config.alias}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.description.label"
|
||||
)}
|
||||
.placeholder=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.description.placeholder"
|
||||
)}
|
||||
name="description"
|
||||
.value=${this.config.description}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-textarea>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.modes.description",
|
||||
"documentation_link",
|
||||
html`<a
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
"/integrations/automation/#automation-modes"
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.modes.documentation"
|
||||
)}</a
|
||||
>`
|
||||
)}
|
||||
</p>
|
||||
<paper-dropdown-menu-light
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.modes.label"
|
||||
)}
|
||||
no-animations
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
.selected=${this.config.mode
|
||||
? MODES.indexOf(this.config.mode)
|
||||
: 0}
|
||||
@iron-select=${this._modeChanged}
|
||||
>
|
||||
${MODES.map(
|
||||
(mode) => html`
|
||||
<paper-item .mode=${mode}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.modes.${mode}`
|
||||
) || mode}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu-light>
|
||||
${this.config.mode && MODES_MAX.includes(this.config.mode)
|
||||
? html`<paper-input
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.max.${this.config.mode}`
|
||||
)}
|
||||
type="number"
|
||||
name="max"
|
||||
.value=${this.config.max || "10"}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</paper-input>`
|
||||
: html``}
|
||||
</div>
|
||||
${this.stateObj
|
||||
? html`
|
||||
<div class="card-actions layout horizontal justified center">
|
||||
<div class="layout horizontal center">
|
||||
<ha-entity-toggle
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj!}
|
||||
></ha-entity-toggle>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.enable_disable"
|
||||
)}
|
||||
</div>
|
||||
<mwc-button
|
||||
@click=${this._excuteAutomation}
|
||||
.stateObj=${this.stateObj}
|
||||
>
|
||||
${this.hass.localize("ui.card.automation.trigger")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.header"
|
||||
)}
|
||||
</span>
|
||||
<span slot="introduction">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.introduction"
|
||||
)}
|
||||
</p>
|
||||
<a
|
||||
href="${documentationUrl(this.hass, "/docs/automation/trigger/")}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.learn_more"
|
||||
)}
|
||||
</a>
|
||||
</span>
|
||||
<ha-automation-trigger
|
||||
.triggers=${this.config.trigger}
|
||||
@value-changed=${this._triggerChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-trigger>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.header"
|
||||
)}
|
||||
</span>
|
||||
<span slot="introduction">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.introduction"
|
||||
)}
|
||||
</p>
|
||||
<a
|
||||
href="${documentationUrl(this.hass, "/docs/scripts/conditions/")}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.learn_more"
|
||||
)}
|
||||
</a>
|
||||
</span>
|
||||
<ha-automation-condition
|
||||
.conditions=${this.config.condition || []}
|
||||
@value-changed=${this._conditionChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-condition>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.header"
|
||||
)}
|
||||
</span>
|
||||
<span slot="introduction">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.introduction"
|
||||
)}
|
||||
</p>
|
||||
<a
|
||||
href="${documentationUrl(this.hass, "/docs/automation/action/")}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.learn_more"
|
||||
)}
|
||||
</a>
|
||||
</span>
|
||||
<ha-automation-action
|
||||
.actions=${this.config.action}
|
||||
@value-changed=${this._actionChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
</ha-config-section>`;
|
||||
}
|
||||
|
||||
private _excuteAutomation(ev: Event) {
|
||||
triggerAutomation(this.hass, (ev.target as any).stateObj.entity_id);
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const target = ev.target as any;
|
||||
const name = target.name;
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
let newVal = ev.detail.value;
|
||||
if (target.type === "number") {
|
||||
newVal = Number(newVal);
|
||||
}
|
||||
if ((this.config![name] || "") === newVal) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this.config!, [name]: newVal },
|
||||
});
|
||||
}
|
||||
|
||||
private _modeChanged(ev: CustomEvent) {
|
||||
const mode = ((ev.target as PaperListboxElement)?.selectedItem as any)
|
||||
?.mode;
|
||||
|
||||
if (mode === this.config!.mode) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.config!,
|
||||
mode,
|
||||
max: !MODES_MAX.includes(mode) ? undefined : this.config.max,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _triggerChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this.config!, trigger: ev.detail.value as Trigger[] },
|
||||
});
|
||||
}
|
||||
|
||||
private _conditionChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.config!,
|
||||
condition: ev.detail.value as Condition[],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _actionChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this.config!, action: ev.detail.value as Action[] },
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
.errors {
|
||||
padding: 20px;
|
||||
font-weight: bold;
|
||||
color: var(--error-color);
|
||||
}
|
||||
.content {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
span[slot="introduction"] a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-entity-toggle {
|
||||
margin-right: 8px;
|
||||
}
|
||||
mwc-fab {
|
||||
position: relative;
|
||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
}
|
||||
mwc-fab.dirty {
|
||||
bottom: 0;
|
||||
}
|
||||
.selected_menu_item {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
li[role="separator"] {
|
||||
border-bottom-color: var(--divider-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"manual-automation-editor": HaManualAutomationEditor;
|
||||
}
|
||||
}
|
12
src/panels/config/automation/show-dialog-new-automation.ts
Normal file
12
src/panels/config/automation/show-dialog-new-automation.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
export const loadNewAutomationDialog = () =>
|
||||
import(/* webpackChunkName: "thingtalk-dialog" */ "./dialog-new-automation");
|
||||
|
||||
export const showNewAutomationDialog = (element: HTMLElement): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "ha-dialog-new-automation",
|
||||
dialogImport: loadNewAutomationDialog,
|
||||
dialogParams: {},
|
||||
});
|
||||
};
|
@ -20,7 +20,7 @@ import { convertThingTalk } from "../../../../data/cloud";
|
||||
import type { PolymerChangedEvent } from "../../../../polymer-types";
|
||||
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { ThingtalkDialogParams } from "../show-dialog-thingtalk";
|
||||
import type { ThingtalkDialogParams } from "./show-dialog-thingtalk";
|
||||
import "./ha-thingtalk-placeholders";
|
||||
import type { PlaceholderValues } from "./ha-thingtalk-placeholders";
|
||||
|
||||
@ -50,16 +50,21 @@ class DialogThingtalk extends LitElement {
|
||||
|
||||
@internalProperty() private _placeholders?: PlaceholderContainer;
|
||||
|
||||
@query("#input", true) private _input?: PaperInputElement;
|
||||
@query("#input") private _input?: PaperInputElement;
|
||||
|
||||
private _value!: string;
|
||||
private _value?: string;
|
||||
|
||||
private _config!: Partial<AutomationConfig>;
|
||||
|
||||
public showDialog(params: ThingtalkDialogParams): void {
|
||||
public async showDialog(params: ThingtalkDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
this._error = undefined;
|
||||
this._opened = true;
|
||||
if (params.input) {
|
||||
this._value = params.input;
|
||||
await this.updateComplete;
|
||||
this._generate();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@ -126,6 +131,7 @@ class DialogThingtalk extends LitElement {
|
||||
<paper-input
|
||||
id="input"
|
||||
label="What should this automation do?"
|
||||
.value=${this._value}
|
||||
autofocus
|
||||
@keyup=${this._handleKeyUp}
|
||||
></paper-input>
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { AutomationConfig } from "../../../data/automation";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { AutomationConfig } from "../../../../data/automation";
|
||||
|
||||
export interface ThingtalkDialogParams {
|
||||
callback: (config: Partial<AutomationConfig> | undefined) => void;
|
||||
input?: string;
|
||||
}
|
||||
|
||||
export const loadThingtalkDialog = () =>
|
||||
import(
|
||||
/* webpackChunkName: "thingtalk-dialog" */ "./thingtalk/dialog-thingtalk"
|
||||
);
|
||||
import(/* webpackChunkName: "thingtalk-dialog" */ "./dialog-thingtalk");
|
||||
|
||||
export const showThingtalkDialog = (
|
||||
element: HTMLElement,
|
186
src/panels/config/blueprint/dialog-import-blueprint.ts
Normal file
186
src/panels/config/blueprint/dialog-import-blueprint.ts
Normal file
@ -0,0 +1,186 @@
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||
import "../../../components/ha-circular-progress";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../components/ha-dialog";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import {
|
||||
BlueprintImportResult,
|
||||
importBlueprint,
|
||||
saveBlueprint,
|
||||
} from "../../../data/blueprint";
|
||||
|
||||
@customElement("ha-dialog-import-blueprint")
|
||||
class DialogImportBlueprint extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _params?;
|
||||
|
||||
@internalProperty() private _importing = false;
|
||||
|
||||
@internalProperty() private _saving = false;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
@internalProperty() private _result?: BlueprintImportResult;
|
||||
|
||||
@query("#input") private _input?: PaperInputElement;
|
||||
|
||||
public showDialog(params): void {
|
||||
this._params = params;
|
||||
this._error = undefined;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._error = undefined;
|
||||
this._result = undefined;
|
||||
this._params = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${this.hass.localize("ui.panel.config.blueprint.add.header")}
|
||||
>
|
||||
<div>
|
||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
||||
${this._result
|
||||
? html`${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.import",
|
||||
"name",
|
||||
"domain",
|
||||
html`<b>${this._result.blueprint.metadata.name}</b>`,
|
||||
this._result.blueprint.metadata.domain
|
||||
)}
|
||||
<paper-input
|
||||
id="input"
|
||||
.value=${this._result.suggested_filename}
|
||||
label="Filename"
|
||||
></paper-input>
|
||||
<pre>${this._result.raw_data}</pre>`
|
||||
: html`${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.import_introduction"
|
||||
)}<paper-input
|
||||
id="input"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.url"
|
||||
)}
|
||||
dialogInitialFocus
|
||||
></paper-input>`}
|
||||
</div>
|
||||
${!this._result
|
||||
? html`<mwc-button
|
||||
slot="primaryAction"
|
||||
@click=${this._import}
|
||||
.disabled=${this._importing}
|
||||
>
|
||||
${this._importing
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
size="small"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.importing"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
: ""}
|
||||
${this.hass.localize("ui.panel.config.blueprint.add.import_btn")}
|
||||
</mwc-button>`
|
||||
: html`<mwc-button
|
||||
slot="secondaryAction"
|
||||
@click=${this.closeDialog}
|
||||
.disabled=${this._saving}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</mwc-button>
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
@click=${this._save}
|
||||
.disabled=${this._saving}
|
||||
>
|
||||
${this._saving
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
size="small"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.saving"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
: ""}
|
||||
${this.hass.localize("ui.panel.config.blueprint.add.save_btn")}
|
||||
</mwc-button>`}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _import() {
|
||||
this._importing = true;
|
||||
this._error = undefined;
|
||||
try {
|
||||
const url = this._input?.value;
|
||||
if (!url) {
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.error_no_url"
|
||||
);
|
||||
return;
|
||||
}
|
||||
this._result = await importBlueprint(this.hass, url);
|
||||
} catch (e) {
|
||||
this._error = e.message;
|
||||
} finally {
|
||||
this._importing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async _save() {
|
||||
this._saving = true;
|
||||
try {
|
||||
const filename = this._input?.value;
|
||||
if (!filename) {
|
||||
return;
|
||||
}
|
||||
await saveBlueprint(
|
||||
this.hass,
|
||||
this._result!.blueprint.metadata.domain,
|
||||
filename,
|
||||
this._result!.raw_data,
|
||||
this._result!.url
|
||||
);
|
||||
this._params.importedCallback();
|
||||
this.closeDialog();
|
||||
} catch (e) {
|
||||
this._error = e.message;
|
||||
} finally {
|
||||
this._saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [haStyleDialog, css``];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-dialog-import-blueprint": DialogImportBlueprint;
|
||||
}
|
||||
}
|
212
src/panels/config/blueprint/ha-blueprint-overview.ts
Normal file
212
src/panels/config/blueprint/ha-blueprint-overview.ts
Normal file
@ -0,0 +1,212 @@
|
||||
import "@material/mwc-fab";
|
||||
import "@material/mwc-icon-button";
|
||||
import { mdiPlus, mdiHelpCircle, mdiDelete } from "@mdi/js";
|
||||
import "@polymer/paper-tooltip/paper-tooltip";
|
||||
import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../components/entity/ha-entity-toggle";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import {
|
||||
BlueprintMetaData,
|
||||
Blueprints,
|
||||
deleteBlueprint,
|
||||
} from "../../../data/blueprint";
|
||||
import { showAddBlueprintDialog } from "./show-dialog-import-blueprint";
|
||||
import { showAutomationEditor } from "../../../data/automation";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
interface BlueprintMetaDataPath extends BlueprintMetaData {
|
||||
path: string;
|
||||
}
|
||||
|
||||
const createNewFunctions = {
|
||||
automation: (
|
||||
context: HaBlueprintOverview,
|
||||
blueprintMeta: BlueprintMetaDataPath
|
||||
) => {
|
||||
showAutomationEditor(context, {
|
||||
alias: blueprintMeta.name,
|
||||
use_blueprint: { path: blueprintMeta.path },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@customElement("ha-blueprint-overview")
|
||||
class HaBlueprintOverview extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property() public route!: Route;
|
||||
|
||||
@property() public blueprints!: Blueprints;
|
||||
|
||||
private _processedBlueprints = memoizeOne((blueprints: Blueprints) => {
|
||||
const result = Object.entries(blueprints).map(([path, blueprint]) => ({
|
||||
...blueprint.metadata,
|
||||
path,
|
||||
}));
|
||||
return result;
|
||||
});
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(_language): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = {
|
||||
name: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.headers.name"
|
||||
),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
},
|
||||
};
|
||||
columns.domain = {
|
||||
title: "Domain",
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
width: "20%",
|
||||
};
|
||||
columns.path = {
|
||||
title: "Path",
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
width: "20%",
|
||||
};
|
||||
columns.create = {
|
||||
title: "",
|
||||
type: "icon-button",
|
||||
template: (_, blueprint) => html`<mwc-icon-button
|
||||
.blueprint=${blueprint}
|
||||
@click=${(ev) => this._createNew(ev)}
|
||||
><ha-svg-icon .path=${mdiPlus}></ha-svg-icon
|
||||
></mwc-icon-button>`,
|
||||
};
|
||||
columns.delete = {
|
||||
title: "",
|
||||
type: "icon-button",
|
||||
template: (_, blueprint) => html`<mwc-icon-button
|
||||
.blueprint=${blueprint}
|
||||
@click=${(ev) => this._delete(ev)}
|
||||
><ha-svg-icon .path=${mdiDelete}></ha-svg-icon
|
||||
></mwc-icon-button>`,
|
||||
};
|
||||
return columns;
|
||||
}
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-tabs-subpage-data-table
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
back-path="/config"
|
||||
.route=${this.route}
|
||||
.tabs=${configSections.automation}
|
||||
.columns=${this._columns(this.hass.language)}
|
||||
.data=${this._processedBlueprints(this.blueprints)}
|
||||
id="entity_id"
|
||||
.noDataText=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.no_blueprints"
|
||||
)}
|
||||
hasFab
|
||||
>
|
||||
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
|
||||
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-fab
|
||||
slot="fab"
|
||||
title=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.add_blueprint"
|
||||
)}
|
||||
@click=${this._addBlueprint}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</mwc-fab>
|
||||
</hass-tabs-subpage-data-table>
|
||||
`;
|
||||
}
|
||||
|
||||
private _showHelp() {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.blueprint.caption"),
|
||||
text: html`
|
||||
${this.hass.localize("ui.panel.config.blueprint.overview.introduction")}
|
||||
<p>
|
||||
<a
|
||||
href="${documentationUrl(this.hass, "/docs/blueprint/editor/")}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.learn_more"
|
||||
)}
|
||||
</a>
|
||||
</p>
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
private _addBlueprint() {
|
||||
showAddBlueprintDialog(this, { importedCallback: () => this._reload() });
|
||||
}
|
||||
|
||||
private _reload() {
|
||||
fireEvent(this, "reload-blueprints");
|
||||
}
|
||||
|
||||
private _createNew(ev) {
|
||||
const blueprint = ev.currentTarget.blueprint as BlueprintMetaDataPath;
|
||||
createNewFunctions[blueprint.domain](this, blueprint);
|
||||
}
|
||||
|
||||
private async _delete(ev) {
|
||||
const blueprint = ev.currentTarget.blueprint;
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.confirm_delete_header"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.confirm_delete_text"
|
||||
),
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await deleteBlueprint(this.hass, blueprint.domain, blueprint.path);
|
||||
fireEvent(this, "reload-blueprints");
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return haStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-blueprint-overview": HaBlueprintOverview;
|
||||
}
|
||||
}
|
76
src/panels/config/blueprint/ha-config-blueprint.ts
Normal file
76
src/panels/config/blueprint/ha-config-blueprint.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { customElement, property, PropertyValues } from "lit-element";
|
||||
import {
|
||||
HassRouterPage,
|
||||
RouterOptions,
|
||||
} from "../../../layouts/hass-router-page";
|
||||
import "./ha-blueprint-overview";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { Blueprints, fetchBlueprints } from "../../../data/blueprint";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"reload-blueprints": undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-config-blueprint")
|
||||
class HaConfigBlueprint extends HassRouterPage {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
@property() public isWide!: boolean;
|
||||
|
||||
@property() public showAdvanced!: boolean;
|
||||
|
||||
@property() public blueprints: Blueprints = {};
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
defaultPage: "dashboard",
|
||||
routes: {
|
||||
dashboard: {
|
||||
tag: "ha-blueprint-overview",
|
||||
cache: true,
|
||||
},
|
||||
edit: {
|
||||
tag: "ha-blueprint-editor",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
private async _getBlueprints() {
|
||||
this.blueprints = await fetchBlueprints(this.hass, "automation");
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.addEventListener("reload-blueprints", () => {
|
||||
this._getBlueprints();
|
||||
});
|
||||
this._getBlueprints();
|
||||
}
|
||||
|
||||
protected updatePageEl(pageEl, changedProps: PropertyValues) {
|
||||
pageEl.hass = this.hass;
|
||||
pageEl.narrow = this.narrow;
|
||||
pageEl.isWide = this.isWide;
|
||||
pageEl.route = this.routeTail;
|
||||
pageEl.showAdvanced = this.showAdvanced;
|
||||
pageEl.blueprints = this.blueprints;
|
||||
|
||||
if (
|
||||
(!changedProps || changedProps.has("route")) &&
|
||||
this._currentPage === "edit"
|
||||
) {
|
||||
const blueprintId = this.routeTail.path.substr(1);
|
||||
pageEl.blueprintId = blueprintId === "new" ? null : blueprintId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-blueprint": HaConfigBlueprint;
|
||||
}
|
||||
}
|
17
src/panels/config/blueprint/show-dialog-import-blueprint.ts
Normal file
17
src/panels/config/blueprint/show-dialog-import-blueprint.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
export const loadImportBlueprintDialog = () =>
|
||||
import(
|
||||
/* webpackChunkName: "add-blueprint-dialog" */ "./dialog-import-blueprint"
|
||||
);
|
||||
|
||||
export const showAddBlueprintDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "ha-dialog-import-blueprint",
|
||||
dialogImport: loadImportBlueprintDialog,
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@ -33,6 +33,7 @@ import {
|
||||
mdiMathLog,
|
||||
mdiPencil,
|
||||
mdiNfcVariant,
|
||||
mdiPaletteSwatch,
|
||||
} from "@mdi/js";
|
||||
|
||||
declare global {
|
||||
@ -74,6 +75,12 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
},
|
||||
],
|
||||
automation: [
|
||||
{
|
||||
component: "blueprint",
|
||||
path: "/config/blueprint",
|
||||
translationKey: "ui.panel.config.blueprint.caption",
|
||||
iconPath: mdiPaletteSwatch,
|
||||
},
|
||||
{
|
||||
component: "automation",
|
||||
path: "/config/automation",
|
||||
@ -92,6 +99,8 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
translationKey: "ui.panel.config.script.caption",
|
||||
iconPath: mdiScriptText,
|
||||
},
|
||||
],
|
||||
helpers: [
|
||||
{
|
||||
component: "helpers",
|
||||
path: "/config/helpers",
|
||||
@ -206,6 +215,13 @@ class HaPanelConfig extends HassRouterPage {
|
||||
/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"
|
||||
),
|
||||
},
|
||||
blueprint: {
|
||||
tag: "ha-config-blueprint",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "panel-config-blueprint" */ "./blueprint/ha-config-blueprint"
|
||||
),
|
||||
},
|
||||
tags: {
|
||||
tag: "ha-config-tags",
|
||||
load: () =>
|
||||
|
@ -339,6 +339,11 @@
|
||||
"add_user": "Add user",
|
||||
"remove_user": "Remove user"
|
||||
},
|
||||
"blueprint-picker": {
|
||||
"select_blueprint": "Select a Blueprint",
|
||||
"add_user": "Add user",
|
||||
"remove_user": "Remove user"
|
||||
},
|
||||
"device-picker": {
|
||||
"clear": "Clear",
|
||||
"toggle": "Toggle",
|
||||
@ -1110,6 +1115,19 @@
|
||||
"name": "Name"
|
||||
}
|
||||
},
|
||||
"dialog_new": {
|
||||
"header": "Create a new automation",
|
||||
"how": "How do you want to create your new automation?",
|
||||
|
||||
"blueprint": { "use_blueprint": "Use a blueprint" },
|
||||
"thingtalk": {
|
||||
"header": "Describe the automation you want to create",
|
||||
"intro": "And we will try to create it for you. For example: Turn the lights off when I leave.",
|
||||
"input_label": "What should this automation do?",
|
||||
"create": "Create"
|
||||
},
|
||||
"start_empty": "Start with an empty automation"
|
||||
},
|
||||
"editor": {
|
||||
"enable_disable": "Enable/Disable automation",
|
||||
"introduction": "Use automations to bring your home alive.",
|
||||
@ -1125,6 +1143,14 @@
|
||||
"label": "Description",
|
||||
"placeholder": "Optional description"
|
||||
},
|
||||
"blueprint": {
|
||||
"header": "Blueprint",
|
||||
"blueprint_to_use": "Blueprint to use",
|
||||
"no_blueprints": "You don't have any blueprints",
|
||||
"manage_blueprints": "Manage Blueprints",
|
||||
"inputs": "Inputs",
|
||||
"no_inputs": "This blueprint doesn't have any inputs."
|
||||
},
|
||||
"modes": {
|
||||
"label": "Mode",
|
||||
"description": "The mode controls what happens when the automation is triggered while the actions are still running from a previous trigger. Check the {documentation_link} for more info.",
|
||||
@ -1414,6 +1440,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"blueprint": {
|
||||
"caption": "Blueprints",
|
||||
"description": "Manage blueprints",
|
||||
"overview": {
|
||||
"header": "Blueprint Editor",
|
||||
"introduction": "The blueprint editor allows you to create and edit blueprints.",
|
||||
"learn_more": "Learn more about blueprints",
|
||||
"headers": {
|
||||
"name": "Name"
|
||||
},
|
||||
"confirm_delete_header": "Delete this Blueprint?",
|
||||
"confirm_delete_text": "Are you sure you want to delete this Blueprint"
|
||||
},
|
||||
"add": {
|
||||
"header": "Add new blueprint",
|
||||
"import_header": "Import {name} ({domain})",
|
||||
"import_introduction": "You can import Blueprints of other users from Github and the community forums. Enter the url of the Blueprint below.",
|
||||
"url": "Url of the blueprint",
|
||||
"importing": "Importing blueprint...",
|
||||
"import_btn": "Import blueprint",
|
||||
"saving": "Saving blueprint...",
|
||||
"save_btn": "Save blueprint",
|
||||
"error_no_url": "Please enter the url of the blueprint."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"caption": "Scripts",
|
||||
"description": "Manage scripts",
|
||||
|
Loading…
x
Reference in New Issue
Block a user