From 7b2be54f8f07f44270fd86f9b759929111494f45 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 2 Dec 2019 11:20:09 +0100 Subject: [PATCH] YAML support for automation triggers (#4289) * WIP: Add yaml editors to automation * Fix form overwriting yaml on switching back * Finish triggers * prettier --- src/common/preact/event.ts | 5 +++- src/panels/config/js/automation-component.tsx | 23 ++++++++++++++ src/panels/config/js/trigger/device.tsx | 16 ++++++++-- src/panels/config/js/trigger/event.tsx | 6 ++-- src/panels/config/js/trigger/geo_location.tsx | 6 ++-- .../config/js/trigger/homeassistant.tsx | 10 +++++-- src/panels/config/js/trigger/mqtt.tsx | 10 ++++--- .../config/js/trigger/numeric_state.tsx | 10 ++++--- src/panels/config/js/trigger/state.tsx | 18 ++++++----- src/panels/config/js/trigger/sun.tsx | 8 +++-- src/panels/config/js/trigger/template.tsx | 5 ++-- src/panels/config/js/trigger/time.tsx | 5 ++-- src/panels/config/js/trigger/time_pattern.tsx | 5 ++-- src/panels/config/js/trigger/trigger_edit.tsx | 30 ++++++++++++++----- src/panels/config/js/trigger/trigger_row.tsx | 28 +++++++++++++++-- src/panels/config/js/trigger/webhook.tsx | 7 +++-- src/panels/config/js/trigger/zone.tsx | 15 ++++++++-- src/panels/config/js/yaml_textarea.tsx | 2 +- src/translations/en.json | 2 ++ 19 files changed, 162 insertions(+), 49 deletions(-) create mode 100644 src/panels/config/js/automation-component.tsx diff --git a/src/common/preact/event.ts b/src/common/preact/event.ts index 7e4ec871e9..a35d1969e4 100644 --- a/src/common/preact/event.ts +++ b/src/common/preact/event.ts @@ -7,8 +7,11 @@ // export function onChangeEvent(this: OnChangeComponent, prop, ev) { export function onChangeEvent(this: any, prop, ev) { - const origData = this.props[prop]; + if (!this.initialized) { + return; + } + const origData = this.props[prop]; if (ev.target.value === origData[ev.target.name]) { return; } diff --git a/src/panels/config/js/automation-component.tsx b/src/panels/config/js/automation-component.tsx new file mode 100644 index 0000000000..05104f6360 --- /dev/null +++ b/src/panels/config/js/automation-component.tsx @@ -0,0 +1,23 @@ +import { h, Component, ComponentChild } from "preact"; + +export class AutomationComponent extends Component { + // @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
; + } +} diff --git a/src/panels/config/js/trigger/device.tsx b/src/panels/config/js/trigger/device.tsx index 748e9ee11f..03d5ee3d1b 100644 --- a/src/panels/config/js/trigger/device.tsx +++ b/src/panels/config/js/trigger/device.tsx @@ -1,4 +1,4 @@ -import { h, Component } from "preact"; +import { h } from "preact"; import "../../../../components/device/ha-device-picker"; import "../../../../components/device/ha-device-trigger-picker"; @@ -9,7 +9,9 @@ import { deviceAutomationsEqual, } from "../../../../data/device_automation"; -export default class DeviceTrigger extends Component { +import { AutomationComponent } from "../automation-component"; + +export default class DeviceTrigger extends AutomationComponent { private _origTrigger; constructor() { @@ -21,10 +23,16 @@ export default class DeviceTrigger extends Component { } 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 && @@ -75,6 +83,7 @@ export default class DeviceTrigger extends Component { } public componentDidMount() { + this.initialized = true; if (!this.state.capabilities) { this._getCapabilities(); } @@ -99,6 +108,9 @@ export default class DeviceTrigger extends Component { } private _extraFieldsChanged(ev) { + if (!this.initialized) { + return; + } this.props.onChange(this.props.index, { ...this.props.trigger, ...ev.detail.value, diff --git a/src/panels/config/js/trigger/event.tsx b/src/panels/config/js/trigger/event.tsx index cc1390f427..cff15eb95a 100644 --- a/src/panels/config/js/trigger/event.tsx +++ b/src/panels/config/js/trigger/event.tsx @@ -1,10 +1,12 @@ -import { h, Component } from "preact"; +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 Component { +export default class EventTrigger extends AutomationComponent { private onChange: (obj: any) => void; constructor() { super(); diff --git a/src/panels/config/js/trigger/geo_location.tsx b/src/panels/config/js/trigger/geo_location.tsx index 52deb4a6b2..bc4f1060ab 100644 --- a/src/panels/config/js/trigger/geo_location.tsx +++ b/src/panels/config/js/trigger/geo_location.tsx @@ -1,11 +1,13 @@ -import { h, Component } from "preact"; +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 Component { +export default class GeolocationTrigger extends AutomationComponent { private onChange: (obj: any) => void; constructor() { super(); diff --git a/src/panels/config/js/trigger/homeassistant.tsx b/src/panels/config/js/trigger/homeassistant.tsx index 79f6a45c36..c6fb1dc721 100644 --- a/src/panels/config/js/trigger/homeassistant.tsx +++ b/src/panels/config/js/trigger/homeassistant.tsx @@ -1,8 +1,11 @@ -import { h, Component } from "preact"; +import { h } from "preact"; + import "@polymer/paper-radio-button/paper-radio-button"; import "@polymer/paper-radio-group/paper-radio-group"; -export default class HassTrigger extends Component { +import { AutomationComponent } from "../automation-component"; + +export default class HassTrigger extends AutomationComponent { constructor() { super(); @@ -10,6 +13,9 @@ export default class HassTrigger extends Component { } public radioGroupPicked(ev) { + if (!this.initialized) { + return; + } this.props.onChange(this.props.index, { ...this.props.trigger, event: ev.target.selected, diff --git a/src/panels/config/js/trigger/mqtt.tsx b/src/panels/config/js/trigger/mqtt.tsx index 283c0e8bb0..a1afdf183a 100644 --- a/src/panels/config/js/trigger/mqtt.tsx +++ b/src/panels/config/js/trigger/mqtt.tsx @@ -1,12 +1,14 @@ -import { h, Component } from "preact"; +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 Component { +export default class MQTTTrigger extends AutomationComponent { private onChange: (obj: any) => void; - constructor() { - super(); + constructor(props) { + super(props); this.onChange = onChangeEvent.bind(this, "trigger"); } diff --git a/src/panels/config/js/trigger/numeric_state.tsx b/src/panels/config/js/trigger/numeric_state.tsx index b706703fc8..35ef709f38 100644 --- a/src/panels/config/js/trigger/numeric_state.tsx +++ b/src/panels/config/js/trigger/numeric_state.tsx @@ -1,15 +1,17 @@ -import { h, Component } from "preact"; +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 Component { +export default class NumericStateTrigger extends AutomationComponent { private onChange: (obj: any) => void; - constructor() { - super(); + constructor(props) { + super(props); this.onChange = onChangeEvent.bind(this, "trigger"); this.entityPicked = this.entityPicked.bind(this); diff --git a/src/panels/config/js/trigger/state.tsx b/src/panels/config/js/trigger/state.tsx index 825731a5c6..10f0065164 100644 --- a/src/panels/config/js/trigger/state.tsx +++ b/src/panels/config/js/trigger/state.tsx @@ -1,20 +1,23 @@ -import { h, Component } from "preact"; +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 Component { +export default class StateTrigger extends AutomationComponent { private onChange: (obj: any) => void; - constructor() { - super(); - + 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, @@ -23,8 +26,7 @@ export default class StateTrigger extends Component { /* eslint-disable camelcase */ public render({ trigger, hass, localize }) { - const { entity_id, to } = trigger; - const trgFrom = trigger.from; + const { entity_id, to, from } = trigger; let trgFor = trigger.for; if (trgFor && (trgFor.hours || trgFor.minutes || trgFor.seconds)) { @@ -50,7 +52,7 @@ export default class StateTrigger extends Component { "ui.panel.config.automation.editor.triggers.type.state.from" )} name="from" - value={trgFrom} + value={from} onvalue-changed={this.onChange} /> { +export default class SunTrigger extends AutomationComponent { private onChange: (obj: any) => void; constructor() { super(); @@ -16,6 +17,9 @@ export default class SunTrigger extends Component { } public radioGroupPicked(ev) { + if (!this.initialized) { + return; + } this.props.onChange(this.props.index, { ...this.props.trigger, event: ev.target.selected, diff --git a/src/panels/config/js/trigger/template.tsx b/src/panels/config/js/trigger/template.tsx index deec5a18b4..0d9a3526f0 100644 --- a/src/panels/config/js/trigger/template.tsx +++ b/src/panels/config/js/trigger/template.tsx @@ -1,10 +1,11 @@ -import { h, Component } from "preact"; +import { h } from "preact"; import "../../../../components/ha-textarea"; import { onChangeEvent } from "../../../../common/preact/event"; +import { AutomationComponent } from "../automation-component"; -export default class TemplateTrigger extends Component { +export default class TemplateTrigger extends AutomationComponent { private onChange: (obj: any) => void; constructor() { super(); diff --git a/src/panels/config/js/trigger/time.tsx b/src/panels/config/js/trigger/time.tsx index e181aff3dc..a8ab6f7d30 100644 --- a/src/panels/config/js/trigger/time.tsx +++ b/src/panels/config/js/trigger/time.tsx @@ -1,10 +1,11 @@ -import { h, Component } from "preact"; +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 Component { +export default class TimeTrigger extends AutomationComponent { private onChange: (obj: any) => void; constructor() { super(); diff --git a/src/panels/config/js/trigger/time_pattern.tsx b/src/panels/config/js/trigger/time_pattern.tsx index a4dd8a78f1..2738810cb2 100644 --- a/src/panels/config/js/trigger/time_pattern.tsx +++ b/src/panels/config/js/trigger/time_pattern.tsx @@ -1,10 +1,11 @@ -import { h, Component } from "preact"; +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 Component { +export default class TimePatternTrigger extends AutomationComponent { private onChange: (obj: any) => void; constructor() { super(); diff --git a/src/panels/config/js/trigger/trigger_edit.tsx b/src/panels/config/js/trigger/trigger_edit.tsx index a52af204c3..4715b7d456 100644 --- a/src/panels/config/js/trigger/trigger_edit.tsx +++ b/src/panels/config/js/trigger/trigger_edit.tsx @@ -4,6 +4,10 @@ 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"; @@ -41,25 +45,31 @@ export default class TriggerEdit extends Component { super(); this.typeChanged = this.typeChanged.bind(this); + this.onYamlChange = this.onYamlChange.bind(this); } - public render({ index, trigger, onChange, hass, localize }) { + 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 (!Comp) { + if (yamlMode || !Comp) { return ( -
- {localize( - "ui.panel.config.automation.editor.triggers.unsupported_platform", - "platform", - trigger.platform +
+ {!Comp && ( +
+ {localize( + "ui.panel.config.automation.editor.triggers.unsupported_platform", + "platform", + trigger.platform + )} +
)} -
{JSON.stringify(trigger, null, 2)}
+
); } + return (
{ }); } } + + 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 index 7853a8d9dc..9ca51d93cf 100644 --- a/src/panels/config/js/trigger/trigger_row.tsx +++ b/src/panels/config/js/trigger/trigger_row.tsx @@ -8,28 +8,44 @@ 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) { + 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" @@ -43,7 +59,7 @@ export default class TriggerRow extends Component {
- +
); @@ -61,4 +77,10 @@ export default class TriggerRow extends Component { 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 index 86960fc654..5775895c33 100644 --- a/src/panels/config/js/trigger/webhook.tsx +++ b/src/panels/config/js/trigger/webhook.tsx @@ -1,8 +1,11 @@ -import { h, Component } from "preact"; +import { h } from "preact"; + import "@polymer/paper-input/paper-input"; import { onChangeEvent } from "../../../../common/preact/event"; -export default class WebhookTrigger extends Component { +import { AutomationComponent } from "../automation-component"; + +export default class WebhookTrigger extends AutomationComponent { private onChange: (obj: any) => void; constructor() { super(); diff --git a/src/panels/config/js/trigger/zone.tsx b/src/panels/config/js/trigger/zone.tsx index 837ddeb98d..abf54237bd 100644 --- a/src/panels/config/js/trigger/zone.tsx +++ b/src/panels/config/js/trigger/zone.tsx @@ -1,16 +1,18 @@ -import { h, Component } from "preact"; +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 Component { +export default class ZoneTrigger extends AutomationComponent { constructor() { super(); @@ -70,6 +72,9 @@ export default class ZoneTrigger extends Component { } private entityPicked(ev) { + if (!this.initialized) { + return; + } this.props.onChange(this.props.index, { ...this.props.trigger, entity_id: ev.target.value, @@ -77,6 +82,9 @@ export default class ZoneTrigger extends Component { } private zonePicked(ev) { + if (!this.initialized) { + return; + } this.props.onChange(this.props.index, { ...this.props.trigger, zone: ev.target.value, @@ -84,6 +92,9 @@ export default class ZoneTrigger extends Component { } private radioGroupPicked(ev) { + if (!this.initialized) { + return; + } this.props.onChange(this.props.index, { ...this.props.trigger, event: ev.target.selected, diff --git a/src/panels/config/js/yaml_textarea.tsx b/src/panels/config/js/yaml_textarea.tsx index 0b8115e7d3..db283b174f 100644 --- a/src/panels/config/js/yaml_textarea.tsx +++ b/src/panels/config/js/yaml_textarea.tsx @@ -66,7 +66,7 @@ export default class YAMLTextArea extends Component { }; return (
-

{label}

+ {label &&

{label}

}