mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 03:06:41 +00:00
Convert automation actions/scripts to Lit (#4324)
* Convert automation actions/scripts to Lit * Update ha-automation-action-row.ts * Comments
This commit is contained in:
parent
073428849e
commit
393adacc9e
@ -5,6 +5,7 @@ import {
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { navigate } from "../common/navigate";
|
import { navigate } from "../common/navigate";
|
||||||
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
||||||
|
import { Action } from "./script";
|
||||||
|
|
||||||
export interface AutomationEntity extends HassEntityBase {
|
export interface AutomationEntity extends HassEntityBase {
|
||||||
attributes: HassEntityAttributeBase & {
|
attributes: HassEntityAttributeBase & {
|
||||||
@ -18,7 +19,7 @@ export interface AutomationConfig {
|
|||||||
description: string;
|
description: string;
|
||||||
trigger: Trigger[];
|
trigger: Trigger[];
|
||||||
condition?: Condition[];
|
condition?: Condition[];
|
||||||
action: any[];
|
action: Action[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ForDict {
|
export interface ForDict {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { computeObjectId } from "../common/entity/compute_object_id";
|
import { computeObjectId } from "../common/entity/compute_object_id";
|
||||||
|
import { Condition } from "./automation";
|
||||||
|
|
||||||
export interface EventAction {
|
export interface EventAction {
|
||||||
event: string;
|
event: string;
|
||||||
@ -7,12 +8,40 @@ export interface EventAction {
|
|||||||
event_data_template?: { [key: string]: any };
|
event_data_template?: { [key: string]: any };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ServiceAction {
|
||||||
|
service: string;
|
||||||
|
entity_id?: string;
|
||||||
|
data?: { [key: string]: any };
|
||||||
|
}
|
||||||
|
|
||||||
export interface DeviceAction {
|
export interface DeviceAction {
|
||||||
device_id: string;
|
device_id: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
entity_id: string;
|
entity_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DelayAction {
|
||||||
|
delay: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SceneAction {
|
||||||
|
scene: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WaitAction {
|
||||||
|
wait_template: string;
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Action =
|
||||||
|
| EventAction
|
||||||
|
| DeviceAction
|
||||||
|
| ServiceAction
|
||||||
|
| Condition
|
||||||
|
| DelayAction
|
||||||
|
| SceneAction
|
||||||
|
| WaitAction;
|
||||||
|
|
||||||
export const triggerScript = (
|
export const triggerScript = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityId: string,
|
entityId: string,
|
||||||
|
272
src/panels/config/automation/action/ha-automation-action-row.ts
Normal file
272
src/panels/config/automation/action/ha-automation-action-row.ts
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperListboxElement } from "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import "@polymer/paper-menu-button/paper-menu-button";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
import { Action } from "../../../../data/script";
|
||||||
|
|
||||||
|
import "./types/ha-automation-action-service";
|
||||||
|
import "./types/ha-automation-action-device_id";
|
||||||
|
import "./types/ha-automation-action-delay";
|
||||||
|
import "./types/ha-automation-action-event";
|
||||||
|
import "./types/ha-automation-action-condition";
|
||||||
|
import "./types/ha-automation-action-scene";
|
||||||
|
import "./types/ha-automation-action-wait_template";
|
||||||
|
|
||||||
|
const OPTIONS = [
|
||||||
|
"condition",
|
||||||
|
"delay",
|
||||||
|
"device_id",
|
||||||
|
"event",
|
||||||
|
"scene",
|
||||||
|
"service",
|
||||||
|
"wait_template",
|
||||||
|
];
|
||||||
|
|
||||||
|
const getType = (action: Action) => {
|
||||||
|
return OPTIONS.find((option) => option in action);
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// for fire event
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"move-action": { direction: "up" | "down" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionElement extends LitElement {
|
||||||
|
action: Action;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleChangeEvent = (element: ActionElement, ev: CustomEvent) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const name = (ev.target as any)?.name;
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newVal = ev.detail.value;
|
||||||
|
|
||||||
|
if ((element.action[name] || "") === newVal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newAction: Action;
|
||||||
|
if (!newVal) {
|
||||||
|
newAction = { ...element.action };
|
||||||
|
delete newAction[name];
|
||||||
|
} else {
|
||||||
|
newAction = { ...element.action, [name]: newVal };
|
||||||
|
}
|
||||||
|
fireEvent(element, "value-changed", { value: newAction });
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-row")
|
||||||
|
export default class HaAutomationActionRow extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: Action;
|
||||||
|
@property() public index!: number;
|
||||||
|
@property() public totalActions!: number;
|
||||||
|
@property() private _yamlMode = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const type = getType(this.action);
|
||||||
|
const selected = type ? OPTIONS.indexOf(type) : -1;
|
||||||
|
const yamlMode = this._yamlMode || selected === -1;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-menu">
|
||||||
|
${this.index !== 0
|
||||||
|
? html`
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:arrow-up"
|
||||||
|
@click=${this._moveUp}
|
||||||
|
></paper-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.index !== this.totalActions - 1
|
||||||
|
? html`
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:arrow-down"
|
||||||
|
@click=${this._moveDown}
|
||||||
|
></paper-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<paper-menu-button
|
||||||
|
no-animations
|
||||||
|
horizontal-align="right"
|
||||||
|
horizontal-offset="-5"
|
||||||
|
vertical-offset="-5"
|
||||||
|
close-on-activate
|
||||||
|
>
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:dots-vertical"
|
||||||
|
slot="dropdown-trigger"
|
||||||
|
></paper-icon-button>
|
||||||
|
<paper-listbox slot="dropdown-content">
|
||||||
|
<paper-item
|
||||||
|
@click=${this._switchYamlMode}
|
||||||
|
.disabled=${selected === -1}
|
||||||
|
>
|
||||||
|
${yamlMode
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.edit_ui"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.edit_yaml"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
<paper-item disabled>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.duplicate"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
<paper-item @click=${this._onDelete}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.delete"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-menu-button>
|
||||||
|
</div>
|
||||||
|
${yamlMode
|
||||||
|
? html`
|
||||||
|
<div style="margin-right: 24px;">
|
||||||
|
${selected === -1
|
||||||
|
? html`
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.unsupported_action",
|
||||||
|
"action",
|
||||||
|
type
|
||||||
|
)}
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<ha-yaml-editor
|
||||||
|
.value=${this.action}
|
||||||
|
@value-changed=${this._onYamlChange}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<paper-dropdown-menu-light
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type_select"
|
||||||
|
)}
|
||||||
|
no-animations
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
.selected=${selected}
|
||||||
|
@iron-select=${this._typeChanged}
|
||||||
|
>
|
||||||
|
${OPTIONS.map(
|
||||||
|
(opt) => html`
|
||||||
|
<paper-item .action=${opt}>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.actions.type.${opt}.label`
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu-light>
|
||||||
|
<div>
|
||||||
|
${dynamicElement(`ha-automation-action-${type}`, {
|
||||||
|
hass: this.hass,
|
||||||
|
action: this.action,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _moveUp() {
|
||||||
|
fireEvent(this, "move-action", { direction: "up" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _moveDown() {
|
||||||
|
fireEvent(this, "move-action", { direction: "down" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onDelete() {
|
||||||
|
if (
|
||||||
|
confirm(
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.delete_confirm"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _typeChanged(ev: CustomEvent) {
|
||||||
|
const type = ((ev.target as PaperListboxElement)?.selectedItem as any)
|
||||||
|
?.action;
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type !== getType(this.action)) {
|
||||||
|
const elClass = customElements.get(`ha-automation-action-${type}`);
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...elClass.defaultConfig,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onYamlChange(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", { value: ev.detail.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _switchYamlMode() {
|
||||||
|
this._yamlMode = !this._yamlMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.card-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 3;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.rtl .card-menu {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.card-menu paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-row": HaAutomationActionRow;
|
||||||
|
}
|
||||||
|
}
|
98
src/panels/config/automation/action/ha-automation-action.ts
Normal file
98
src/panels/config/automation/action/ha-automation-action.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import { Action } from "../../../../data/script";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import "./ha-automation-action-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action")
|
||||||
|
export default class HaAutomationAction extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public actions!: Action[];
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
${this.actions.map(
|
||||||
|
(action, idx) => html`
|
||||||
|
<ha-automation-action-row
|
||||||
|
.index=${idx}
|
||||||
|
.totalActions=${this.actions.length}
|
||||||
|
.action=${action}
|
||||||
|
@move-action=${this._move}
|
||||||
|
@value-changed=${this._actionChanged}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></ha-automation-action-row>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-actions add-card">
|
||||||
|
<mwc-button @click=${this._addAction}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.add"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addAction() {
|
||||||
|
const actions = this.actions.concat({
|
||||||
|
service: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", { value: actions });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _move(ev: CustomEvent) {
|
||||||
|
const index = (ev.target as any).index;
|
||||||
|
const newIndex = ev.detail.direction === "up" ? index - 1 : index + 1;
|
||||||
|
const actions = this.actions.concat();
|
||||||
|
const action = actions.splice(index, 1)[0];
|
||||||
|
actions.splice(newIndex, 0, action);
|
||||||
|
fireEvent(this, "value-changed", { value: actions });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _actionChanged(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const actions = [...this.actions];
|
||||||
|
const newValue = ev.detail.value;
|
||||||
|
const index = (ev.target as any).index;
|
||||||
|
|
||||||
|
if (newValue === null) {
|
||||||
|
actions.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
actions[index] = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", { value: actions });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-automation-action-row,
|
||||||
|
ha-card {
|
||||||
|
display: block;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
.add-card mwc-button {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action": HaAutomationAction;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import "../../condition/ha-automation-condition-editor";
|
||||||
|
|
||||||
|
import { LitElement, property, customElement, html } from "lit-element";
|
||||||
|
import { ActionElement } from "../ha-automation-action-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { Condition } from "../../../../../data/automation";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-condition")
|
||||||
|
export class HaConditionAction extends LitElement implements ActionElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: Condition;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { condition: "state" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return html`
|
||||||
|
<ha-automation-condition-editor
|
||||||
|
.condition=${this.action}
|
||||||
|
.hass=${this.hass}
|
||||||
|
@value-changed=${this._conditionChanged}
|
||||||
|
></ha-automation-condition-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _conditionChanged(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: ev.detail.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-condition": HaConditionAction;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "../../../../../components/ha-service-picker";
|
||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
import "../../../../../components/ha-yaml-editor";
|
||||||
|
|
||||||
|
import { LitElement, property, customElement, html } from "lit-element";
|
||||||
|
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { DelayAction } from "../../../../../data/script";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-delay")
|
||||||
|
export class HaDelayAction extends LitElement implements ActionElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: DelayAction;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { delay: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { delay } = this.action;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.delay.delay"
|
||||||
|
)}
|
||||||
|
name="delay"
|
||||||
|
.value=${delay}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-delay": HaDelayAction;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
import "../../../../../components/device/ha-device-picker";
|
||||||
|
import "../../../../../components/device/ha-device-action-picker";
|
||||||
|
import "../../../../../components/ha-form/ha-form";
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchDeviceActionCapabilities,
|
||||||
|
deviceAutomationsEqual,
|
||||||
|
DeviceAction,
|
||||||
|
} from "../../../../../data/device_automation";
|
||||||
|
import { LitElement, customElement, property, html } from "lit-element";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-device_id")
|
||||||
|
export class HaDeviceAction extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: DeviceAction;
|
||||||
|
@property() private _deviceId?: string;
|
||||||
|
@property() private _capabilities?;
|
||||||
|
private _origAction?: DeviceAction;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
device_id: "",
|
||||||
|
domain: "",
|
||||||
|
entity_id: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const deviceId = this._deviceId || this.action.device_id;
|
||||||
|
const extraFieldsData =
|
||||||
|
this._capabilities && this._capabilities.extra_fields
|
||||||
|
? this._capabilities.extra_fields.map((item) => {
|
||||||
|
return { [item.name]: this.action[item.name] };
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-device-picker
|
||||||
|
.value=${deviceId}
|
||||||
|
@value-changed=${this._devicePicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
label="Device"
|
||||||
|
></ha-device-picker>
|
||||||
|
<ha-device-action-picker
|
||||||
|
.value=${this.action}
|
||||||
|
.deviceId=${deviceId}
|
||||||
|
@value-changed=${this._deviceActionPicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
label="Action"
|
||||||
|
></ha-device-action-picker>
|
||||||
|
${extraFieldsData
|
||||||
|
? html`
|
||||||
|
<ha-form
|
||||||
|
.data=${Object.assign({}, ...extraFieldsData)}
|
||||||
|
.schema=${this._capabilities.extra_fields}
|
||||||
|
.computeLabel=${this._extraFieldsComputeLabelCallback(
|
||||||
|
this.hass.localize
|
||||||
|
)}
|
||||||
|
@value-changed=${this._extraFieldsChanged}
|
||||||
|
></ha-form>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated() {
|
||||||
|
if (!this._capabilities) {
|
||||||
|
this._getCapabilities();
|
||||||
|
}
|
||||||
|
if (this.action) {
|
||||||
|
this._origAction = this.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedPros) {
|
||||||
|
const prevAction = changedPros.get("action");
|
||||||
|
if (prevAction && !deviceAutomationsEqual(prevAction, this.action)) {
|
||||||
|
this._getCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getCapabilities() {
|
||||||
|
const action = this.action;
|
||||||
|
|
||||||
|
this._capabilities = action.domain
|
||||||
|
? await fetchDeviceActionCapabilities(this.hass, action)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _devicePicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._deviceId = ev.target.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deviceActionPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
let action = ev.detail.value;
|
||||||
|
if (this._origAction && deviceAutomationsEqual(this._origAction, action)) {
|
||||||
|
action = this._origAction;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", { value: action });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _extraFieldsChanged(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.action,
|
||||||
|
...ev.detail.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _extraFieldsComputeLabelCallback(localize) {
|
||||||
|
// Returns a callback for ha-form to calculate labels per schema object
|
||||||
|
return (schema) =>
|
||||||
|
localize(
|
||||||
|
`ui.panel.config.automation.editor.actions.type.device.extra_fields.${schema.name}`
|
||||||
|
) || schema.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-device_id": HaDeviceAction;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "../../../../../components/ha-service-picker";
|
||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
import "../../../../../components/ha-yaml-editor";
|
||||||
|
|
||||||
|
import { LitElement, property, customElement } from "lit-element";
|
||||||
|
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { html } from "lit-html";
|
||||||
|
import { EventAction } from "../../../../../data/script";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-event")
|
||||||
|
export class HaEventAction extends LitElement implements ActionElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: EventAction;
|
||||||
|
|
||||||
|
public static get defaultConfig(): EventAction {
|
||||||
|
return { event: "", event_data: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { event, event_data } = this.action;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.event.event"
|
||||||
|
)}
|
||||||
|
name="event"
|
||||||
|
.value=${event}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<ha-yaml-editor
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.event.service_data"
|
||||||
|
)}
|
||||||
|
.name=${"event_data"}
|
||||||
|
.value=${event_data}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-event": HaEventAction;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
|
||||||
|
import { LitElement, property, customElement, html } from "lit-element";
|
||||||
|
import { ActionElement } from "../ha-automation-action-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { PolymerChangedEvent } from "../../../../../polymer-types";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { SceneAction } from "../../../../../data/script";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-scene")
|
||||||
|
export class HaSceneAction extends LitElement implements ActionElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: SceneAction;
|
||||||
|
|
||||||
|
public static get defaultConfig(): SceneAction {
|
||||||
|
return { scene: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { scene } = this.action;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-entity-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${scene}
|
||||||
|
@value-changed=${this._entityPicked}
|
||||||
|
.includeDomains=${["scene"]}
|
||||||
|
allow-custom-entity
|
||||||
|
></ha-entity-picker>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityPicked(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.action, scene: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-scene": HaSceneAction;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "../../../../../components/ha-service-picker";
|
||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
import "../../../../../components/ha-yaml-editor";
|
||||||
|
|
||||||
|
import { LitElement, property, customElement } from "lit-element";
|
||||||
|
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { html } from "lit-html";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { computeDomain } from "../../../../../common/entity/compute_domain";
|
||||||
|
import { computeObjectId } from "../../../../../common/entity/compute_object_id";
|
||||||
|
import { PolymerChangedEvent } from "../../../../../polymer-types";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { ServiceAction } from "../../../../../data/script";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-service")
|
||||||
|
export class HaServiceAction extends LitElement implements ActionElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: ServiceAction;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { service: "", data: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getServiceData = memoizeOne((service: string) => {
|
||||||
|
if (!service) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const domain = computeDomain(service);
|
||||||
|
const serviceName = computeObjectId(service);
|
||||||
|
const serviceDomains = this.hass.services;
|
||||||
|
if (!(domain in serviceDomains)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (!(serviceName in serviceDomains[domain])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = serviceDomains[domain][serviceName].fields;
|
||||||
|
return Object.keys(fields).map((field) => {
|
||||||
|
return { key: field, ...fields[field] };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { service, data, entity_id } = this.action;
|
||||||
|
|
||||||
|
const serviceData = this._getServiceData(service);
|
||||||
|
const entity = serviceData.find((attr) => attr.key === "entity_id");
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-service-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${service}
|
||||||
|
@value-changed=${this._serviceChanged}
|
||||||
|
></ha-service-picker>
|
||||||
|
${entity
|
||||||
|
? html`
|
||||||
|
<ha-entity-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${entity_id}
|
||||||
|
.label=${entity.description}
|
||||||
|
@value-changed=${this._entityPicked}
|
||||||
|
.includeDomains=${[computeDomain(service)]}
|
||||||
|
allow-custom-entity
|
||||||
|
></ha-entity-picker>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<ha-yaml-editor
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.service.service_data"
|
||||||
|
)}
|
||||||
|
.name=${"data"}
|
||||||
|
.value=${data}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serviceChanged(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (ev.detail.value === this.action.service) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.action, service: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityPicked(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.action, entity_id: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-service": HaServiceAction;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
|
import { LitElement, property, customElement } from "lit-element";
|
||||||
|
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { html } from "lit-html";
|
||||||
|
import { WaitAction } from "../../../../../data/script";
|
||||||
|
|
||||||
|
@customElement("ha-automation-action-wait_template")
|
||||||
|
export class HaWaitAction extends LitElement implements ActionElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public action!: WaitAction;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { wait_template: "", timeout: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { wait_template, timeout } = this.action;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-textarea
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.wait_template.wait_template"
|
||||||
|
)}
|
||||||
|
name="wait_template"
|
||||||
|
.value=${wait_template}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
dir="ltr"
|
||||||
|
></ha-textarea>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.wait_template.timeout"
|
||||||
|
)}
|
||||||
|
.name=${"timeout"}
|
||||||
|
.value=${timeout}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-action-wait_template": HaWaitAction;
|
||||||
|
}
|
||||||
|
}
|
@ -38,9 +38,6 @@ export default class HaAutomationConditionEditor extends LitElement {
|
|||||||
@property() public yamlMode = false;
|
@property() public yamlMode = false;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.condition) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
const selected = OPTIONS.indexOf(this.condition.condition);
|
const selected = OPTIONS.indexOf(this.condition.condition);
|
||||||
const yamlMode = this.yamlMode || selected === -1;
|
const yamlMode = this.yamlMode || selected === -1;
|
||||||
return html`
|
return html`
|
||||||
|
@ -28,9 +28,8 @@ export class HaDeviceCondition extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (this._deviceId === undefined) {
|
const deviceId = this._deviceId || this.condition.device_id;
|
||||||
this._deviceId = this.condition.device_id;
|
|
||||||
}
|
|
||||||
const extraFieldsData =
|
const extraFieldsData =
|
||||||
this._capabilities && this._capabilities.extra_fields
|
this._capabilities && this._capabilities.extra_fields
|
||||||
? this._capabilities.extra_fields.map((item) => {
|
? this._capabilities.extra_fields.map((item) => {
|
||||||
@ -40,14 +39,14 @@ export class HaDeviceCondition extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-device-picker
|
<ha-device-picker
|
||||||
.value=${this._deviceId}
|
.value=${deviceId}
|
||||||
@value-changed=${this._devicePicked}
|
@value-changed=${this._devicePicked}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
label="Device"
|
label="Device"
|
||||||
></ha-device-picker>
|
></ha-device-picker>
|
||||||
<ha-device-condition-picker
|
<ha-device-condition-picker
|
||||||
.value=${this.condition}
|
.value=${this.condition}
|
||||||
.deviceId=${this._deviceId}
|
.deviceId=${deviceId}
|
||||||
@value-changed=${this._deviceConditionPicked}
|
@value-changed=${this._deviceConditionPicked}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
label="Condition"
|
label="Condition"
|
||||||
|
@ -82,9 +82,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
@property() private _yamlMode = false;
|
@property() private _yamlMode = false;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.trigger) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
const selected = OPTIONS.indexOf(this.trigger.platform);
|
const selected = OPTIONS.indexOf(this.trigger.platform);
|
||||||
const yamlMode = this._yamlMode || selected === -1;
|
const yamlMode = this._yamlMode || selected === -1;
|
||||||
|
|
||||||
|
@ -28,9 +28,8 @@ export class HaDeviceTrigger extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (this._deviceId === undefined) {
|
const deviceId = this._deviceId || this.trigger.device_id;
|
||||||
this._deviceId = this.trigger.device_id;
|
|
||||||
}
|
|
||||||
const extraFieldsData =
|
const extraFieldsData =
|
||||||
this._capabilities && this._capabilities.extra_fields
|
this._capabilities && this._capabilities.extra_fields
|
||||||
? this._capabilities.extra_fields.map((item) => {
|
? this._capabilities.extra_fields.map((item) => {
|
||||||
@ -40,14 +39,14 @@ export class HaDeviceTrigger extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-device-picker
|
<ha-device-picker
|
||||||
.value=${this._deviceId}
|
.value=${deviceId}
|
||||||
@value-changed=${this._devicePicked}
|
@value-changed=${this._devicePicked}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
label="Device"
|
label="Device"
|
||||||
></ha-device-picker>
|
></ha-device-picker>
|
||||||
<ha-device-trigger-picker
|
<ha-device-trigger-picker
|
||||||
.value=${this.trigger}
|
.value=${this.trigger}
|
||||||
.deviceId=${this._deviceId}
|
.deviceId=${deviceId}
|
||||||
@value-changed=${this._deviceTriggerPicked}
|
@value-changed=${this._deviceTriggerPicked}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
label="Trigger"
|
label="Trigger"
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import { h, Component, ComponentChild } from "preact";
|
|
||||||
|
|
||||||
export class AutomationComponent<P = {}, S = {}> extends Component<P, S> {
|
|
||||||
// @ts-ignore
|
|
||||||
protected initialized: boolean;
|
|
||||||
|
|
||||||
constructor(props?, context?) {
|
|
||||||
super(props, context);
|
|
||||||
this.initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount() {
|
|
||||||
this.initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount() {
|
|
||||||
this.initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(_props?, _state?, _context?: any): ComponentChild {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,8 +7,7 @@ import "../../../components/ha-textarea";
|
|||||||
|
|
||||||
import "../automation/trigger/ha-automation-trigger";
|
import "../automation/trigger/ha-automation-trigger";
|
||||||
import "../automation/condition/ha-automation-condition";
|
import "../automation/condition/ha-automation-condition";
|
||||||
|
import "../automation/action/ha-automation-action";
|
||||||
import Script from "./script/index";
|
|
||||||
|
|
||||||
export default class Automation extends Component<any> {
|
export default class Automation extends Component<any> {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -38,8 +37,8 @@ export default class Automation extends Component<any> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public actionChanged(action) {
|
public actionChanged(ev: CustomEvent) {
|
||||||
this.props.onChange({ ...this.props.automation, action });
|
this.props.onChange({ ...this.props.automation, action: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
public render({ automation, isWide, hass, localize }) {
|
public render({ automation, isWide, hass, localize }) {
|
||||||
@ -144,11 +143,10 @@ export default class Automation extends Component<any> {
|
|||||||
{localize("ui.panel.config.automation.editor.actions.learn_more")}
|
{localize("ui.panel.config.automation.editor.actions.learn_more")}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<Script
|
<ha-automation-action
|
||||||
script={action}
|
actions={action}
|
||||||
onChange={this.actionChanged}
|
onvalue-changed={this.actionChanged}
|
||||||
hass={hass}
|
hass={hass}
|
||||||
localize={localize}
|
|
||||||
/>
|
/>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
import { h, Component } from "preact";
|
|
||||||
import "../../../components/ha-textarea";
|
|
||||||
|
|
||||||
export default class JSONTextArea extends Component<any, any> {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isvalid: true,
|
|
||||||
value: JSON.stringify(props.value || {}, null, 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onChange = this.onChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onChange(ev) {
|
|
||||||
const value = ev.target.value;
|
|
||||||
let parsed;
|
|
||||||
let isValid;
|
|
||||||
|
|
||||||
try {
|
|
||||||
parsed = JSON.parse(value);
|
|
||||||
isValid = true;
|
|
||||||
} catch (err) {
|
|
||||||
// Invalid JSON
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
value,
|
|
||||||
isValid,
|
|
||||||
});
|
|
||||||
if (isValid) {
|
|
||||||
this.props.onChange(parsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillReceiveProps({ value }) {
|
|
||||||
if (value === this.props.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
value: JSON.stringify(value, null, 2),
|
|
||||||
isValid: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ label }, { value, isValid }) {
|
|
||||||
const style: any = {
|
|
||||||
minWidth: 300,
|
|
||||||
width: "100%",
|
|
||||||
};
|
|
||||||
if (!isValid) {
|
|
||||||
style.border = "1px solid red";
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ha-textarea
|
|
||||||
label={label}
|
|
||||||
value={value}
|
|
||||||
style={style}
|
|
||||||
onvalue-changed={this.onChange}
|
|
||||||
dir="ltr"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -26,6 +26,7 @@ declare global {
|
|||||||
"ha-automation-trigger": any;
|
"ha-automation-trigger": any;
|
||||||
"ha-automation-condition": any;
|
"ha-automation-condition": any;
|
||||||
"ha-automation-condition-editor": any;
|
"ha-automation-condition-editor": any;
|
||||||
|
"ha-automation-action": any;
|
||||||
"ha-device-trigger-picker": any;
|
"ha-device-trigger-picker": any;
|
||||||
"ha-device-action-picker": any;
|
"ha-device-action-picker": any;
|
||||||
"ha-form": any;
|
"ha-form": any;
|
||||||
|
@ -4,7 +4,7 @@ import "@polymer/paper-input/paper-input";
|
|||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
|
||||||
import Script from "./script/index";
|
import "../automation/action/ha-automation-action";
|
||||||
|
|
||||||
export default class ScriptEditor extends Component<{
|
export default class ScriptEditor extends Component<{
|
||||||
onChange: (...args: any[]) => any;
|
onChange: (...args: any[]) => any;
|
||||||
@ -27,8 +27,8 @@ export default class ScriptEditor extends Component<{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public sequenceChanged(sequence) {
|
public sequenceChanged(ev: CustomEvent) {
|
||||||
this.props.onChange({ ...this.props.script, sequence });
|
this.props.onChange({ ...this.props.script, sequence: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -68,11 +68,10 @@ export default class ScriptEditor extends Component<{
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<Script
|
<ha-automation-action
|
||||||
script={sequence}
|
actions={sequence}
|
||||||
onChange={this.sequenceChanged}
|
onvalue-changed={this.sequenceChanged}
|
||||||
hass={hass}
|
hass={hass}
|
||||||
localize={localize}
|
|
||||||
/>
|
/>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
import { h, Component } from "preact";
|
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
|
|
||||||
import YAMLTextArea from "../yaml_textarea";
|
|
||||||
|
|
||||||
import CallServiceAction from "./call_service";
|
|
||||||
import ConditionAction from "./condition";
|
|
||||||
import DelayAction from "./delay";
|
|
||||||
import DeviceAction from "./device";
|
|
||||||
import EventAction from "./event";
|
|
||||||
import SceneAction from "./scene";
|
|
||||||
import WaitAction from "./wait";
|
|
||||||
|
|
||||||
const TYPES = {
|
|
||||||
service: CallServiceAction,
|
|
||||||
delay: DelayAction,
|
|
||||||
wait_template: WaitAction,
|
|
||||||
condition: ConditionAction,
|
|
||||||
event: EventAction,
|
|
||||||
device_id: DeviceAction,
|
|
||||||
scene: SceneAction,
|
|
||||||
};
|
|
||||||
|
|
||||||
const OPTIONS = Object.keys(TYPES).sort();
|
|
||||||
|
|
||||||
function getType(action) {
|
|
||||||
const keys = Object.keys(TYPES);
|
|
||||||
// tslint:disable-next-line: prefer-for-of
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
if (keys[i] in action) {
|
|
||||||
return keys[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Action extends Component<any> {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.typeChanged = this.typeChanged.bind(this);
|
|
||||||
this.onYamlChange = this.onYamlChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public typeChanged(ev) {
|
|
||||||
const newType = ev.target.selectedItem.attributes.action.value;
|
|
||||||
const oldType = getType(this.props.action);
|
|
||||||
|
|
||||||
if (oldType !== newType) {
|
|
||||||
this.props.onChange(this.props.index, TYPES[newType].defaultConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ index, action, onChange, hass, localize, yamlMode }) {
|
|
||||||
const type = getType(action);
|
|
||||||
// tslint:disable-next-line: variable-name
|
|
||||||
const Comp = type && TYPES[type];
|
|
||||||
// @ts-ignore
|
|
||||||
const selected = OPTIONS.indexOf(type);
|
|
||||||
|
|
||||||
if (yamlMode || !Comp) {
|
|
||||||
return (
|
|
||||||
<div style="margin-right: 24px;">
|
|
||||||
{!Comp && (
|
|
||||||
<div>
|
|
||||||
{localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.unsupported_action",
|
|
||||||
"action",
|
|
||||||
type
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<YAMLTextArea value={action} onChange={this.onYamlChange} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<paper-dropdown-menu-light
|
|
||||||
label={localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type_select"
|
|
||||||
)}
|
|
||||||
no-animations
|
|
||||||
>
|
|
||||||
<paper-listbox
|
|
||||||
slot="dropdown-content"
|
|
||||||
selected={selected}
|
|
||||||
oniron-select={this.typeChanged}
|
|
||||||
>
|
|
||||||
{OPTIONS.map((opt) => (
|
|
||||||
<paper-item action={opt}>
|
|
||||||
{localize(
|
|
||||||
`ui.panel.config.automation.editor.actions.type.${opt}.label`
|
|
||||||
)}
|
|
||||||
</paper-item>
|
|
||||||
))}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu-light>
|
|
||||||
<Comp
|
|
||||||
index={index}
|
|
||||||
action={action}
|
|
||||||
onChange={onChange}
|
|
||||||
hass={hass}
|
|
||||||
localize={localize}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onYamlChange(condition) {
|
|
||||||
this.props.onChange(this.props.index, condition);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
import { h, Component } from "preact";
|
|
||||||
import "@polymer/paper-menu-button/paper-menu-button";
|
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
|
|
||||||
import ActionEdit from "./action_edit";
|
|
||||||
|
|
||||||
export default class Action extends Component<any> {
|
|
||||||
public state: { yamlMode: boolean };
|
|
||||||
private moveUp: (event: Event) => void;
|
|
||||||
private moveDown: (event: Event) => void;
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
yamlMode: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onDelete = this.onDelete.bind(this);
|
|
||||||
this.switchYamlMode = this.switchYamlMode.bind(this);
|
|
||||||
this.moveUp = props.moveUp.bind(this, props.index);
|
|
||||||
this.moveDown = props.moveDown.bind(this, props.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onDelete() {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (
|
|
||||||
confirm(
|
|
||||||
this.props.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.delete_confirm"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.props.onChange(this.props.index, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(props, { yamlMode }) {
|
|
||||||
return (
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-content">
|
|
||||||
<div class="card-menu" style="z-index: 3">
|
|
||||||
{props.index !== 0 && (
|
|
||||||
<paper-icon-button icon="hass:arrow-up" onTap={this.moveUp} />
|
|
||||||
)}
|
|
||||||
{props.index !== props.length - 1 && (
|
|
||||||
<paper-icon-button icon="hass:arrow-down" onTap={this.moveDown} />
|
|
||||||
)}
|
|
||||||
<paper-menu-button
|
|
||||||
no-animations
|
|
||||||
horizontal-align="right"
|
|
||||||
horizontal-offset="-5"
|
|
||||||
vertical-offset="-5"
|
|
||||||
close-on-activate
|
|
||||||
>
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:dots-vertical"
|
|
||||||
slot="dropdown-trigger"
|
|
||||||
/>
|
|
||||||
<paper-listbox slot="dropdown-content">
|
|
||||||
<paper-item onTap={this.switchYamlMode}>
|
|
||||||
{yamlMode
|
|
||||||
? props.localize(
|
|
||||||
"ui.panel.config.automation.editor.edit_ui"
|
|
||||||
)
|
|
||||||
: props.localize(
|
|
||||||
"ui.panel.config.automation.editor.edit_yaml"
|
|
||||||
)}
|
|
||||||
</paper-item>
|
|
||||||
<paper-item disabled>
|
|
||||||
{props.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.duplicate"
|
|
||||||
)}
|
|
||||||
</paper-item>
|
|
||||||
<paper-item onTap={this.onDelete}>
|
|
||||||
{props.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.delete"
|
|
||||||
)}
|
|
||||||
</paper-item>
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-menu-button>
|
|
||||||
</div>
|
|
||||||
<ActionEdit {...props} yamlMode={yamlMode} />
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private switchYamlMode() {
|
|
||||||
this.setState({
|
|
||||||
yamlMode: !this.state.yamlMode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
import { h } from "preact";
|
|
||||||
import "../../../../components/ha-service-picker";
|
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
|
||||||
|
|
||||||
import YAMLTextArea from "../yaml_textarea";
|
|
||||||
import { AutomationComponent } from "../automation-component";
|
|
||||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
|
||||||
import { computeObjectId } from "../../../../common/entity/compute_object_id";
|
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
|
|
||||||
export default class CallServiceAction extends AutomationComponent<any> {
|
|
||||||
private _getServiceData = memoizeOne((service: string) => {
|
|
||||||
if (!service) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const domain = computeDomain(service);
|
|
||||||
const serviceName = computeObjectId(service);
|
|
||||||
const serviceDomains = this.props.hass.services;
|
|
||||||
if (!(domain in serviceDomains)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
if (!(serviceName in serviceDomains[domain])) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const fields = serviceDomains[domain][serviceName].fields;
|
|
||||||
return Object.keys(fields).map((field) => {
|
|
||||||
return { key: field, ...fields[field] };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.serviceChanged = this.serviceChanged.bind(this);
|
|
||||||
this.entityChanged = this.entityChanged.bind(this);
|
|
||||||
this.serviceDataChanged = this.serviceDataChanged.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public serviceChanged(ev) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newAction = {
|
|
||||||
...this.props.action,
|
|
||||||
service: ev.target.value,
|
|
||||||
};
|
|
||||||
if (
|
|
||||||
computeDomain(this.props.action.service) !==
|
|
||||||
computeDomain(ev.target.value)
|
|
||||||
) {
|
|
||||||
delete newAction.entity_id;
|
|
||||||
}
|
|
||||||
this.props.onChange(this.props.index, newAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public entityChanged(ev) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onChange(this.props.index, {
|
|
||||||
...this.props.action,
|
|
||||||
entity_id: ev.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public serviceDataChanged(data) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onChange(this.props.index, { ...this.props.action, data });
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ action, hass, localize }) {
|
|
||||||
const { service, data, entity_id } = action;
|
|
||||||
const serviceData = this._getServiceData(service);
|
|
||||||
const entity = serviceData.find((attr) => attr.key === "entity_id");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ha-service-picker
|
|
||||||
hass={hass}
|
|
||||||
value={service}
|
|
||||||
onChange={this.serviceChanged}
|
|
||||||
/>
|
|
||||||
{entity && (
|
|
||||||
<ha-entity-picker
|
|
||||||
hass={hass}
|
|
||||||
value={entity_id}
|
|
||||||
label={entity.description}
|
|
||||||
onChange={this.entityChanged}
|
|
||||||
includeDomains={[computeDomain(service)]}
|
|
||||||
allow-custom-entity
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<YAMLTextArea
|
|
||||||
label={localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.service.service_data"
|
|
||||||
)}
|
|
||||||
value={data}
|
|
||||||
onChange={this.serviceDataChanged}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(CallServiceAction as any).defaultConfig = {
|
|
||||||
alias: "",
|
|
||||||
service: "",
|
|
||||||
data: {},
|
|
||||||
};
|
|
@ -1,32 +0,0 @@
|
|||||||
import { h, Component } from "preact";
|
|
||||||
|
|
||||||
import "../../automation/condition/ha-automation-condition-editor";
|
|
||||||
|
|
||||||
export default class ConditionAction extends Component<any> {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.conditionChanged = this.conditionChanged.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public conditionChanged(ev) {
|
|
||||||
this.props.onChange(this.props.index, ev.detail.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
public render({ action, hass }) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ha-automation-condition-editor
|
|
||||||
condition={action}
|
|
||||||
onvalue-changed={this.conditionChanged}
|
|
||||||
hass={hass}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(ConditionAction as any).defaultConfig = {
|
|
||||||
condition: "state",
|
|
||||||
};
|
|
@ -1,45 +0,0 @@
|
|||||||
import { h } from "preact";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { AutomationComponent } from "../automation-component";
|
|
||||||
|
|
||||||
export default class DelayAction extends AutomationComponent<any> {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.onChange = this.onChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ action, localize }) {
|
|
||||||
const { delay } = action;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<paper-input
|
|
||||||
label={localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.delay.delay"
|
|
||||||
)}
|
|
||||||
name="delay"
|
|
||||||
value={delay}
|
|
||||||
onvalue-changed={this.onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onChange(ev) {
|
|
||||||
if (
|
|
||||||
!this.initialized ||
|
|
||||||
ev.target.value === this.props.action[ev.target.name]
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.onChange(this.props.index, {
|
|
||||||
...this.props.action,
|
|
||||||
[ev.target.name]: ev.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(DelayAction as any).defaultConfig = {
|
|
||||||
delay: "",
|
|
||||||
};
|
|
@ -1,144 +0,0 @@
|
|||||||
import { h } from "preact";
|
|
||||||
|
|
||||||
import "../../../../components/device/ha-device-picker";
|
|
||||||
import "../../../../components/device/ha-device-action-picker";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
|
||||||
import { AutomationComponent } from "../automation-component";
|
|
||||||
|
|
||||||
import {
|
|
||||||
fetchDeviceActionCapabilities,
|
|
||||||
deviceAutomationsEqual,
|
|
||||||
} from "../../../../data/device_automation";
|
|
||||||
import { DeviceAction } from "../../../../data/script";
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
|
||||||
|
|
||||||
export default class DeviceActionEditor extends AutomationComponent<
|
|
||||||
{
|
|
||||||
index: number;
|
|
||||||
action: DeviceAction;
|
|
||||||
hass: HomeAssistant;
|
|
||||||
onChange(index: number, action: DeviceAction);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device_id: string | undefined;
|
|
||||||
capabilities: any | undefined;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
public static defaultConfig: DeviceAction = {
|
|
||||||
device_id: "",
|
|
||||||
domain: "",
|
|
||||||
entity_id: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
private _origAction;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.devicePicked = this.devicePicked.bind(this);
|
|
||||||
this.deviceActionPicked = this.deviceActionPicked.bind(this);
|
|
||||||
this._extraFieldsChanged = this._extraFieldsChanged.bind(this);
|
|
||||||
this.state = { device_id: undefined, capabilities: undefined };
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
const { action, hass } = this.props;
|
|
||||||
const deviceId = this.state.device_id || action.device_id;
|
|
||||||
const capabilities = this.state.capabilities;
|
|
||||||
const extraFieldsData =
|
|
||||||
capabilities && capabilities.extra_fields
|
|
||||||
? capabilities.extra_fields.map((item) => {
|
|
||||||
return { [item.name]: this.props.action[item.name] };
|
|
||||||
})
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ha-device-picker
|
|
||||||
value={deviceId}
|
|
||||||
onChange={this.devicePicked}
|
|
||||||
hass={hass}
|
|
||||||
label="Device"
|
|
||||||
/>
|
|
||||||
<ha-device-action-picker
|
|
||||||
value={action}
|
|
||||||
deviceId={deviceId}
|
|
||||||
onChange={this.deviceActionPicked}
|
|
||||||
hass={hass}
|
|
||||||
label="Action"
|
|
||||||
/>
|
|
||||||
{extraFieldsData && (
|
|
||||||
<ha-form
|
|
||||||
data={Object.assign({}, ...extraFieldsData)}
|
|
||||||
onvalue-changed={this._extraFieldsChanged}
|
|
||||||
schema={this.state.capabilities.extra_fields}
|
|
||||||
computeLabel={this._extraFieldsComputeLabelCallback(hass.localize)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount() {
|
|
||||||
this.initialized = true;
|
|
||||||
if (!this.state.capabilities) {
|
|
||||||
this._getCapabilities();
|
|
||||||
}
|
|
||||||
if (this.props.action) {
|
|
||||||
this._origAction = this.props.action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidUpdate(prevProps) {
|
|
||||||
if (!deviceAutomationsEqual(prevProps.action, this.props.action)) {
|
|
||||||
this._getCapabilities();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private devicePicked(ev) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({ ...this.state, device_id: ev.target.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
private deviceActionPicked(ev) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let deviceAction = ev.target.value;
|
|
||||||
if (
|
|
||||||
this._origAction &&
|
|
||||||
deviceAutomationsEqual(this._origAction, deviceAction)
|
|
||||||
) {
|
|
||||||
deviceAction = this._origAction;
|
|
||||||
}
|
|
||||||
this.props.onChange(this.props.index, deviceAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getCapabilities() {
|
|
||||||
const action = this.props.action;
|
|
||||||
|
|
||||||
const capabilities = action.domain
|
|
||||||
? await fetchDeviceActionCapabilities(this.props.hass, action)
|
|
||||||
: null;
|
|
||||||
this.setState({ ...this.state, capabilities });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _extraFieldsChanged(ev) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onChange(this.props.index, {
|
|
||||||
...this.props.action,
|
|
||||||
...ev.detail.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _extraFieldsComputeLabelCallback(localize) {
|
|
||||||
// Returns a callback for ha-form to calculate labels per schema object
|
|
||||||
return (schema) =>
|
|
||||||
localize(
|
|
||||||
`ui.panel.config.automation.editor.actions.type.device_id.extra_fields.${schema.name}`
|
|
||||||
) || schema.name;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
import { h } from "preact";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
|
|
||||||
import YAMLTextArea from "../yaml_textarea";
|
|
||||||
import { onChangeEvent } from "../../../../common/preact/event";
|
|
||||||
import { LocalizeFunc } from "../../../../common/translations/localize";
|
|
||||||
import { EventAction } from "../../../../data/script";
|
|
||||||
|
|
||||||
import { AutomationComponent } from "../automation-component";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
index: number;
|
|
||||||
action: EventAction;
|
|
||||||
localize: LocalizeFunc;
|
|
||||||
onChange: (index: number, action: EventAction) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class EventActionForm extends AutomationComponent<Props> {
|
|
||||||
private onChange: (event: Event) => void;
|
|
||||||
|
|
||||||
static get defaultConfig(): EventAction {
|
|
||||||
return {
|
|
||||||
event: "",
|
|
||||||
event_data: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.onChange = onChangeEvent.bind(this, "action");
|
|
||||||
this.serviceDataChanged = this.serviceDataChanged.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
const {
|
|
||||||
action: { event, event_data },
|
|
||||||
localize,
|
|
||||||
} = this.props;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<paper-input
|
|
||||||
label={localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.event.event"
|
|
||||||
)}
|
|
||||||
name="event"
|
|
||||||
value={event}
|
|
||||||
onvalue-changed={this.onChange}
|
|
||||||
/>
|
|
||||||
<YAMLTextArea
|
|
||||||
label={localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.event.service_data"
|
|
||||||
)}
|
|
||||||
value={event_data}
|
|
||||||
onChange={this.serviceDataChanged}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private serviceDataChanged(eventData) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onChange(this.props.index, {
|
|
||||||
...this.props.action,
|
|
||||||
event_data: eventData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
import { h, Component } from "preact";
|
|
||||||
import "@material/mwc-button";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
|
|
||||||
import ActionRow from "./action_row";
|
|
||||||
|
|
||||||
export default class Script extends Component<any> {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.addAction = this.addAction.bind(this);
|
|
||||||
this.actionChanged = this.actionChanged.bind(this);
|
|
||||||
this.moveUp = this.moveUp.bind(this);
|
|
||||||
this.moveDown = this.moveDown.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public addAction() {
|
|
||||||
const script = this.props.script.concat({
|
|
||||||
service: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onChange(script);
|
|
||||||
}
|
|
||||||
|
|
||||||
public actionChanged(index, newValue) {
|
|
||||||
const script = this.props.script.concat();
|
|
||||||
|
|
||||||
if (newValue === null) {
|
|
||||||
script.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
script[index] = newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.onChange(script);
|
|
||||||
}
|
|
||||||
|
|
||||||
public moveUp(index: number) {
|
|
||||||
this.move(index, index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public moveDown(index: number) {
|
|
||||||
this.move(index, index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ script, hass, localize }) {
|
|
||||||
return (
|
|
||||||
<div class="script">
|
|
||||||
{script.map((act, idx) => (
|
|
||||||
<ActionRow
|
|
||||||
index={idx}
|
|
||||||
length={script.length}
|
|
||||||
action={act}
|
|
||||||
onChange={this.actionChanged}
|
|
||||||
moveUp={this.moveUp}
|
|
||||||
moveDown={this.moveDown}
|
|
||||||
hass={hass}
|
|
||||||
localize={localize}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-actions add-card">
|
|
||||||
<mwc-button onTap={this.addAction}>
|
|
||||||
{localize("ui.panel.config.automation.editor.actions.add")}
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private move(index: number, newIndex: number) {
|
|
||||||
const script = this.props.script.concat();
|
|
||||||
const action = script.splice(index, 1)[0];
|
|
||||||
script.splice(newIndex, 0, action);
|
|
||||||
this.props.onChange(script);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import { h } from "preact";
|
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
|
||||||
import { AutomationComponent } from "../automation-component";
|
|
||||||
|
|
||||||
export default class SceneAction extends AutomationComponent<any> {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.sceneChanged = this.sceneChanged.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sceneChanged(ev: any) {
|
|
||||||
this.props.onChange(this.props.index, {
|
|
||||||
...this.props.action,
|
|
||||||
scene: ev.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ action, hass }) {
|
|
||||||
const { scene } = action;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ha-entity-picker
|
|
||||||
value={scene}
|
|
||||||
onChange={this.sceneChanged}
|
|
||||||
hass={hass}
|
|
||||||
includeDomains={["scene"]}
|
|
||||||
allowCustomEntity
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(SceneAction as any).defaultConfig = {
|
|
||||||
scene: "",
|
|
||||||
};
|
|
@ -1,60 +0,0 @@
|
|||||||
import { h } from "preact";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
|
|
||||||
import "../../../../components/ha-textarea";
|
|
||||||
|
|
||||||
import { onChangeEvent } from "../../../../common/preact/event";
|
|
||||||
import { AutomationComponent } from "../automation-component";
|
|
||||||
|
|
||||||
export default class WaitAction extends AutomationComponent<any> {
|
|
||||||
private onChange: (obj: any) => void;
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.onChange = onChangeEvent.bind(this, "action");
|
|
||||||
this.onTemplateChange = this.onTemplateChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets fired on mount. If empty, onChangeEvent removes attribute.
|
|
||||||
// Without the attribute this action is no longer matched to this component.
|
|
||||||
public onTemplateChange(ev) {
|
|
||||||
if (!this.initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onChange(this.props.index, {
|
|
||||||
...this.props.action,
|
|
||||||
[ev.target.getAttribute("name")]: ev.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ action, localize }) {
|
|
||||||
/* eslint-disable camelcase */
|
|
||||||
const { wait_template, timeout } = action;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ha-textarea
|
|
||||||
label={localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.wait_template.wait_template"
|
|
||||||
)}
|
|
||||||
name="wait_template"
|
|
||||||
value={wait_template}
|
|
||||||
onvalue-changed={this.onTemplateChange}
|
|
||||||
dir="ltr"
|
|
||||||
/>
|
|
||||||
<paper-input
|
|
||||||
label={localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.wait_template.timeout"
|
|
||||||
)}
|
|
||||||
name="timeout"
|
|
||||||
value={timeout}
|
|
||||||
onvalue-changed={this.onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(WaitAction as any).defaultConfig = {
|
|
||||||
wait_template: "",
|
|
||||||
timeout: "",
|
|
||||||
};
|
|
@ -1,80 +0,0 @@
|
|||||||
import { h, Component } from "preact";
|
|
||||||
import { safeDump, safeLoad } from "js-yaml";
|
|
||||||
import "../../../components/ha-code-editor";
|
|
||||||
|
|
||||||
const isEmpty = (obj: object) => {
|
|
||||||
for (const key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class YAMLTextArea extends Component<any, any> {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
let value: string | undefined;
|
|
||||||
try {
|
|
||||||
value =
|
|
||||||
props.value && !isEmpty(props.value)
|
|
||||||
? safeDump(props.value)
|
|
||||||
: undefined;
|
|
||||||
} catch (err) {
|
|
||||||
alert(`There was an error converting to YAML: ${err}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isvalid: true,
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onChange = this.onChange.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onChange(ev) {
|
|
||||||
const value = ev.detail.value;
|
|
||||||
let parsed;
|
|
||||||
let isValid = true;
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
try {
|
|
||||||
parsed = safeLoad(value);
|
|
||||||
isValid = true;
|
|
||||||
} catch (err) {
|
|
||||||
// Invalid YAML
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parsed = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
value,
|
|
||||||
isValid,
|
|
||||||
});
|
|
||||||
if (isValid) {
|
|
||||||
this.props.onChange(parsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public render({ label }, { value, isValid }) {
|
|
||||||
const style: any = {
|
|
||||||
minWidth: 300,
|
|
||||||
width: "100%",
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{label && <p>{label}</p>}
|
|
||||||
<ha-code-editor
|
|
||||||
mode="yaml"
|
|
||||||
style={style}
|
|
||||||
value={value}
|
|
||||||
error={isValid === false}
|
|
||||||
onvalue-changed={this.onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user