Add yaml editor to automation actions and scripts (#4306)

* Add yaml editor to automation actions and scripts

* Add types

* Update event.tsx
This commit is contained in:
Bram Kragten 2019-12-02 14:08:19 +01:00 committed by GitHub
parent 94c120cdb1
commit fa8f6b7b91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 105 additions and 45 deletions

View File

@ -1,6 +1,6 @@
import { h, Component, ComponentChild } from "preact"; import { h, Component, ComponentChild } from "preact";
export class AutomationComponent extends Component<any, any> { export class AutomationComponent<P = {}, S = {}> extends Component<P, S> {
// @ts-ignore // @ts-ignore
protected initialized: boolean; protected initialized: boolean;

View File

@ -11,7 +11,7 @@ import {
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class DeviceCondition extends AutomationComponent { export default class DeviceCondition extends AutomationComponent<any, any> {
private _origCondition; private _origCondition;
constructor() { constructor() {

View File

@ -3,7 +3,7 @@ import { h } from "preact";
import Condition from "./index"; import Condition from "./index";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class LogicalCondition extends AutomationComponent { export default class LogicalCondition extends AutomationComponent<any> {
constructor() { constructor() {
super(); super();
this.conditionChanged = this.conditionChanged.bind(this); this.conditionChanged = this.conditionChanged.bind(this);

View File

@ -6,7 +6,7 @@ import "../../../../components/entity/ha-entity-picker";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class NumericStateCondition extends AutomationComponent { export default class NumericStateCondition extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -5,7 +5,7 @@ import "../../../../components/entity/ha-entity-picker";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class StateCondition extends AutomationComponent { export default class StateCondition extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -6,7 +6,7 @@ import "@polymer/paper-radio-group/paper-radio-group";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class SunCondition extends AutomationComponent { export default class SunCondition extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
private afterPicked: (obj: any) => void; private afterPicked: (obj: any) => void;
private beforePicked: (obj: any) => void; private beforePicked: (obj: any) => void;

View File

@ -4,7 +4,7 @@ import "../../../../components/ha-textarea";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class TemplateCondition extends AutomationComponent { export default class TemplateCondition extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -4,7 +4,7 @@ import "@polymer/paper-input/paper-input";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class TimeCondition extends AutomationComponent { export default class TimeCondition extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -9,7 +9,7 @@ function zoneAndLocationFilter(stateObj) {
return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone"; return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone";
} }
export default class ZoneCondition extends AutomationComponent { export default class ZoneCondition extends AutomationComponent<any> {
constructor() { constructor() {
super(); super();

View File

@ -3,6 +3,8 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import YAMLTextArea from "../yaml_textarea";
import CallServiceAction from "./call_service"; import CallServiceAction from "./call_service";
import ConditionAction from "./condition"; import ConditionAction from "./condition";
import DelayAction from "./delay"; import DelayAction from "./delay";
@ -39,6 +41,7 @@ export default class Action extends Component<any> {
super(); super();
this.typeChanged = this.typeChanged.bind(this); this.typeChanged = this.typeChanged.bind(this);
this.onYamlChange = this.onYamlChange.bind(this);
} }
public typeChanged(ev) { public typeChanged(ev) {
@ -50,25 +53,30 @@ export default class Action extends Component<any> {
} }
} }
public render({ index, action, onChange, hass, localize }) { public render({ index, action, onChange, hass, localize, yamlMode }) {
const type = getType(action); const type = getType(action);
// tslint:disable-next-line: variable-name // tslint:disable-next-line: variable-name
const Comp = type && TYPES[type]; const Comp = type && TYPES[type];
// @ts-ignore // @ts-ignore
const selected = OPTIONS.indexOf(type); const selected = OPTIONS.indexOf(type);
if (!Comp) { if (yamlMode || !Comp) {
return ( return (
<div style="margin-right: 24px;">
{!Comp && (
<div> <div>
{localize( {localize(
"ui.panel.config.automation.editor.actions.unsupported_action", "ui.panel.config.automation.editor.actions.unsupported_action",
"action", "action",
type type
)} )}
<pre>{JSON.stringify(action, null, 2)}</pre> </div>
)}
<YAMLTextArea value={action} onChange={this.onYamlChange} />
</div> </div>
); );
} }
return ( return (
<div> <div>
<paper-dropdown-menu-light <paper-dropdown-menu-light
@ -101,4 +109,8 @@ export default class Action extends Component<any> {
</div> </div>
); );
} }
private onYamlChange(condition) {
this.props.onChange(this.props.index, condition);
}
} }

View File

@ -8,10 +8,16 @@ import "../../../../components/ha-card";
import ActionEdit from "./action_edit"; import ActionEdit from "./action_edit";
export default class Action extends Component<any> { export default class Action extends Component<any> {
public state: { yamlMode: boolean };
constructor() { constructor() {
super(); super();
this.state = {
yamlMode: false,
};
this.onDelete = this.onDelete.bind(this); this.onDelete = this.onDelete.bind(this);
this.switchYamlMode = this.switchYamlMode.bind(this);
} }
public onDelete() { public onDelete() {
@ -27,22 +33,32 @@ export default class Action extends Component<any> {
} }
} }
public render(props) { public render(props, { yamlMode }) {
return ( return (
<ha-card> <ha-card>
<div class="card-content"> <div class="card-content">
<div class="card-menu"> <div class="card-menu" style="z-index: 3">
<paper-menu-button <paper-menu-button
no-animations no-animations
horizontal-align="right" horizontal-align="right"
horizontal-offset="-5" horizontal-offset="-5"
vertical-offset="-5" vertical-offset="-5"
close-on-activate
> >
<paper-icon-button <paper-icon-button
icon="hass:dots-vertical" icon="hass:dots-vertical"
slot="dropdown-trigger" slot="dropdown-trigger"
/> />
<paper-listbox slot="dropdown-content"> <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> <paper-item disabled>
{props.localize( {props.localize(
"ui.panel.config.automation.editor.actions.duplicate" "ui.panel.config.automation.editor.actions.duplicate"
@ -56,9 +72,15 @@ export default class Action extends Component<any> {
</paper-listbox> </paper-listbox>
</paper-menu-button> </paper-menu-button>
</div> </div>
<ActionEdit {...props} /> <ActionEdit {...props} yamlMode={yamlMode} />
</div> </div>
</ha-card> </ha-card>
); );
} }
private switchYamlMode() {
this.setState({
yamlMode: !this.state.yamlMode,
});
}
} }

View File

@ -1,9 +1,10 @@
import { h, Component } from "preact"; import { h } from "preact";
import "../../../../components/ha-service-picker"; import "../../../../components/ha-service-picker";
import YAMLTextArea from "../yaml_textarea"; import YAMLTextArea from "../yaml_textarea";
import { AutomationComponent } from "../automation-component";
export default class CallServiceAction extends Component<any> { export default class CallServiceAction extends AutomationComponent<any> {
constructor() { constructor() {
super(); super();
@ -12,6 +13,9 @@ export default class CallServiceAction extends Component<any> {
} }
public serviceChanged(ev) { public serviceChanged(ev) {
if (!this.initialized) {
return;
}
this.props.onChange(this.props.index, { this.props.onChange(this.props.index, {
...this.props.action, ...this.props.action,
service: ev.target.value, service: ev.target.value,
@ -19,6 +23,9 @@ export default class CallServiceAction extends Component<any> {
} }
public serviceDataChanged(data) { public serviceDataChanged(data) {
if (!this.initialized) {
return;
}
this.props.onChange(this.props.index, { ...this.props.action, data }); this.props.onChange(this.props.index, { ...this.props.action, data });
} }

View File

@ -1,8 +1,9 @@
import { h, Component } from "preact"; import { h } from "preact";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component";
export default class DelayAction extends Component<any> { export default class DelayAction extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -1,8 +1,9 @@
import { h, Component } from "preact"; import { h } from "preact";
import "../../../../components/device/ha-device-picker"; import "../../../../components/device/ha-device-picker";
import "../../../../components/device/ha-device-action-picker"; import "../../../../components/device/ha-device-action-picker";
import "../../../../components/ha-form/ha-form"; import "../../../../components/ha-form/ha-form";
import { AutomationComponent } from "../automation-component";
import { import {
fetchDeviceActionCapabilities, fetchDeviceActionCapabilities,
@ -11,7 +12,7 @@ import {
import { DeviceAction } from "../../../../data/script"; import { DeviceAction } from "../../../../data/script";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
export default class DeviceActionEditor extends Component< export default class DeviceActionEditor extends AutomationComponent<
{ {
index: number; index: number;
action: DeviceAction; action: DeviceAction;
@ -78,6 +79,7 @@ export default class DeviceActionEditor extends Component<
} }
public componentDidMount() { public componentDidMount() {
this.initialized = true;
if (!this.state.capabilities) { if (!this.state.capabilities) {
this._getCapabilities(); this._getCapabilities();
} }
@ -93,10 +95,16 @@ export default class DeviceActionEditor extends Component<
} }
private devicePicked(ev) { private devicePicked(ev) {
if (!this.initialized) {
return;
}
this.setState({ ...this.state, device_id: ev.target.value }); this.setState({ ...this.state, device_id: ev.target.value });
} }
private deviceActionPicked(ev) { private deviceActionPicked(ev) {
if (!this.initialized) {
return;
}
let deviceAction = ev.target.value; let deviceAction = ev.target.value;
if ( if (
this._origAction && this._origAction &&
@ -117,6 +125,9 @@ export default class DeviceActionEditor extends Component<
} }
private _extraFieldsChanged(ev) { private _extraFieldsChanged(ev) {
if (!this.initialized) {
return;
}
this.props.onChange(this.props.index, { this.props.onChange(this.props.index, {
...this.props.action, ...this.props.action,
...ev.detail.value, ...ev.detail.value,

View File

@ -1,4 +1,4 @@
import { h, Component } from "preact"; import { h } from "preact";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import YAMLTextArea from "../yaml_textarea"; import YAMLTextArea from "../yaml_textarea";
@ -6,6 +6,8 @@ import { onChangeEvent } from "../../../../common/preact/event";
import { LocalizeFunc } from "../../../../common/translations/localize"; import { LocalizeFunc } from "../../../../common/translations/localize";
import { EventAction } from "../../../../data/script"; import { EventAction } from "../../../../data/script";
import { AutomationComponent } from "../automation-component";
interface Props { interface Props {
index: number; index: number;
action: EventAction; action: EventAction;
@ -13,7 +15,7 @@ interface Props {
onChange: (index: number, action: EventAction) => void; onChange: (index: number, action: EventAction) => void;
} }
export default class EventActionForm extends Component<Props> { export default class EventActionForm extends AutomationComponent<Props> {
private onChange: (event: Event) => void; private onChange: (event: Event) => void;
static get defaultConfig(): EventAction { static get defaultConfig(): EventAction {
@ -57,6 +59,9 @@ export default class EventActionForm extends Component<Props> {
} }
private serviceDataChanged(eventData) { private serviceDataChanged(eventData) {
if (!this.initialized) {
return;
}
this.props.onChange(this.props.index, { this.props.onChange(this.props.index, {
...this.props.action, ...this.props.action,
event_data: eventData, event_data: eventData,

View File

@ -1,7 +1,8 @@
import { h, Component } from "preact"; import { h } from "preact";
import "../../../../components/entity/ha-entity-picker"; import "../../../../components/entity/ha-entity-picker";
import { AutomationComponent } from "../automation-component";
export default class SceneAction extends Component<any> { export default class SceneAction extends AutomationComponent<any> {
constructor() { constructor() {
super(); super();

View File

@ -1,11 +1,12 @@
import { h, Component } from "preact"; import { h } from "preact";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import "../../../../components/ha-textarea"; import "../../../../components/ha-textarea";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component";
export default class WaitAction extends Component<any> { export default class WaitAction extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -11,7 +11,7 @@ import {
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class DeviceTrigger extends AutomationComponent { export default class DeviceTrigger extends AutomationComponent<any, any> {
private _origTrigger; private _origTrigger;
constructor() { constructor() {

View File

@ -6,7 +6,7 @@ import YAMLTextArea from "../yaml_textarea";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class EventTrigger extends AutomationComponent { export default class EventTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -7,7 +7,7 @@ import "../../../../components/entity/ha-entity-picker";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class GeolocationTrigger extends AutomationComponent { export default class GeolocationTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -5,7 +5,7 @@ import "@polymer/paper-radio-group/paper-radio-group";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class HassTrigger extends AutomationComponent { export default class HassTrigger extends AutomationComponent<any> {
constructor() { constructor() {
super(); super();

View File

@ -5,7 +5,7 @@ import "@polymer/paper-input/paper-input";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class MQTTTrigger extends AutomationComponent { export default class MQTTTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor(props) { constructor(props) {
super(props); super(props);

View File

@ -8,7 +8,7 @@ import "../../../../components/entity/ha-entity-picker";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class NumericStateTrigger extends AutomationComponent { export default class NumericStateTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor(props) { constructor(props) {
super(props); super(props);

View File

@ -6,7 +6,7 @@ import "../../../../components/entity/ha-entity-picker";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class StateTrigger extends AutomationComponent { export default class StateTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor(props) { constructor(props) {
super(props); super(props);

View File

@ -7,7 +7,7 @@ import "@polymer/paper-radio-group/paper-radio-group";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class SunTrigger extends AutomationComponent { export default class SunTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -5,7 +5,7 @@ import "../../../../components/ha-textarea";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class TemplateTrigger extends AutomationComponent { export default class TemplateTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -5,7 +5,7 @@ import "@polymer/paper-input/paper-input";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class TimeTrigger extends AutomationComponent { export default class TimeTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -5,7 +5,7 @@ import "@polymer/paper-input/paper-input";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class TimePatternTrigger extends AutomationComponent { export default class TimePatternTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -5,7 +5,7 @@ import "@polymer/paper-input/paper-input";
import { onChangeEvent } from "../../../../common/preact/event"; import { onChangeEvent } from "../../../../common/preact/event";
import { AutomationComponent } from "../automation-component"; import { AutomationComponent } from "../automation-component";
export default class WebhookTrigger extends AutomationComponent { export default class WebhookTrigger extends AutomationComponent<any> {
private onChange: (obj: any) => void; private onChange: (obj: any) => void;
constructor() { constructor() {
super(); super();

View File

@ -12,7 +12,7 @@ function zoneAndLocationFilter(stateObj) {
return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone"; return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone";
} }
export default class ZoneTrigger extends AutomationComponent { export default class ZoneTrigger extends AutomationComponent<any> {
constructor() { constructor() {
super(); super();