From ff3087c39ccdcb6bab364c38abe91d73caf1241b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 4 Dec 2019 18:57:47 +0100 Subject: [PATCH] Convert automation trigger to litelement (#4315) * Convert automation trigger to Lit * Update ha-automation-trigger-row.ts * dynamicContentDirective * update * Lint * Implement other types --- src/common/dom/dynamic-content-directive.ts | 29 ++ .../device/ha-device-automation-picker.ts | 1 + src/components/ha-yaml-editor.ts | 81 ++++ src/data/device_automation.ts | 2 +- .../trigger/ha-automation-trigger-row.ts | 345 ++++++++++++++++++ .../trigger/ha-automation-trigger.ts | 93 +++++ .../types/ha-automation-trigger-device.ts | 128 +++++++ .../types/ha-automation-trigger-event.ts | 53 +++ .../ha-automation-trigger-geo_location.ts | 99 +++++ .../ha-automation-trigger-homeassistant.ts | 63 ++++ .../types/ha-automation-trigger-mqtt.ts | 50 +++ .../ha-automation-trigger-numeric_state.ts | 103 ++++++ .../types/ha-automation-trigger-state.ts | 93 +++++ .../types/ha-automation-trigger-sun.ts | 75 ++++ .../types/ha-automation-trigger-template.ts | 36 ++ .../types/ha-automation-trigger-time.ts | 36 ++ .../ha-automation-trigger-time_pattern.ts | 58 +++ .../types/ha-automation-trigger-webhook.ts | 43 +++ .../types/ha-automation-trigger-zone.ts | 108 ++++++ src/panels/config/js/automation.tsx | 14 +- src/panels/config/js/preact-types.ts | 1 + src/panels/config/js/trigger/device.tsx | 133 ------- src/panels/config/js/trigger/event.tsx | 54 --- src/panels/config/js/trigger/geo_location.tsx | 88 ----- .../config/js/trigger/homeassistant.tsx | 58 --- src/panels/config/js/trigger/index.tsx | 59 --- src/panels/config/js/trigger/mqtt.tsx | 44 --- .../config/js/trigger/numeric_state.tsx | 90 ----- src/panels/config/js/trigger/state.tsx | 81 ---- src/panels/config/js/trigger/sun.tsx | 71 ---- src/panels/config/js/trigger/template.tsx | 37 -- src/panels/config/js/trigger/time.tsx | 36 -- src/panels/config/js/trigger/time_pattern.tsx | 50 --- src/panels/config/js/trigger/trigger_edit.tsx | 120 ------ src/panels/config/js/trigger/trigger_row.tsx | 86 ----- src/panels/config/js/trigger/webhook.tsx | 35 -- src/panels/config/js/trigger/zone.tsx | 109 ------ 37 files changed, 1503 insertions(+), 1159 deletions(-) create mode 100644 src/common/dom/dynamic-content-directive.ts create mode 100644 src/components/ha-yaml-editor.ts create mode 100644 src/panels/config/automation/trigger/ha-automation-trigger-row.ts create mode 100644 src/panels/config/automation/trigger/ha-automation-trigger.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-sun.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-template.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-webhook.ts create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-zone.ts delete mode 100644 src/panels/config/js/trigger/device.tsx delete mode 100644 src/panels/config/js/trigger/event.tsx delete mode 100644 src/panels/config/js/trigger/geo_location.tsx delete mode 100644 src/panels/config/js/trigger/homeassistant.tsx delete mode 100644 src/panels/config/js/trigger/index.tsx delete mode 100644 src/panels/config/js/trigger/mqtt.tsx delete mode 100644 src/panels/config/js/trigger/numeric_state.tsx delete mode 100644 src/panels/config/js/trigger/state.tsx delete mode 100644 src/panels/config/js/trigger/sun.tsx delete mode 100644 src/panels/config/js/trigger/template.tsx delete mode 100644 src/panels/config/js/trigger/time.tsx delete mode 100644 src/panels/config/js/trigger/time_pattern.tsx delete mode 100644 src/panels/config/js/trigger/trigger_edit.tsx delete mode 100644 src/panels/config/js/trigger/trigger_row.tsx delete mode 100644 src/panels/config/js/trigger/webhook.tsx delete mode 100644 src/panels/config/js/trigger/zone.tsx diff --git a/src/common/dom/dynamic-content-directive.ts b/src/common/dom/dynamic-content-directive.ts new file mode 100644 index 0000000000..31a8a1ead7 --- /dev/null +++ b/src/common/dom/dynamic-content-directive.ts @@ -0,0 +1,29 @@ +import { directive, Part, NodePart } from "lit-html"; + +export const dynamicContentDirective = directive( + (tag: string, properties: { [key: string]: any }) => (part: Part): void => { + if (!(part instanceof NodePart)) { + throw new Error( + "dynamicContentDirective can only be used in content bindings" + ); + } + + let element = part.value as HTMLElement | undefined; + + if ( + element !== undefined && + tag.toUpperCase() === (element as HTMLElement).tagName + ) { + Object.entries(properties).forEach(([key, value]) => { + element![key] = value; + }); + return; + } + + element = document.createElement(tag); + Object.entries(properties).forEach(([key, value]) => { + element![key] = value; + }); + part.setValue(element); + } +); diff --git a/src/components/device/ha-device-automation-picker.ts b/src/components/device/ha-device-automation-picker.ts index 98c5ce492a..34eacfaa25 100644 --- a/src/components/device/ha-device-automation-picker.ts +++ b/src/components/device/ha-device-automation-picker.ts @@ -176,6 +176,7 @@ export abstract class HaDeviceAutomationPicker< this.value = automation; setTimeout(() => { fireEvent(this, "change"); + fireEvent(this, "value-changed", { value: automation }); }, 0); } diff --git a/src/components/ha-yaml-editor.ts b/src/components/ha-yaml-editor.ts new file mode 100644 index 0000000000..dc3a0cb08e --- /dev/null +++ b/src/components/ha-yaml-editor.ts @@ -0,0 +1,81 @@ +import { safeDump, safeLoad } from "js-yaml"; +import "./ha-code-editor"; +import { LitElement, property, customElement, html } from "lit-element"; +import { fireEvent } from "../common/dom/fire_event"; + +const isEmpty = (obj: object) => { + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + return false; + } + } + return true; +}; + +@customElement("ha-yaml-editor") +export class HaYamlEditor extends LitElement { + @property() public value?: any; + @property() public isValid = true; + @property() public label?: string; + @property() private _yaml?: string; + + protected firstUpdated() { + try { + this._yaml = + this.value && !isEmpty(this.value) ? safeDump(this.value) : ""; + } catch (err) { + alert(`There was an error converting to YAML: ${err}`); + } + } + + protected render() { + if (this._yaml === undefined) { + return; + } + return html` + ${this.label + ? html` +

${this.label}

+ ` + : ""} + + `; + } + + private _onChange(ev: CustomEvent) { + ev.stopPropagation(); + 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.value = parsed; + this.isValid = isValid; + + if (isValid) { + fireEvent(this, "value-changed", { value: parsed }); + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-yaml-editor": HaYamlEditor; + } +} diff --git a/src/data/device_automation.ts b/src/data/device_automation.ts index 2653171643..bc58f1b00a 100644 --- a/src/data/device_automation.ts +++ b/src/data/device_automation.ts @@ -18,7 +18,7 @@ export interface DeviceCondition extends DeviceAutomation { } export interface DeviceTrigger extends DeviceAutomation { - platform: string; + platform: "device"; } export const fetchDeviceActions = (hass: HomeAssistant, deviceId: string) => diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts new file mode 100644 index 0000000000..ea5b52b387 --- /dev/null +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -0,0 +1,345 @@ +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 { dynamicContentDirective } from "../../../../common/dom/dynamic-content-directive"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-card"; +import { HomeAssistant } from "../../../../types"; + +import "./types/ha-automation-trigger-device"; +import "./types/ha-automation-trigger-event"; +import "./types/ha-automation-trigger-state"; +import "./types/ha-automation-trigger-geo_location"; +import "./types/ha-automation-trigger-homeassistant"; +import "./types/ha-automation-trigger-mqtt"; +import "./types/ha-automation-trigger-numeric_state"; +import "./types/ha-automation-trigger-sun"; +import "./types/ha-automation-trigger-template"; +import "./types/ha-automation-trigger-time"; +import "./types/ha-automation-trigger-time_pattern"; +import "./types/ha-automation-trigger-webhook"; +import "./types/ha-automation-trigger-zone"; +import { DeviceTrigger } from "../../../../data/device_automation"; + +const OPTIONS = [ + "device", + "event", + "state", + "geo_location", + "homeassistant", + "mqtt", + "numeric_state", + "sun", + "template", + "time", + "time_pattern", + "webhook", + "zone", +]; + +export interface ForDict { + hours?: number | string; + minutes?: number | string; + seconds?: number | string; +} + +export interface StateTrigger { + platform: "state"; + entity_id: string; + from?: string | number; + to?: string | number; + for?: string | number | ForDict; +} + +export interface MqttTrigger { + platform: "mqtt"; + topic: string; + payload?: string; +} + +export interface GeoLocationTrigger { + platform: "geo_location"; + source: "string"; + zone: "string"; + event: "enter" | "leave"; +} + +export interface HassTrigger { + platform: "homeassistant"; + event: "start" | "shutdown"; +} + +export interface NumericStateTrigger { + platform: "numeric_state"; + entity_id: string; + above?: number; + below?: number; + value_template?: string; + for?: string | number | ForDict; +} + +export interface SunTrigger { + platform: "sun"; + offset: number; + event: "sunrise" | "sunset"; +} + +export interface TimePatternTrigger { + platform: "time_pattern"; + hours?: number | string; + minutes?: number | string; + seconds?: number | string; +} + +export interface WebhookTrigger { + platform: "webhook"; + webhook_id: string; +} + +export interface ZoneTrigger { + platform: "zone"; + entity_id: string; + zone: string; + event: "enter" | "leave"; +} + +export interface TimeTrigger { + platform: "time"; + at: string; +} + +export interface TemplateTrigger { + platform: "template"; + value_template: string; +} + +export interface EventTrigger { + platform: "event"; + event_type: string; + event_data: any; +} + +export type Trigger = + | StateTrigger + | MqttTrigger + | GeoLocationTrigger + | HassTrigger + | NumericStateTrigger + | SunTrigger + | TimePatternTrigger + | WebhookTrigger + | ZoneTrigger + | TimeTrigger + | TemplateTrigger + | EventTrigger + | DeviceTrigger; + +export interface TriggerElement extends LitElement { + trigger: Trigger; +} + +export const handleChangeEvent = (element: TriggerElement, ev: CustomEvent) => { + ev.stopPropagation(); + const name = (ev.target as any)?.name; + if (!name) { + return; + } + const newVal = ev.detail.value; + + if ((element.trigger[name] || "") === newVal) { + return; + } + + let newTrigger: Trigger; + if (!newVal) { + newTrigger = { ...element.trigger }; + delete newTrigger[name]; + } else { + newTrigger = { ...element.trigger, [name]: newVal }; + } + fireEvent(element, "value-changed", { value: newTrigger }); +}; + +@customElement("ha-automation-trigger-row") +export default class HaAutomationTriggerRow extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: Trigger; + @property() private _yamlMode = false; + + protected render() { + if (!this.trigger) { + return html``; + } + const hasEditor = OPTIONS.includes(this.trigger.platform); + if (!hasEditor) { + this._yamlMode = true; + } + const selected = OPTIONS.indexOf(this.trigger.platform); + return html` + +
+
+ + + + + ${this._yamlMode + ? this.hass.localize( + "ui.panel.config.automation.editor.edit_ui" + ) + : this.hass.localize( + "ui.panel.config.automation.editor.edit_yaml" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.duplicate" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.delete" + )} + + + +
+ ${this._yamlMode + ? html` +
+ ${!hasEditor + ? html` + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.unsupported_platform", + "platform", + this.trigger.platform + )} + ` + : ""} + +
+ ` + : html` + + + ${OPTIONS.map( + (opt) => html` + + ${this.hass.localize( + `ui.panel.config.automation.editor.triggers.type.${opt}.label` + )} + + ` + )} + + +
+ ${dynamicContentDirective( + `ha-automation-trigger-${this.trigger.platform}`, + { hass: this.hass, trigger: this.trigger } + )} +
+ `} +
+
+ `; + } + + private _onDelete() { + if ( + confirm( + this.hass.localize( + "ui.panel.config.automation.editor.triggers.delete_confirm" + ) + ) + ) { + fireEvent(this, "value-changed", { value: null }); + } + } + + private _typeChanged(ev: CustomEvent) { + const type = ((ev.target as PaperListboxElement)?.selectedItem as any) + ?.platform; + + if (!type) { + return; + } + + const elClass = customElements.get(`ha-automation-trigger-${type}`); + + if (type !== this.trigger.platform) { + fireEvent(this, "value-changed", { + value: { + platform: type, + ...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-trigger-row": HaAutomationTriggerRow; + } +} diff --git a/src/panels/config/automation/trigger/ha-automation-trigger.ts b/src/panels/config/automation/trigger/ha-automation-trigger.ts new file mode 100644 index 0000000000..108c4c0248 --- /dev/null +++ b/src/panels/config/automation/trigger/ha-automation-trigger.ts @@ -0,0 +1,93 @@ +import { + LitElement, + customElement, + html, + property, + CSSResult, + css, +} from "lit-element"; +import "@material/mwc-button"; +import "../../../../components/ha-card"; + +import { fireEvent } from "../../../../common/dom/fire_event"; +import { HomeAssistant } from "../../../../types"; + +import "./ha-automation-trigger-row"; + +@customElement("ha-automation-trigger") +export default class HaAutomationTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public triggers; + + protected render() { + return html` +
+ ${this.triggers.map( + (trg, idx) => html` + + ` + )} + +
+ + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.add" + )} + +
+
+
+ `; + } + + private _addTrigger() { + const triggers = this.triggers.concat({ + platform: "state", + }); + + fireEvent(this, "value-changed", { value: triggers }); + } + + private _triggerChanged(ev: CustomEvent) { + ev.stopPropagation(); + const triggers = [...this.triggers]; + const newValue = ev.detail.value; + const index = (ev.target as any).index; + + if (newValue === null) { + triggers.splice(index, 1); + } else { + triggers[index] = newValue; + } + + fireEvent(this, "value-changed", { value: triggers }); + } + + static get styles(): CSSResult { + return css` + .triggers, + .script { + margin-top: -16px; + } + .triggers ha-card, + .script ha-card { + margin-top: 16px; + } + .add-card mwc-button { + display: block; + text-align: center; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger": HaAutomationTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts new file mode 100644 index 0000000000..8ea5a96cfe --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts @@ -0,0 +1,128 @@ +import "../../../../../components/device/ha-device-picker"; +import "../../../../../components/device/ha-device-trigger-picker"; +import "../../../../../components/ha-form/ha-form"; + +import { + fetchDeviceTriggerCapabilities, + deviceAutomationsEqual, + DeviceTrigger, +} 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-trigger-device") +export class HaDeviceTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: DeviceTrigger; + @property() private _deviceId?: string; + @property() private _capabilities?; + private _origTrigger?: DeviceTrigger; + + public static get defaultConfig() { + return { + device_id: "", + domain: "", + entity_id: "", + }; + } + + protected render() { + if (this._deviceId === undefined) { + this._deviceId = this.trigger.device_id; + } + const extraFieldsData = + this._capabilities && this._capabilities.extra_fields + ? this._capabilities.extra_fields.map((item) => { + return { [item.name]: this.trigger[item.name] }; + }) + : undefined; + + return html` + + + ${extraFieldsData + ? html` + + ` + : ""} + `; + } + + protected firstUpdated() { + if (!this._capabilities) { + this._getCapabilities(); + } + if (this.trigger) { + this._origTrigger = this.trigger; + } + } + + protected updated(changedPros) { + const prevTrigger = changedPros.get("trigger"); + if (prevTrigger && !deviceAutomationsEqual(prevTrigger, this.trigger)) { + this._getCapabilities(); + } + } + + private async _getCapabilities() { + const trigger = this.trigger; + + this._capabilities = trigger.domain + ? await fetchDeviceTriggerCapabilities(this.hass, trigger) + : null; + } + + private _devicePicked(ev) { + ev.stopPropagation(); + this._deviceId = ev.target.value; + } + + private _deviceTriggerPicked(ev) { + ev.stopPropagation(); + let trigger = ev.detail.value; + if ( + this._origTrigger && + deviceAutomationsEqual(this._origTrigger, trigger) + ) { + trigger = this._origTrigger; + } + fireEvent(this, "value-changed", { value: trigger }); + } + + private _extraFieldsChanged(ev) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { + ...this.trigger, + ...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.triggers.type.device.extra_fields.${schema.name}` + ) || schema.name; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts new file mode 100644 index 0000000000..130a5f4e5b --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts @@ -0,0 +1,53 @@ +import "@polymer/paper-input/paper-input"; +import "../../../../../components/ha-yaml-editor"; + +import { LitElement, property, customElement } from "lit-element"; +import { + TriggerElement, + EventTrigger, + handleChangeEvent, +} from "../ha-automation-trigger-row"; +import { HomeAssistant } from "../../../../../types"; +import { html } from "lit-html"; + +@customElement("ha-automation-trigger-event") +export class HaEventTrigger extends LitElement implements TriggerElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: EventTrigger; + + public static get defaultConfig() { + return { event_type: "", event_data: {} }; + } + + public render() { + const { event_type, event_data } = this.trigger; + return html` + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-event": HaEventTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location.ts new file mode 100644 index 0000000000..491bcb9d91 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location.ts @@ -0,0 +1,99 @@ +import "@polymer/paper-radio-button/paper-radio-button"; +import "@polymer/paper-radio-group/paper-radio-group"; +// tslint:disable-next-line +import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group"; +import "../../../../../components/entity/ha-entity-picker"; +import { LitElement, customElement, property, html } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { + GeoLocationTrigger, + handleChangeEvent, +} from "../ha-automation-trigger-row"; +import { fireEvent } from "../../../../../common/dom/fire_event"; + +@customElement("ha-automation-trigger-geo_location") +export default class HaGeolocationTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: GeoLocationTrigger; + + public static get defaultConfig() { + return { + source: "", + zone: "", + event: "enter", + }; + } + + protected render() { + const { source, zone, event } = this.trigger; + + return html` + + + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.geo_location.enter" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.geo_location.leave" + )} + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } + + private _zonePicked(ev: CustomEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.trigger, zone: ev.detail.value }, + }); + } + + private _radioGroupPicked(ev: CustomEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { + ...this.trigger, + event: (ev.target as PaperRadioGroupElement).selected, + }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-geo_location": HaGeolocationTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant.ts new file mode 100644 index 0000000000..328df5ae7d --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant.ts @@ -0,0 +1,63 @@ +import "@polymer/paper-radio-button/paper-radio-button"; +import "@polymer/paper-radio-group/paper-radio-group"; +// tslint:disable-next-line +import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group"; +import { LitElement, html, property, customElement } from "lit-element"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { HomeAssistant } from "../../../../../types"; +import { HassTrigger } from "../ha-automation-trigger-row"; + +@customElement("ha-automation-trigger-homeassistant") +export default class HaHassTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: HassTrigger; + + public static get defaultConfig() { + return { + event: "start", + }; + } + + public render() { + const { event } = this.trigger; + return html` + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.homeassistant.start" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.homeassistant.shutdown" + )} + + + `; + } + + private _radioGroupPicked(ev) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { + ...this.trigger, + event: (ev.target as PaperRadioGroupElement).selected, + }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-homeassistant": HaHassTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt.ts new file mode 100644 index 0000000000..ccf951be80 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt.ts @@ -0,0 +1,50 @@ +import "@polymer/paper-input/paper-input"; +import { LitElement, customElement, property, html } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { + handleChangeEvent, + TriggerElement, + MqttTrigger, +} from "../ha-automation-trigger-row"; + +@customElement("ha-automation-trigger-mqtt") +export class HaMQTTTrigger extends LitElement implements TriggerElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: MqttTrigger; + + public static get defaultConfig() { + return { topic: "" }; + } + + protected render() { + const { topic, payload } = this.trigger; + return html` + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-mqtt": HaMQTTTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts new file mode 100644 index 0000000000..5ac8310b4e --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts @@ -0,0 +1,103 @@ +import "@polymer/paper-input/paper-input"; +import "../../../../../components/ha-textarea"; + +import "../../../../../components/entity/ha-entity-picker"; +import { LitElement, html, customElement, property } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { + NumericStateTrigger, + ForDict, + handleChangeEvent, +} from "../ha-automation-trigger-row"; + +@customElement("ha-automation-trigger-numeric_state") +export default class HaNumericStateTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: NumericStateTrigger; + + public static get defaultConfig() { + return { + entity_id: "", + }; + } + + public render() { + const { value_template, entity_id, below, above } = this.trigger; + let trgFor = this.trigger.for; + + if ( + trgFor && + ((trgFor as ForDict).hours || + (trgFor as ForDict).minutes || + (trgFor as ForDict).seconds) + ) { + // If the trigger was defined using the yaml dict syntax, convert it to + // the equivalent string format + let { hours = 0, minutes = 0, seconds = 0 } = trgFor as ForDict; + hours = hours.toString(); + minutes = minutes.toString().padStart(2, "0"); + seconds = seconds.toString().padStart(2, "0"); + + trgFor = `${hours}:${minutes}:${seconds}`; + } + return html` + + + + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } + + private _entityPicked(ev) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.trigger, entity_id: ev.detail.value }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-numeric_state": HaNumericStateTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts new file mode 100644 index 0000000000..13ff38961b --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts @@ -0,0 +1,93 @@ +import "@polymer/paper-input/paper-input"; +import { customElement, html, LitElement, property } from "lit-element"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import "../../../../../components/entity/ha-entity-picker"; +import { HomeAssistant } from "../../../../../types"; +import { + handleChangeEvent, + TriggerElement, + StateTrigger, + ForDict, +} from "../ha-automation-trigger-row"; +import { PolymerChangedEvent } from "../../../../../polymer-types"; + +@customElement("ha-automation-trigger-state") +export class HaStateTrigger extends LitElement implements TriggerElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: StateTrigger; + + public static get defaultConfig() { + return { entity_id: "" }; + } + + protected render() { + const { entity_id, to, from } = this.trigger; + let trgFor = this.trigger.for; + + if ( + trgFor && + ((trgFor as ForDict).hours || + (trgFor as ForDict).minutes || + (trgFor as ForDict).seconds) + ) { + // If the trigger was defined using the yaml dict syntax, convert it to + // the equivalent string format + let { hours = 0, minutes = 0, seconds = 0 } = trgFor as ForDict; + hours = hours.toString(); + minutes = minutes.toString().padStart(2, "0"); + seconds = seconds.toString().padStart(2, "0"); + + trgFor = `${hours}:${minutes}:${seconds}`; + } + + return html` + + + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } + + private _entityPicked(ev: PolymerChangedEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.trigger, entity_id: ev.detail.value }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-state": HaStateTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-sun.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-sun.ts new file mode 100644 index 0000000000..9a855f96fd --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-sun.ts @@ -0,0 +1,75 @@ +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-radio-button/paper-radio-button"; +import "@polymer/paper-radio-group/paper-radio-group"; +// tslint:disable-next-line +import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group"; +import { LitElement, customElement, property, html } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { + SunTrigger, + handleChangeEvent, + TriggerElement, +} from "../ha-automation-trigger-row"; +import { fireEvent } from "../../../../../common/dom/fire_event"; + +@customElement("ha-automation-trigger-sun") +export class HaSunTrigger extends LitElement implements TriggerElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: SunTrigger; + + public static get defaultConfig() { + return { + event: "sunrise", + }; + } + + protected render() { + const { offset, event } = this.trigger; + return html` + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.sun.sunrise" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.sun.sunset" + )} + + + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } + + private _radioGroupPicked(ev) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { + ...this.trigger, + event: (ev.target as PaperRadioGroupElement).selected, + }, + }); + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-template.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-template.ts new file mode 100644 index 0000000000..b229f2e065 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-template.ts @@ -0,0 +1,36 @@ +import "../../../../../components/ha-textarea"; +import { LitElement, property, html, customElement } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { + TemplateTrigger, + handleChangeEvent, +} from "../ha-automation-trigger-row"; + +@customElement("ha-automation-trigger-template") +export class HaTemplateTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: TemplateTrigger; + + public static get defaultConfig() { + return { value_template: "" }; + } + + protected render() { + const { value_template } = this.trigger; + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts new file mode 100644 index 0000000000..1222a72ff5 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts @@ -0,0 +1,36 @@ +import "@polymer/paper-input/paper-input"; +import { LitElement, html, property, customElement } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { + TimeTrigger, + handleChangeEvent, + TriggerElement, +} from "../ha-automation-trigger-row"; + +@customElement("ha-automation-trigger-time") +export class HaTimeTrigger extends LitElement implements TriggerElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: TimeTrigger; + + public static get defaultConfig() { + return { at: "" }; + } + + protected render() { + const { at } = this.trigger; + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern.ts new file mode 100644 index 0000000000..95b534961a --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern.ts @@ -0,0 +1,58 @@ +import "@polymer/paper-input/paper-input"; +import { LitElement, property, html, customElement } from "lit-element"; +import { + TriggerElement, + handleChangeEvent, + TimePatternTrigger, +} from "../ha-automation-trigger-row"; +import { HomeAssistant } from "../../../../../types"; + +@customElement("ha-automation-trigger-time_pattern") +export class HaTimePatternTrigger extends LitElement implements TriggerElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: TimePatternTrigger; + + public static get defaultConfig() { + return {}; + } + + protected render() { + const { hours, minutes, seconds } = this.trigger; + return html` + + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-time_pattern": HaTimePatternTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-webhook.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-webhook.ts new file mode 100644 index 0000000000..fa7c1fcf27 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-webhook.ts @@ -0,0 +1,43 @@ +import "@polymer/paper-input/paper-input"; +import { LitElement, customElement, property, html } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { + WebhookTrigger, + handleChangeEvent, +} from "../ha-automation-trigger-row"; + +@customElement("ha-automation-trigger-webhook") +export class HaWebhookTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: WebhookTrigger; + + public static get defaultConfig() { + return { + webhook_id: "", + }; + } + + protected render() { + const { webhook_id: webhookId } = this.trigger; + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-webhook": HaWebhookTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-zone.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-zone.ts new file mode 100644 index 0000000000..d1f71598b0 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-zone.ts @@ -0,0 +1,108 @@ +import "@polymer/paper-radio-button/paper-radio-button"; +import "@polymer/paper-radio-group/paper-radio-group"; +// tslint:disable-next-line +import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group"; +import "../../../../../components/entity/ha-entity-picker"; + +import { hasLocation } from "../../../../../common/entity/has_location"; +import { computeStateDomain } from "../../../../../common/entity/compute_state_domain"; +import { LitElement, property, html, customElement } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { ZoneTrigger } from "../ha-automation-trigger-row"; +import { PolymerChangedEvent } from "../../../../../polymer-types"; +import { fireEvent } from "../../../../../common/dom/fire_event"; + +function zoneAndLocationFilter(stateObj) { + return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone"; +} + +@customElement("ha-automation-trigger-zone") +export class HaZoneTrigger extends LitElement { + @property() public hass!: HomeAssistant; + @property() public trigger!: ZoneTrigger; + + public static get defaultConfig() { + return { + entity_id: "", + zone: "", + event: "enter", + }; + } + + protected render() { + const { entity_id, zone, event } = this.trigger; + return html` + + + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.zone.enter" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.zone.leave" + )} + + + `; + } + + private _entityPicked(ev: PolymerChangedEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.trigger, entity_id: ev.detail.value }, + }); + } + + private _zonePicked(ev: PolymerChangedEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.trigger, zone: ev.detail.value }, + }); + } + + private _radioGroupPicked(ev: CustomEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { + ...this.trigger, + event: (ev.target as PaperRadioGroupElement).selected, + }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-zone": HaZoneTrigger; + } +} diff --git a/src/panels/config/js/automation.tsx b/src/panels/config/js/automation.tsx index e94284cbf2..fdabbc953b 100644 --- a/src/panels/config/js/automation.tsx +++ b/src/panels/config/js/automation.tsx @@ -5,7 +5,8 @@ import "../ha-config-section"; import "../../../components/ha-card"; import "../../../components/ha-textarea"; -import Trigger from "./trigger/index"; +import "../automation/trigger/ha-automation-trigger"; + import Condition from "./condition/index"; import Script from "./script/index"; @@ -26,8 +27,8 @@ export default class Automation extends Component { }); } - public triggerChanged(trigger) { - this.props.onChange({ ...this.props.automation, trigger }); + public triggerChanged(ev: CustomEvent) { + this.props.onChange({ ...this.props.automation, trigger: ev.detail.value }); } public conditionChanged(condition) { @@ -90,11 +91,10 @@ export default class Automation extends Component { )} - diff --git a/src/panels/config/js/preact-types.ts b/src/panels/config/js/preact-types.ts index 617fe5d2dd..25f7ba08eb 100644 --- a/src/panels/config/js/preact-types.ts +++ b/src/panels/config/js/preact-types.ts @@ -23,6 +23,7 @@ declare global { "ha-code-editor": any; "ha-service-picker": any; "mwc-button": any; + "ha-automation-trigger": any; "ha-device-trigger-picker": any; "ha-device-action-picker": any; "ha-form": any; diff --git a/src/panels/config/js/trigger/device.tsx b/src/panels/config/js/trigger/device.tsx deleted file mode 100644 index 81e202fc03..0000000000 --- a/src/panels/config/js/trigger/device.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { h } from "preact"; - -import "../../../../components/device/ha-device-picker"; -import "../../../../components/device/ha-device-trigger-picker"; -import "../../../../components/ha-form/ha-form"; - -import { - fetchDeviceTriggerCapabilities, - deviceAutomationsEqual, -} from "../../../../data/device_automation"; - -import { AutomationComponent } from "../automation-component"; - -export default class DeviceTrigger extends AutomationComponent { - private _origTrigger; - - constructor() { - super(); - this.devicePicked = this.devicePicked.bind(this); - this.deviceTriggerPicked = this.deviceTriggerPicked.bind(this); - this._extraFieldsChanged = this._extraFieldsChanged.bind(this); - this.state = { device_id: undefined, capabilities: undefined }; - } - - public devicePicked(ev) { - if (!this.initialized) { - return; - } - this.setState({ ...this.state, device_id: ev.target.value }); - } - - public deviceTriggerPicked(ev) { - if (!this.initialized) { - return; - } - let trigger = ev.target.value; - if ( - this._origTrigger && - deviceAutomationsEqual(this._origTrigger, trigger) - ) { - trigger = this._origTrigger; - } - this.props.onChange(this.props.index, trigger); - } - - /* eslint-disable camelcase */ - public render({ trigger, hass }, { device_id, capabilities }) { - if (device_id === undefined) { - device_id = trigger.device_id; - } - const extraFieldsData = - capabilities && capabilities.extra_fields - ? capabilities.extra_fields.map((item) => { - return { [item.name]: this.props.trigger[item.name] }; - }) - : undefined; - - return ( -
- - - {extraFieldsData && ( - - )} -
- ); - } - - public componentDidMount() { - this.initialized = true; - if (!this.state.capabilities) { - this._getCapabilities(); - } - if (this.props.trigger) { - this._origTrigger = this.props.trigger; - } - } - - public componentDidUpdate(prevProps) { - if (!deviceAutomationsEqual(prevProps.trigger, this.props.trigger)) { - this._getCapabilities(); - } - } - - private async _getCapabilities() { - const trigger = this.props.trigger; - - const capabilities = trigger.domain - ? await fetchDeviceTriggerCapabilities(this.props.hass, trigger) - : null; - this.setState({ ...this.state, capabilities }); - } - - private _extraFieldsChanged(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.trigger, - ...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.triggers.type.device.extra_fields.${schema.name}` - ) || schema.name; - } -} - -(DeviceTrigger as any).defaultConfig = { - device_id: "", - domain: "", - entity_id: "", -}; diff --git a/src/panels/config/js/trigger/event.tsx b/src/panels/config/js/trigger/event.tsx deleted file mode 100644 index da3ab66baf..0000000000 --- a/src/panels/config/js/trigger/event.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; - -import YAMLTextArea from "../yaml_textarea"; -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class EventTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "trigger"); - this.eventDataChanged = this.eventDataChanged.bind(this); - } - - /* eslint-disable camelcase */ - // tslint:disable-next-line: variable-name - public eventDataChanged(event_data) { - this.props.onChange(this.props.index, { - ...this.props.trigger, - event_data, - }); - } - - public render({ trigger, localize }) { - const { event_type, event_data } = trigger; - return ( -
- - -
- ); - } -} - -(EventTrigger as any).defaultConfig = { - event_type: "", - event_data: {}, -}; diff --git a/src/panels/config/js/trigger/geo_location.tsx b/src/panels/config/js/trigger/geo_location.tsx deleted file mode 100644 index 836c04f42b..0000000000 --- a/src/panels/config/js/trigger/geo_location.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-radio-button/paper-radio-button"; -import "@polymer/paper-radio-group/paper-radio-group"; -import "../../../../components/entity/ha-entity-picker"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class GeolocationTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "trigger"); - this.zonePicked = this.zonePicked.bind(this); - this.radioGroupPicked = this.radioGroupPicked.bind(this); - } - - public zonePicked(ev) { - this.props.onChange(this.props.index, { - ...this.props.trigger, - zone: ev.target.value, - }); - } - - public radioGroupPicked(ev) { - this.props.onChange(this.props.index, { - ...this.props.trigger, - event: ev.target.selected, - }); - } - - /* eslint-disable camelcase */ - public render({ trigger, hass, localize }) { - const { source, zone, event } = trigger; - - return ( -
- - - - - - {localize( - "ui.panel.config.automation.editor.triggers.type.geo_location.enter" - )} - - - {localize( - "ui.panel.config.automation.editor.triggers.type.geo_location.leave" - )} - - -
- ); - } -} - -(GeolocationTrigger as any).defaultConfig = { - source: "", - zone: "", - event: "enter", -}; diff --git a/src/panels/config/js/trigger/homeassistant.tsx b/src/panels/config/js/trigger/homeassistant.tsx deleted file mode 100644 index 52bf5ac192..0000000000 --- a/src/panels/config/js/trigger/homeassistant.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-radio-button/paper-radio-button"; -import "@polymer/paper-radio-group/paper-radio-group"; - -import { AutomationComponent } from "../automation-component"; - -export default class HassTrigger extends AutomationComponent { - constructor() { - super(); - - this.radioGroupPicked = this.radioGroupPicked.bind(this); - } - - public radioGroupPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.trigger, - event: ev.target.selected, - }); - } - - /* eslint-disable camelcase */ - public render({ trigger, localize }) { - const { event } = trigger; - return ( -
- - - - {localize( - "ui.panel.config.automation.editor.triggers.type.homeassistant.start" - )} - - - {localize( - "ui.panel.config.automation.editor.triggers.type.homeassistant.shutdown" - )} - - -
- ); - } -} - -(HassTrigger as any).defaultConfig = { - event: "start", -}; diff --git a/src/panels/config/js/trigger/index.tsx b/src/panels/config/js/trigger/index.tsx deleted file mode 100644 index fa196e418e..0000000000 --- a/src/panels/config/js/trigger/index.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { h, Component } from "preact"; -import "@material/mwc-button"; -import "../../../../components/ha-card"; - -import TriggerRow from "./trigger_row"; -import StateTrigger from "./state"; - -export default class Trigger extends Component { - constructor() { - super(); - - this.addTrigger = this.addTrigger.bind(this); - this.triggerChanged = this.triggerChanged.bind(this); - } - - public addTrigger() { - const trigger = this.props.trigger.concat({ - platform: "state", - ...(StateTrigger as any).defaultConfig, - }); - - this.props.onChange(trigger); - } - - public triggerChanged(index, newValue) { - const trigger = this.props.trigger.concat(); - - if (newValue === null) { - trigger.splice(index, 1); - } else { - trigger[index] = newValue; - } - - this.props.onChange(trigger); - } - - public render({ trigger, hass, localize }) { - return ( -
- {trigger.map((trg, idx) => ( - - ))} - -
- - {localize("ui.panel.config.automation.editor.triggers.add")} - -
-
-
- ); - } -} diff --git a/src/panels/config/js/trigger/mqtt.tsx b/src/panels/config/js/trigger/mqtt.tsx deleted file mode 100644 index 16cfdb612e..0000000000 --- a/src/panels/config/js/trigger/mqtt.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class MQTTTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor(props) { - super(props); - - this.onChange = onChangeEvent.bind(this, "trigger"); - } - - /* eslint-disable camelcase */ - public render({ trigger, localize }) { - const { topic, payload } = trigger; - return ( -
- - -
- ); - } -} - -(MQTTTrigger as any).defaultConfig = { - topic: "", -}; diff --git a/src/panels/config/js/trigger/numeric_state.tsx b/src/panels/config/js/trigger/numeric_state.tsx deleted file mode 100644 index 366ee4f32f..0000000000 --- a/src/panels/config/js/trigger/numeric_state.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; -import "../../../../components/ha-textarea"; - -import "../../../../components/entity/ha-entity-picker"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class NumericStateTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor(props) { - super(props); - - this.onChange = onChangeEvent.bind(this, "trigger"); - this.entityPicked = this.entityPicked.bind(this); - } - - public entityPicked(ev) { - this.props.onChange(this.props.index, { - ...this.props.trigger, - entity_id: ev.target.value, - }); - } - - /* eslint-disable camelcase */ - public render({ trigger, hass, localize }) { - const { value_template, entity_id, below, above } = trigger; - let trgFor = trigger.for; - - if (trgFor && (trgFor.hours || trgFor.minutes || trgFor.seconds)) { - // If the trigger was defined using the yaml dict syntax, convert it to - // the equivalent string format - let { hours = 0, minutes = 0, seconds = 0 } = trgFor; - hours = hours.toString(); - minutes = minutes.toString().padStart(2, "0"); - seconds = seconds.toString().padStart(2, "0"); - - trgFor = `${hours}:${minutes}:${seconds}`; - } - return ( -
- - - - - -
- ); - } -} - -(NumericStateTrigger as any).defaultConfig = { - entity_id: "", -}; diff --git a/src/panels/config/js/trigger/state.tsx b/src/panels/config/js/trigger/state.tsx deleted file mode 100644 index 6450ccfae3..0000000000 --- a/src/panels/config/js/trigger/state.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; -import "../../../../components/entity/ha-entity-picker"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class StateTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor(props) { - super(props); - this.onChange = onChangeEvent.bind(this, "trigger"); - this.entityPicked = this.entityPicked.bind(this); - } - - public entityPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.trigger, - entity_id: ev.target.value, - }); - } - - /* eslint-disable camelcase */ - public render({ trigger, hass, localize }) { - const { entity_id, to, from } = trigger; - let trgFor = trigger.for; - - if (trgFor && (trgFor.hours || trgFor.minutes || trgFor.seconds)) { - // If the trigger was defined using the yaml dict syntax, convert it to - // the equivalent string format - let { hours = 0, minutes = 0, seconds = 0 } = trgFor; - hours = hours.toString(); - minutes = minutes.toString().padStart(2, "0"); - seconds = seconds.toString().padStart(2, "0"); - - trgFor = `${hours}:${minutes}:${seconds}`; - } - return ( -
- - - - -
- ); - } -} - -(StateTrigger as any).defaultConfig = { - entity_id: "", -}; diff --git a/src/panels/config/js/trigger/sun.tsx b/src/panels/config/js/trigger/sun.tsx deleted file mode 100644 index c8d9b01616..0000000000 --- a/src/panels/config/js/trigger/sun.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; -import "@polymer/paper-radio-button/paper-radio-button"; -import "@polymer/paper-radio-group/paper-radio-group"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class SunTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "trigger"); - this.radioGroupPicked = this.radioGroupPicked.bind(this); - } - - public radioGroupPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.trigger, - event: ev.target.selected, - }); - } - - /* eslint-disable camelcase */ - public render({ trigger, localize }) { - const { offset, event } = trigger; - return ( -
- - - - {localize( - "ui.panel.config.automation.editor.triggers.type.sun.sunrise" - )} - - - {localize( - "ui.panel.config.automation.editor.triggers.type.sun.sunset" - )} - - - - -
- ); - } -} - -(SunTrigger as any).defaultConfig = { - event: "sunrise", -}; diff --git a/src/panels/config/js/trigger/template.tsx b/src/panels/config/js/trigger/template.tsx deleted file mode 100644 index aa25103043..0000000000 --- a/src/panels/config/js/trigger/template.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { h } from "preact"; - -import "../../../../components/ha-textarea"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class TemplateTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "trigger"); - } - - public render({ trigger, localize }) { - /* eslint-disable camelcase */ - const { value_template } = trigger; - return ( -
- -
- ); - } -} - -(TemplateTrigger as any).defaultConfig = { - value_template: "", -}; diff --git a/src/panels/config/js/trigger/time.tsx b/src/panels/config/js/trigger/time.tsx deleted file mode 100644 index 70a2c718c6..0000000000 --- a/src/panels/config/js/trigger/time.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class TimeTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "trigger"); - } - - /* eslint-disable camelcase */ - public render({ trigger, localize }) { - const { at } = trigger; - return ( -
- -
- ); - } -} - -(TimeTrigger as any).defaultConfig = { - at: "", -}; diff --git a/src/panels/config/js/trigger/time_pattern.tsx b/src/panels/config/js/trigger/time_pattern.tsx deleted file mode 100644 index b6af8172bd..0000000000 --- a/src/panels/config/js/trigger/time_pattern.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class TimePatternTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "trigger"); - } - - /* eslint-disable camelcase */ - public render({ trigger, localize }) { - const { hours, minutes, seconds } = trigger; - return ( -
- - - -
- ); - } -} - -(TimePatternTrigger as any).defaultConfig = {}; diff --git a/src/panels/config/js/trigger/trigger_edit.tsx b/src/panels/config/js/trigger/trigger_edit.tsx deleted file mode 100644 index 4715b7d456..0000000000 --- a/src/panels/config/js/trigger/trigger_edit.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { h, Component } from "preact"; - -import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; - -import "../../../../components/ha-code-editor"; - -import YAMLTextArea from "../yaml_textarea"; - -import DeviceTrigger from "./device"; -import EventTrigger from "./event"; -import GeolocationTrigger from "./geo_location"; -import HassTrigger from "./homeassistant"; -import MQTTTrigger from "./mqtt"; -import NumericStateTrigger from "./numeric_state"; -import TimePatternTrigger from "./time_pattern"; -import StateTrigger from "./state"; -import SunTrigger from "./sun"; -import TemplateTrigger from "./template"; -import TimeTrigger from "./time"; -import WebhookTrigger from "./webhook"; -import ZoneTrigger from "./zone"; - -const TYPES = { - device: DeviceTrigger, - event: EventTrigger, - state: StateTrigger, - geo_location: GeolocationTrigger, - homeassistant: HassTrigger, - mqtt: MQTTTrigger, - numeric_state: NumericStateTrigger, - sun: SunTrigger, - template: TemplateTrigger, - time: TimeTrigger, - time_pattern: TimePatternTrigger, - webhook: WebhookTrigger, - zone: ZoneTrigger, -}; - -const OPTIONS = Object.keys(TYPES).sort(); - -export default class TriggerEdit extends Component { - constructor() { - super(); - - this.typeChanged = this.typeChanged.bind(this); - this.onYamlChange = this.onYamlChange.bind(this); - } - - public render({ index, trigger, onChange, hass, localize, yamlMode }) { - // tslint:disable-next-line: variable-name - const Comp = TYPES[trigger.platform]; - const selected = OPTIONS.indexOf(trigger.platform); - - if (yamlMode || !Comp) { - return ( -
- {!Comp && ( -
- {localize( - "ui.panel.config.automation.editor.triggers.unsupported_platform", - "platform", - trigger.platform - )} -
- )} - -
- ); - } - - return ( -
- - - {OPTIONS.map((opt) => ( - - {localize( - `ui.panel.config.automation.editor.triggers.type.${opt}.label` - )} - - ))} - - - -
- ); - } - - private typeChanged(ev) { - const type = ev.target.selectedItem.attributes.platform.value; - - if (type !== this.props.trigger.platform) { - this.props.onChange(this.props.index, { - platform: type, - ...TYPES[type].defaultConfig, - }); - } - } - - private onYamlChange(trigger) { - this.props.onChange(this.props.index, trigger); - } -} diff --git a/src/panels/config/js/trigger/trigger_row.tsx b/src/panels/config/js/trigger/trigger_row.tsx deleted file mode 100644 index 9ca51d93cf..0000000000 --- a/src/panels/config/js/trigger/trigger_row.tsx +++ /dev/null @@ -1,86 +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 TriggerEdit from "./trigger_edit"; - -export default class TriggerRow extends Component { - public state: { yamlMode: boolean }; - constructor() { - super(); - - this.state = { - yamlMode: false, - }; - - this.onDelete = this.onDelete.bind(this); - this.switchYamlMode = this.switchYamlMode.bind(this); - } - - public render(props, { yamlMode }) { - return ( - -
-
- - - - - {yamlMode - ? props.localize( - "ui.panel.config.automation.editor.edit_ui" - ) - : props.localize( - "ui.panel.config.automation.editor.edit_yaml" - )} - - - {props.localize( - "ui.panel.config.automation.editor.triggers.duplicate" - )} - - - {props.localize( - "ui.panel.config.automation.editor.triggers.delete" - )} - - - -
- -
-
- ); - } - - private onDelete() { - // eslint-disable-next-line - if ( - confirm( - this.props.localize( - "ui.panel.config.automation.editor.triggers.delete_confirm" - ) - ) - ) { - this.props.onChange(this.props.index, null); - } - } - - private switchYamlMode() { - this.setState({ - yamlMode: !this.state.yamlMode, - }); - } -} diff --git a/src/panels/config/js/trigger/webhook.tsx b/src/panels/config/js/trigger/webhook.tsx deleted file mode 100644 index 59806e65fa..0000000000 --- a/src/panels/config/js/trigger/webhook.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-input/paper-input"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class WebhookTrigger extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "trigger"); - } - - public render({ trigger, localize }) { - const { webhook_id: webhookId } = trigger; - return ( -
- -
- ); - } -} - -(WebhookTrigger as any).defaultConfig = { - webhook_id: "", -}; diff --git a/src/panels/config/js/trigger/zone.tsx b/src/panels/config/js/trigger/zone.tsx deleted file mode 100644 index 81914a3d52..0000000000 --- a/src/panels/config/js/trigger/zone.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { h } from "preact"; - -import "@polymer/paper-radio-button/paper-radio-button"; -import "@polymer/paper-radio-group/paper-radio-group"; -import "../../../../components/entity/ha-entity-picker"; - -import { hasLocation } from "../../../../common/entity/has_location"; -import { computeStateDomain } from "../../../../common/entity/compute_state_domain"; -import { AutomationComponent } from "../automation-component"; - -function zoneAndLocationFilter(stateObj) { - return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone"; -} - -export default class ZoneTrigger extends AutomationComponent { - constructor() { - super(); - - this.radioGroupPicked = this.radioGroupPicked.bind(this); - this.entityPicked = this.entityPicked.bind(this); - this.zonePicked = this.zonePicked.bind(this); - } - - /* eslint-disable camelcase */ - public render({ trigger, hass, localize }) { - const { entity_id, zone, event } = trigger; - return ( -
- - - - - - {localize( - "ui.panel.config.automation.editor.triggers.type.zone.enter" - )} - - - {localize( - "ui.panel.config.automation.editor.triggers.type.zone.leave" - )} - - -
- ); - } - - private entityPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.trigger, - entity_id: ev.target.value, - }); - } - - private zonePicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.trigger, - zone: ev.target.value, - }); - } - - private radioGroupPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.trigger, - event: ev.target.selected, - }); - } -} - -(ZoneTrigger as any).defaultConfig = { - entity_id: "", - zone: "", - event: "enter", -};