mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 03:36:44 +00:00
Add blueprint scripts (#9504)
This commit is contained in:
parent
54c64c15f3
commit
371804591d
@ -21,7 +21,9 @@ export interface ScriptEntity extends HassEntityBase {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ScriptConfig {
|
||||
export type ScriptConfig = ManualScriptConfig | BlueprintScriptConfig;
|
||||
|
||||
export interface ManualScriptConfig {
|
||||
alias: string;
|
||||
sequence: Action | Action[];
|
||||
icon?: string;
|
||||
@ -29,7 +31,7 @@ export interface ScriptConfig {
|
||||
max?: number;
|
||||
}
|
||||
|
||||
export interface BlueprintScriptConfig extends ScriptConfig {
|
||||
export interface BlueprintScriptConfig extends ManualScriptConfig {
|
||||
use_blueprint: { path: string; input?: BlueprintInput };
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
Blueprints,
|
||||
deleteBlueprint,
|
||||
} from "../../../data/blueprint";
|
||||
import { showScriptEditor } from "../../../data/script";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
@ -52,6 +53,12 @@ const createNewFunctions = {
|
||||
use_blueprint: { path: blueprintMeta.path },
|
||||
});
|
||||
},
|
||||
script: (blueprintMeta: BlueprintMetaDataPath) => {
|
||||
showScriptEditor({
|
||||
alias: blueprintMeta.name,
|
||||
use_blueprint: { path: blueprintMeta.path },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@customElement("ha-blueprint-overview")
|
||||
@ -62,27 +69,38 @@ class HaBlueprintOverview extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property() public route!: Route;
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property() public blueprints!: Blueprints;
|
||||
@property({ attribute: false }) public blueprints!: Record<
|
||||
string,
|
||||
Blueprints
|
||||
>;
|
||||
|
||||
private _processedBlueprints = memoizeOne((blueprints: Blueprints) => {
|
||||
const result = Object.entries(blueprints).map(([path, blueprint]) => {
|
||||
private _processedBlueprints = memoizeOne(
|
||||
(blueprints: Record<string, Blueprints>) => {
|
||||
const result: any[] = [];
|
||||
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
|
||||
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
|
||||
if ("error" in blueprint) {
|
||||
return {
|
||||
result.push({
|
||||
name: blueprint.error,
|
||||
type,
|
||||
error: true,
|
||||
path,
|
||||
};
|
||||
}
|
||||
return {
|
||||
});
|
||||
} else {
|
||||
result.push({
|
||||
...blueprint.metadata,
|
||||
type,
|
||||
error: false,
|
||||
path,
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow, _language): DataTableColumnContainer => ({
|
||||
@ -102,6 +120,20 @@ class HaBlueprintOverview extends LitElement {
|
||||
`
|
||||
: undefined,
|
||||
},
|
||||
type: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.headers.type"
|
||||
),
|
||||
template: (type: string) =>
|
||||
html`${this.hass.localize(
|
||||
`ui.panel.config.blueprint.overview.types.${type}`
|
||||
)}`,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
hidden: narrow,
|
||||
direction: "asc",
|
||||
width: "10%",
|
||||
},
|
||||
path: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.headers.file_name"
|
||||
@ -114,6 +146,7 @@ class HaBlueprintOverview extends LitElement {
|
||||
},
|
||||
create: {
|
||||
title: "",
|
||||
width: narrow ? undefined : "20%",
|
||||
type: narrow ? "icon-button" : undefined,
|
||||
template: (_, blueprint: any) =>
|
||||
blueprint.error
|
||||
@ -122,17 +155,18 @@ class HaBlueprintOverview extends LitElement {
|
||||
? html`<ha-icon-button
|
||||
.blueprint=${blueprint}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.use_blueprint"
|
||||
`ui.panel.config.blueprint.overview.create_${blueprint.domain}`
|
||||
)}
|
||||
.path=${mdiRobot}
|
||||
@click=${this._createNew}
|
||||
></ha-icon-button>`
|
||||
.path=${mdiRobot}
|
||||
>
|
||||
</ha-icon-button>`
|
||||
: html`<mwc-button
|
||||
.blueprint=${blueprint}
|
||||
@click=${this._createNew}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.blueprint.overview.use_blueprint"
|
||||
`ui.panel.config.blueprint.overview.create_${blueprint.domain}`
|
||||
)}
|
||||
</mwc-button>`,
|
||||
},
|
||||
|
@ -25,7 +25,7 @@ class HaConfigBlueprint extends HassRouterPage {
|
||||
|
||||
@property() public showAdvanced!: boolean;
|
||||
|
||||
@property() public blueprints: Blueprints = {};
|
||||
@property() public blueprints: Record<string, Blueprints> = {};
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
defaultPage: "dashboard",
|
||||
@ -41,7 +41,11 @@ class HaConfigBlueprint extends HassRouterPage {
|
||||
};
|
||||
|
||||
private async _getBlueprints() {
|
||||
this.blueprints = await fetchBlueprints(this.hass, "automation");
|
||||
const [automation, script] = await Promise.all([
|
||||
fetchBlueprints(this.hass, "automation"),
|
||||
fetchBlueprints(this.hass, "script"),
|
||||
]);
|
||||
this.blueprints = { automation, script };
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
|
205
src/panels/config/script/blueprint-script-editor.ts
Normal file
205
src/panels/config/script/blueprint-script-editor.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-blueprint-picker";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-circular-progress";
|
||||
import "../../../components/ha-markdown";
|
||||
import "../../../components/ha-selector/ha-selector";
|
||||
import "../../../components/ha-settings-row";
|
||||
|
||||
import {
|
||||
BlueprintOrError,
|
||||
Blueprints,
|
||||
fetchBlueprints,
|
||||
} from "../../../data/blueprint";
|
||||
import { BlueprintScriptConfig } from "../../../data/script";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
|
||||
@customElement("blueprint-script-editor")
|
||||
export class HaBlueprintScriptEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@property({ reflect: true, type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ attribute: false }) public config!: BlueprintScriptConfig;
|
||||
|
||||
@state() private _blueprints?: Blueprints;
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._getBlueprints();
|
||||
}
|
||||
|
||||
private get _blueprint(): BlueprintOrError | 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 vertical .isWide=${this.isWide}>
|
||||
<span slot="header"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.header"
|
||||
)}</span
|
||||
>
|
||||
<ha-card>
|
||||
<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>`}
|
||||
</div>
|
||||
|
||||
${this.config.use_blueprint.path
|
||||
? blueprint && "error" in blueprint
|
||||
? html`<p class="warning padding">
|
||||
There is an error in this Blueprint: ${blueprint.error}
|
||||
</p>`
|
||||
: html`${blueprint?.metadata.description
|
||||
? html`<ha-markdown
|
||||
class="card-content"
|
||||
breaks
|
||||
.content=${blueprint.metadata.description}
|
||||
></ha-markdown>`
|
||||
: ""}
|
||||
${blueprint?.metadata?.input &&
|
||||
Object.keys(blueprint.metadata.input).length
|
||||
? Object.entries(blueprint.metadata.input).map(
|
||||
([key, value]) =>
|
||||
html`<ha-settings-row .narrow=${this.narrow}>
|
||||
<span slot="heading">${value?.name || key}</span>
|
||||
<span slot="description">${value?.description}</span>
|
||||
${value?.selector
|
||||
? html`<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${value.selector}
|
||||
.key=${key}
|
||||
.value=${(this.config.use_blueprint.input &&
|
||||
this.config.use_blueprint.input[key]) ??
|
||||
value?.default}
|
||||
@value-changed=${this._inputChanged}
|
||||
></ha-selector>`
|
||||
: html`<paper-input
|
||||
.key=${key}
|
||||
required
|
||||
.value=${(this.config.use_blueprint.input &&
|
||||
this.config.use_blueprint.input[key]) ??
|
||||
value?.default}
|
||||
@value-changed=${this._inputChanged}
|
||||
no-label-float
|
||||
></paper-input>`}
|
||||
</ha-settings-row>`
|
||||
)
|
||||
: html`<p class="padding">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.no_inputs"
|
||||
)}
|
||||
</p>`}`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>`;
|
||||
}
|
||||
|
||||
private async _getBlueprints() {
|
||||
this._blueprints = await fetchBlueprints(this.hass, "script");
|
||||
}
|
||||
|
||||
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 = ev.detail.value;
|
||||
if (
|
||||
(this.config.use_blueprint.input &&
|
||||
this.config.use_blueprint.input[key] === value) ||
|
||||
(!this.config.use_blueprint.input && value === "")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const input = { ...this.config.use_blueprint.input, [key]: value };
|
||||
|
||||
if (value === "" || value === undefined) {
|
||||
delete input[key];
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.config,
|
||||
use_blueprint: {
|
||||
...this.config.use_blueprint,
|
||||
input,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.padding {
|
||||
padding: 16px;
|
||||
}
|
||||
.blueprint-picker-container {
|
||||
padding: 16px;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-settings-row {
|
||||
--paper-time-input-justify-content: flex-end;
|
||||
border-top: 1px solid var(--divider-color);
|
||||
}
|
||||
:host(:not([narrow])) ha-settings-row paper-input {
|
||||
width: 60%;
|
||||
}
|
||||
:host(:not([narrow])) ha-settings-row ha-selector {
|
||||
width: 60%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"blueprint-script-editor": HaBlueprintScriptEditor;
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ import {
|
||||
Action,
|
||||
deleteScript,
|
||||
getScriptEditorInitData,
|
||||
ManualScriptConfig,
|
||||
MODES,
|
||||
MODES_MAX,
|
||||
ScriptConfig,
|
||||
@ -55,6 +56,7 @@ import "../automation/action/ha-automation-action";
|
||||
import { HaDeviceAction } from "../automation/action/types/ha-automation-action-device_id";
|
||||
import "../ha-config-section";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "./blueprint-script-editor";
|
||||
|
||||
export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -236,7 +238,9 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
>
|
||||
</paper-input>`
|
||||
: ""}
|
||||
<p>
|
||||
${"use_blueprint" in this._config
|
||||
? ""
|
||||
: html`<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.modes.description",
|
||||
"documentation_link",
|
||||
@ -289,7 +293,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</paper-input>`
|
||||
: html``}
|
||||
: html``} `}
|
||||
</div>
|
||||
${this.scriptEntityId
|
||||
? html`
|
||||
@ -323,7 +327,18 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
|
||||
<ha-config-section vertical .isWide=${this.isWide}>
|
||||
${"use_blueprint" in this._config
|
||||
? html`<blueprint-script-editor
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.config=${this._config}
|
||||
@value-changed=${this._configChanged}
|
||||
></blueprint-script-editor>`
|
||||
: html`<ha-config-section
|
||||
vertical
|
||||
.isWide=${this.isWide}
|
||||
>
|
||||
<span slot="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.sequence"
|
||||
@ -353,7 +368,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
@value-changed=${this._sequenceChanged}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
</ha-config-section>
|
||||
</ha-config-section>`}
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
@ -427,7 +442,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
(!oldScript || oldScript !== this.scriptEntityId)
|
||||
) {
|
||||
this.hass
|
||||
.callApi<ScriptConfig>(
|
||||
.callApi<ManualScriptConfig>(
|
||||
"GET",
|
||||
`config/script/config/${computeObjectId(this.scriptEntityId)}`
|
||||
)
|
||||
@ -466,11 +481,16 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
) {
|
||||
const initData = getScriptEditorInitData();
|
||||
this._dirty = !!initData;
|
||||
this._config = {
|
||||
const baseConfig: Partial<ScriptConfig> = {
|
||||
alias: this.hass.localize("ui.panel.config.script.editor.default_name"),
|
||||
sequence: [{ ...HaDeviceAction.defaultConfig }],
|
||||
...initData,
|
||||
};
|
||||
if (!initData || !("use_blueprint" in initData)) {
|
||||
baseConfig.sequence = [{ ...HaDeviceAction.defaultConfig }];
|
||||
}
|
||||
this._config = {
|
||||
...baseConfig,
|
||||
...initData,
|
||||
} as ScriptConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,6 +568,11 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _configChanged(ev) {
|
||||
this._config = ev.detail.value;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _sequenceChanged(ev: CustomEvent): void {
|
||||
this._config = { ...this._config!, sequence: ev.detail.value as Action[] };
|
||||
this._errors = undefined;
|
||||
@ -749,3 +774,9 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
}
|
||||
|
||||
customElements.define("ha-script-editor", HaScriptEditor);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-script-editor": HaScriptEditor;
|
||||
}
|
||||
}
|
||||
|
@ -1794,13 +1794,18 @@
|
||||
"learn_more": "Learn more about using blueprints",
|
||||
"headers": {
|
||||
"name": "Name",
|
||||
"domain": "Domain",
|
||||
"type": "Type",
|
||||
"file_name": "File name"
|
||||
},
|
||||
"types": {
|
||||
"automation": "Automation",
|
||||
"script": "Script"
|
||||
},
|
||||
"confirm_delete_header": "Delete this blueprint?",
|
||||
"confirm_delete_text": "Are you sure you want to delete this blueprint?",
|
||||
"add_blueprint": "Import blueprint",
|
||||
"use_blueprint": "Create automation",
|
||||
"create_automation": "Create automation",
|
||||
"create_script": "Create script",
|
||||
"delete_blueprint": "Delete blueprint",
|
||||
"share_blueprint": "Share blueprint",
|
||||
"share_blueprint_no_url": "Unable to share blueprint: no source url",
|
||||
|
Loading…
x
Reference in New Issue
Block a user