diff --git a/src/data/automation.ts b/src/data/automation.ts index 263be8b443..71a7e549c2 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -4,6 +4,7 @@ import { } from "home-assistant-js-websocket"; import { HomeAssistant } from "../types"; import { navigate } from "../common/navigate"; +import { DeviceCondition, DeviceTrigger } from "./device_automation"; export interface AutomationEntity extends HassEntityBase { attributes: HassEntityAttributeBase & { @@ -15,11 +16,162 @@ export interface AutomationEntity extends HassEntityBase { export interface AutomationConfig { alias: string; description: string; - trigger: any[]; - condition?: any[]; + trigger: Trigger[]; + condition?: Condition[]; action: any[]; } +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 LogicalCondition { + condition: "and" | "or"; + conditions: Condition[]; +} + +export interface StateCondition { + condition: "state"; + entity_id: string; + state: string | number; +} + +export interface NumericStateCondition { + condition: "numeric_state"; + entity_id: string; + above?: number; + below?: number; + value_template?: string; +} + +export interface SunCondition { + condition: "sun"; + after_offset: number; + before_offset: number; + after: "sunrise" | "sunset"; + before: "sunrise" | "sunset"; +} + +export interface ZoneCondition { + condition: "zone"; + entity_id: string; + zone: string; +} + +export interface TimeCondition { + condition: "time"; + after: string; + before: string; +} + +export interface TemplateCondition { + condition: "template"; + value_template: string; +} + +export type Condition = + | StateCondition + | NumericStateCondition + | SunCondition + | ZoneCondition + | TimeCondition + | TemplateCondition + | DeviceCondition + | LogicalCondition; + export const deleteAutomation = (hass: HomeAssistant, id: string) => hass.callApi("DELETE", `config/automation/config/${id}`); diff --git a/src/panels/config/automation/condition/ha-automation-condition-editor.ts b/src/panels/config/automation/condition/ha-automation-condition-editor.ts new file mode 100644 index 0000000000..5b84048539 --- /dev/null +++ b/src/panels/config/automation/condition/ha-automation-condition-editor.ts @@ -0,0 +1,128 @@ +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 { customElement, html, LitElement, property } from "lit-element"; +import { dynamicElement } from "../../../../common/dom/dynamic-element-directive"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-card"; +import { HomeAssistant } from "../../../../types"; + +import "./types/ha-automation-condition-device"; +import "./types/ha-automation-condition-state"; +import "./types/ha-automation-condition-numeric_state"; +import "./types/ha-automation-condition-sun"; +import "./types/ha-automation-condition-template"; +import "./types/ha-automation-condition-time"; +import "./types/ha-automation-condition-zone"; +import "./types/ha-automation-condition-and"; +import "./types/ha-automation-condition-or"; +import { Condition } from "../../../../data/automation"; + +const OPTIONS = [ + "device", + "and", + "or", + "state", + "numeric_state", + "sun", + "template", + "time", + "zone", +]; + +@customElement("ha-automation-condition-editor") +export default class HaAutomationConditionEditor extends LitElement { + @property() public hass!: HomeAssistant; + @property() public condition!: Condition; + @property() public yamlMode = false; + + protected render() { + if (!this.condition) { + return html``; + } + const selected = OPTIONS.indexOf(this.condition.condition); + const yamlMode = this.yamlMode || selected === -1; + return html` + ${yamlMode + ? html` +
+ ${selected === -1 + ? html` + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.unsupported_condition", + "condition", + this.condition.condition + )} + ` + : ""} + +
+ ` + : html` + + + ${OPTIONS.map( + (opt) => html` + + ${this.hass.localize( + `ui.panel.config.automation.editor.conditions.type.${opt}.label` + )} + + ` + )} + + +
+ ${dynamicElement( + `ha-automation-condition-${this.condition.condition}`, + { hass: this.hass, condition: this.condition } + )} +
+ `} + `; + } + + private _typeChanged(ev: CustomEvent) { + const type = ((ev.target as PaperListboxElement)?.selectedItem as any) + ?.condition; + + if (!type) { + return; + } + + const elClass = customElements.get(`ha-automation-condition-${type}`); + + if (type !== this.condition.condition) { + fireEvent(this, "value-changed", { + value: { + condition: type, + ...elClass.defaultConfig, + }, + }); + } + } + + private _onYamlChange(ev: CustomEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { value: ev.detail.value }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition-editor": HaAutomationConditionEditor; + } +} diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts new file mode 100644 index 0000000000..e3552302ce --- /dev/null +++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts @@ -0,0 +1,146 @@ +import "@polymer/paper-icon-button/paper-icon-button"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-menu-button/paper-menu-button"; +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, +} from "lit-element"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-card"; +import { HomeAssistant } from "../../../../types"; + +import "./ha-automation-condition-editor"; +import { Condition } from "../../../../data/automation"; + +export interface ConditionElement extends LitElement { + condition: Condition; +} + +export const handleChangeEvent = ( + element: ConditionElement, + ev: CustomEvent +) => { + ev.stopPropagation(); + const name = (ev.target as any)?.name; + if (!name) { + return; + } + const newVal = ev.detail.value; + + if ((element.condition[name] || "") === newVal) { + return; + } + + let newCondition: Condition; + if (!newVal) { + newCondition = { ...element.condition }; + delete newCondition[name]; + } else { + newCondition = { ...element.condition, [name]: newVal }; + } + fireEvent(element, "value-changed", { value: newCondition }); +}; + +@customElement("ha-automation-condition-row") +export default class HaAutomationConditionRow extends LitElement { + @property() public hass!: HomeAssistant; + @property() public condition!: Condition; + @property() private _yamlMode = false; + + protected render() { + if (!this.condition) { + return html``; + } + 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.conditions.duplicate" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.delete" + )} + + + +
+ +
+
+ `; + } + + private _onDelete() { + if ( + confirm( + this.hass.localize( + "ui.panel.config.automation.editor.conditions.delete_confirm" + ) + ) + ) { + fireEvent(this, "value-changed", { value: null }); + } + } + + 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-condition-row": HaAutomationConditionRow; + } +} diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts new file mode 100644 index 0000000000..4a25aa36ab --- /dev/null +++ b/src/panels/config/automation/condition/ha-automation-condition.ts @@ -0,0 +1,92 @@ +import { + LitElement, + customElement, + html, + property, + CSSResult, + css, +} from "lit-element"; +import "@material/mwc-button"; +import "../../../../components/ha-card"; + +import { HaStateCondition } from "./types/ha-automation-condition-state"; + +import { fireEvent } from "../../../../common/dom/fire_event"; +import { HomeAssistant } from "../../../../types"; + +import "./ha-automation-condition-row"; +import { Condition } from "../../../../data/automation"; + +@customElement("ha-automation-condition") +export default class HaAutomationCondition extends LitElement { + @property() public hass!: HomeAssistant; + @property() public conditions!: Condition[]; + + protected render() { + return html` + ${this.conditions.map( + (cond, idx) => html` + + ` + )} + +
+ + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.add" + )} + +
+
+ `; + } + + private _addCondition() { + const conditions = this.conditions.concat({ + condition: "state", + ...HaStateCondition.defaultConfig, + }); + + fireEvent(this, "value-changed", { value: conditions }); + } + + private _conditionChanged(ev: CustomEvent) { + ev.stopPropagation(); + const conditions = [...this.conditions]; + const newValue = ev.detail.value; + const index = (ev.target as any).index; + + if (newValue === null) { + conditions.splice(index, 1); + } else { + conditions[index] = newValue; + } + + fireEvent(this, "value-changed", { value: conditions }); + } + + static get styles(): CSSResult { + return css` + ha-automation-condition-row, + ha-card { + display: block; + margin-top: 16px; + } + .add-card mwc-button { + display: block; + text-align: center; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition": HaAutomationCondition; + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-and.ts b/src/panels/config/automation/condition/types/ha-automation-condition-and.ts new file mode 100644 index 0000000000..111bd727b5 --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-and.ts @@ -0,0 +1,11 @@ +import { HaLogicalCondition } from "./ha-automation-condition-logical"; +import { customElement } from "lit-element"; + +@customElement("ha-automation-condition-and") +export class HaAndCondition extends HaLogicalCondition {} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition-and": HaAndCondition; + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-device.ts b/src/panels/config/automation/condition/types/ha-automation-condition-device.ts new file mode 100644 index 0000000000..02f36ea432 --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-device.ts @@ -0,0 +1,131 @@ +import "../../../../../components/device/ha-device-picker"; +import "../../../../../components/device/ha-device-condition-picker"; +import "../../../../../components/ha-form/ha-form"; + +import { + fetchDeviceConditionCapabilities, + deviceAutomationsEqual, + DeviceCondition, +} 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-condition-device") +export class HaDeviceCondition extends LitElement { + @property() public hass!: HomeAssistant; + @property() public condition!: DeviceCondition; + @property() private _deviceId?: string; + @property() private _capabilities?; + private _origCondition?: DeviceCondition; + + public static get defaultConfig() { + return { + device_id: "", + domain: "", + entity_id: "", + }; + } + + protected render() { + if (this._deviceId === undefined) { + this._deviceId = this.condition.device_id; + } + const extraFieldsData = + this._capabilities && this._capabilities.extra_fields + ? this._capabilities.extra_fields.map((item) => { + return { [item.name]: this.condition[item.name] }; + }) + : undefined; + + return html` + + + ${extraFieldsData + ? html` + + ` + : ""} + `; + } + + protected firstUpdated() { + if (!this._capabilities) { + this._getCapabilities(); + } + if (this.condition) { + this._origCondition = this.condition; + } + } + + protected updated(changedPros) { + const prevCondition = changedPros.get("condition"); + if ( + prevCondition && + !deviceAutomationsEqual(prevCondition, this.condition) + ) { + this._getCapabilities(); + } + } + + private async _getCapabilities() { + const condition = this.condition; + + this._capabilities = condition.domain + ? await fetchDeviceConditionCapabilities(this.hass, condition) + : null; + } + + private _devicePicked(ev) { + ev.stopPropagation(); + this._deviceId = ev.target.value; + } + + private _deviceConditionPicked(ev) { + ev.stopPropagation(); + let condition = ev.detail.value; + if ( + this._origCondition && + deviceAutomationsEqual(this._origCondition, condition) + ) { + condition = this._origCondition; + } + fireEvent(this, "value-changed", { value: condition }); + } + + private _extraFieldsChanged(ev) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { + ...this.condition, + ...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.conditions.type.device.extra_fields.${schema.name}` + ) || schema.name; + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts b/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts new file mode 100644 index 0000000000..5cff91242d --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts @@ -0,0 +1,39 @@ +import { customElement, html, LitElement, property } from "lit-element"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { HomeAssistant } from "../../../../../types"; +import { ConditionElement } from "../ha-automation-condition-row"; +import "../ha-automation-condition"; +import { LogicalCondition } from "../../../../../data/automation"; + +@customElement("ha-automation-condition-logical") +export class HaLogicalCondition extends LitElement implements ConditionElement { + @property() public hass!: HomeAssistant; + @property() public condition!: LogicalCondition; + + public static get defaultConfig() { + return { conditions: [{ condition: "state" }] }; + } + + protected render() { + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.condition, conditions: ev.detail.value }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition-logical": HaLogicalCondition; + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-numeric_state.ts b/src/panels/config/automation/condition/types/ha-automation-condition-numeric_state.ts new file mode 100644 index 0000000000..ae4117ec46 --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-numeric_state.ts @@ -0,0 +1,76 @@ +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 { handleChangeEvent } from "../ha-automation-condition-row"; +import { NumericStateCondition } from "../../../../../data/automation"; + +@customElement("ha-automation-condition-numeric_state") +export default class HaNumericStateCondition extends LitElement { + @property() public hass!: HomeAssistant; + @property() public condition!: NumericStateCondition; + + public static get defaultConfig() { + return { + entity_id: "", + }; + } + + public render() { + const { value_template, entity_id, below, above } = this.condition; + + return html` + + + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } + + private _entityPicked(ev) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.condition, entity_id: ev.detail.value }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition-numeric_state": HaNumericStateCondition; + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-or.ts b/src/panels/config/automation/condition/types/ha-automation-condition-or.ts new file mode 100644 index 0000000000..796b8a012e --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-or.ts @@ -0,0 +1,11 @@ +import { HaLogicalCondition } from "./ha-automation-condition-logical"; +import { customElement } from "lit-element"; + +@customElement("ha-automation-condition-or") +export class HaOrCondition extends HaLogicalCondition {} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition-or": HaOrCondition; + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts new file mode 100644 index 0000000000..11ff6cc085 --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts @@ -0,0 +1,59 @@ +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, + ConditionElement, +} from "../ha-automation-condition-row"; +import { PolymerChangedEvent } from "../../../../../polymer-types"; +import { StateCondition } from "../../../../../data/automation"; + +@customElement("ha-automation-condition-state") +export class HaStateCondition extends LitElement implements ConditionElement { + @property() public hass!: HomeAssistant; + @property() public condition!: StateCondition; + + public static get defaultConfig() { + return { entity_id: "", state: "" }; + } + + protected render() { + const { entity_id, state } = this.condition; + + return html` + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } + + private _entityPicked(ev: PolymerChangedEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.condition, entity_id: ev.detail.value }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition-state": HaStateCondition; + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-sun.ts b/src/panels/config/automation/condition/types/ha-automation-condition-sun.ts new file mode 100644 index 0000000000..8975e889fa --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-sun.ts @@ -0,0 +1,107 @@ +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 { + handleChangeEvent, + ConditionElement, +} from "../ha-automation-condition-row"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { SunCondition } from "../../../../../data/automation"; + +@customElement("ha-automation-condition-sun") +export class HaSunCondition extends LitElement implements ConditionElement { + @property() public hass!: HomeAssistant; + @property() public condition!: SunCondition; + + public static get defaultConfig() { + return {}; + } + + protected render() { + const { after, after_offset, before, before_offset } = this.condition; + return html` + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.type.sun.sunrise" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.type.sun.sunset" + )} + + + + + + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.type.sun.sunrise" + )} + + + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.type.sun.sunset" + )} + + + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } + + private _radioGroupPicked(ev) { + const key = ev.target.name; + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { + ...this.condition, + [key]: (ev.target as PaperRadioGroupElement).selected, + }, + }); + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-template.ts b/src/panels/config/automation/condition/types/ha-automation-condition-template.ts new file mode 100644 index 0000000000..b33cfa6d2f --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-template.ts @@ -0,0 +1,34 @@ +import "../../../../../components/ha-textarea"; +import { LitElement, property, html, customElement } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { handleChangeEvent } from "../ha-automation-condition-row"; +import { TemplateCondition } from "../../../../../data/automation"; + +@customElement("ha-automation-condition-template") +export class HaTemplateCondition extends LitElement { + @property() public hass!: HomeAssistant; + @property() public condition!: TemplateCondition; + + public static get defaultConfig() { + return { value_template: "" }; + } + + protected render() { + const { value_template } = this.condition; + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-time.ts b/src/panels/config/automation/condition/types/ha-automation-condition-time.ts new file mode 100644 index 0000000000..b42af4f4af --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-time.ts @@ -0,0 +1,44 @@ +import "@polymer/paper-input/paper-input"; +import { LitElement, html, property, customElement } from "lit-element"; +import { HomeAssistant } from "../../../../../types"; +import { + handleChangeEvent, + ConditionElement, +} from "../ha-automation-condition-row"; +import { TimeCondition } from "../../../../../data/automation"; + +@customElement("ha-automation-condition-time") +export class HaTimeCondition extends LitElement implements ConditionElement { + @property() public hass!: HomeAssistant; + @property() public condition!: TimeCondition; + + public static get defaultConfig() { + return {}; + } + + protected render() { + const { after, before } = this.condition; + return html` + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + handleChangeEvent(this, ev); + } +} diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-zone.ts b/src/panels/config/automation/condition/types/ha-automation-condition-zone.ts new file mode 100644 index 0000000000..edd2fac186 --- /dev/null +++ b/src/panels/config/automation/condition/types/ha-automation-condition-zone.ts @@ -0,0 +1,78 @@ +import "@polymer/paper-radio-button/paper-radio-button"; +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 { PolymerChangedEvent } from "../../../../../polymer-types"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { ZoneCondition } from "../../../../../data/automation"; + +function zoneAndLocationFilter(stateObj) { + return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone"; +} + +@customElement("ha-automation-condition-zone") +export class HaZoneCondition extends LitElement { + @property() public hass!: HomeAssistant; + @property() public condition!: ZoneCondition; + + public static get defaultConfig() { + return { + entity_id: "", + zone: "", + }; + } + + protected render() { + const { entity_id, zone } = this.condition; + return html` + + + + `; + } + + private _entityPicked(ev: PolymerChangedEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.condition, entity_id: ev.detail.value }, + }); + } + + private _zonePicked(ev: PolymerChangedEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { + value: { ...this.condition, zone: ev.detail.value }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-condition-zone": HaZoneCondition; + } +} diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts index 90e4baf0a2..a6b2c91736 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -30,7 +30,8 @@ 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"; + +import { Trigger } from "../../../../data/automation"; const OPTIONS = [ "device", @@ -48,103 +49,6 @@ const OPTIONS = [ "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; } @@ -181,11 +85,9 @@ export default class HaAutomationTriggerRow extends LitElement { if (!this.trigger) { return html``; } - const hasEditor = OPTIONS.includes(this.trigger.platform); - if (!hasEditor) { - this._yamlMode = true; - } const selected = OPTIONS.indexOf(this.trigger.platform); + const yamlMode = this._yamlMode || selected === -1; + return html`
@@ -202,8 +104,11 @@ export default class HaAutomationTriggerRow extends LitElement { slot="dropdown-trigger" > - - ${this._yamlMode + + ${yamlMode ? this.hass.localize( "ui.panel.config.automation.editor.edit_ui" ) @@ -224,10 +129,10 @@ export default class HaAutomationTriggerRow extends LitElement {
- ${this._yamlMode + ${yamlMode ? html`
- ${!hasEditor + ${selected === -1 ? html` ${this.hass.localize( "ui.panel.config.automation.editor.triggers.unsupported_platform", diff --git a/src/panels/config/automation/trigger/ha-automation-trigger.ts b/src/panels/config/automation/trigger/ha-automation-trigger.ts index 108c4c0248..71f2e303fd 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger.ts @@ -13,41 +13,42 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import { HomeAssistant } from "../../../../types"; import "./ha-automation-trigger-row"; +import { HaStateTrigger } from "./types/ha-automation-trigger-state"; +import { Trigger } from "../../../../data/automation"; @customElement("ha-automation-trigger") export default class HaAutomationTrigger extends LitElement { @property() public hass!: HomeAssistant; - @property() public triggers; + @property() public triggers!: Trigger[]; protected render() { return html` -
- ${this.triggers.map( - (trg, idx) => html` - - ` - )} - -
- - ${this.hass.localize( - "ui.panel.config.automation.editor.triggers.add" - )} - -
-
-
+ ${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", + ...HaStateTrigger.defaultConfig, }); fireEvent(this, "value-changed", { value: triggers }); @@ -70,12 +71,9 @@ export default class HaAutomationTrigger extends LitElement { static get styles(): CSSResult { return css` - .triggers, - .script { - margin-top: -16px; - } - .triggers ha-card, - .script ha-card { + ha-automation-trigger-row, + ha-card { + display: block; margin-top: 16px; } .add-card mwc-button { 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 index 130a5f4e5b..7a70ba055a 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts @@ -4,11 +4,11 @@ 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"; +import { EventTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-event") export class HaEventTrigger extends LitElement implements TriggerElement { 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 index 491bcb9d91..1fadc057c9 100644 --- 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 @@ -5,11 +5,9 @@ import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-g 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 { handleChangeEvent } from "../ha-automation-trigger-row"; import { fireEvent } from "../../../../../common/dom/fire_event"; +import { GeoLocationTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-geo_location") export default class HaGeolocationTrigger extends LitElement { 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 index 328df5ae7d..2f7d6064ef 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant.ts @@ -5,7 +5,7 @@ import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-g 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"; +import { HassTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-homeassistant") export default class HaHassTrigger extends LitElement { 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 index ccf951be80..51dde6bc27 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt.ts @@ -4,8 +4,8 @@ import { HomeAssistant } from "../../../../../types"; import { handleChangeEvent, TriggerElement, - MqttTrigger, } from "../ha-automation-trigger-row"; +import { MqttTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-mqtt") export class HaMQTTTrigger extends LitElement implements TriggerElement { 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 index 5ac8310b4e..fb7ecad6a0 100644 --- 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 @@ -5,11 +5,8 @@ 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"; +import { handleChangeEvent } from "../ha-automation-trigger-row"; +import { NumericStateTrigger, ForDict } from "../../../../../data/automation"; @customElement("ha-automation-trigger-numeric_state") export default class HaNumericStateTrigger extends LitElement { 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 index 13ff38961b..2f93511553 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts @@ -6,10 +6,9 @@ import { HomeAssistant } from "../../../../../types"; import { handleChangeEvent, TriggerElement, - StateTrigger, - ForDict, } from "../ha-automation-trigger-row"; import { PolymerChangedEvent } from "../../../../../polymer-types"; +import { StateTrigger, ForDict } from "../../../../../data/automation"; @customElement("ha-automation-trigger-state") export class HaStateTrigger extends LitElement implements TriggerElement { 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 index 9a855f96fd..5bd0ea9e30 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-sun.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-sun.ts @@ -6,11 +6,11 @@ import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-g 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"; +import { SunTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-sun") export class HaSunTrigger extends LitElement implements TriggerElement { 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 index b229f2e065..f321750103 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-template.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-template.ts @@ -1,10 +1,8 @@ import "../../../../../components/ha-textarea"; import { LitElement, property, html, customElement } from "lit-element"; import { HomeAssistant } from "../../../../../types"; -import { - TemplateTrigger, - handleChangeEvent, -} from "../ha-automation-trigger-row"; +import { handleChangeEvent } from "../ha-automation-trigger-row"; +import { TemplateTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-template") export class HaTemplateTrigger extends LitElement { 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 index 1222a72ff5..1a4de2776c 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts @@ -2,10 +2,10 @@ 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"; +import { TimeTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-time") export class HaTimeTrigger extends LitElement implements TriggerElement { 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 index 95b534961a..c15352abbb 100644 --- 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 @@ -3,9 +3,9 @@ import { LitElement, property, html, customElement } from "lit-element"; import { TriggerElement, handleChangeEvent, - TimePatternTrigger, } from "../ha-automation-trigger-row"; import { HomeAssistant } from "../../../../../types"; +import { TimePatternTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-time_pattern") export class HaTimePatternTrigger extends LitElement implements TriggerElement { 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 index fa7c1fcf27..0456665d4c 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-webhook.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-webhook.ts @@ -1,10 +1,8 @@ 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"; +import { handleChangeEvent } from "../ha-automation-trigger-row"; +import { WebhookTrigger } from "../../../../../data/automation"; @customElement("ha-automation-trigger-webhook") export class HaWebhookTrigger extends LitElement { 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 index d1f71598b0..935ed86dd8 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-zone.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-zone.ts @@ -8,9 +8,9 @@ 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"; +import { ZoneTrigger } from "../../../../../data/automation"; function zoneAndLocationFilter(stateObj) { return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone"; diff --git a/src/panels/config/js/automation.tsx b/src/panels/config/js/automation.tsx index fdabbc953b..41b4ee7bb9 100644 --- a/src/panels/config/js/automation.tsx +++ b/src/panels/config/js/automation.tsx @@ -6,8 +6,8 @@ import "../../../components/ha-card"; import "../../../components/ha-textarea"; import "../automation/trigger/ha-automation-trigger"; +import "../automation/condition/ha-automation-condition"; -import Condition from "./condition/index"; import Script from "./script/index"; export default class Automation extends Component { @@ -31,8 +31,11 @@ export default class Automation extends Component { this.props.onChange({ ...this.props.automation, trigger: ev.detail.value }); } - public conditionChanged(condition) { - this.props.onChange({ ...this.props.automation, condition }); + public conditionChanged(ev: CustomEvent) { + this.props.onChange({ + ...this.props.automation, + condition: ev.detail.value, + }); } public actionChanged(action) { @@ -117,11 +120,10 @@ export default class Automation extends Component { )} - diff --git a/src/panels/config/js/condition/condition_edit.tsx b/src/panels/config/js/condition/condition_edit.tsx deleted file mode 100644 index 2eb2107713..0000000000 --- a/src/panels/config/js/condition/condition_edit.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { h, Component } from "preact"; -import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light"; -import "@polymer/paper-listbox/paper-listbox"; -import "@polymer/paper-item/paper-item"; - -import YAMLTextArea from "../yaml_textarea"; - -import DeviceCondition from "./device"; -import LogicalCondition from "./logical"; -import NumericStateCondition from "./numeric_state"; -import StateCondition from "./state"; -import SunCondition from "./sun"; -import TemplateCondition from "./template"; -import TimeCondition from "./time"; -import ZoneCondition from "./zone"; - -const TYPES = { - and: LogicalCondition, - device: DeviceCondition, - numeric_state: NumericStateCondition, - or: LogicalCondition, - state: StateCondition, - sun: SunCondition, - template: TemplateCondition, - time: TimeCondition, - zone: ZoneCondition, -}; - -const OPTIONS = Object.keys(TYPES).sort(); - -export default class ConditionEdit extends Component { - constructor() { - super(); - - this.typeChanged = this.typeChanged.bind(this); - this.onYamlChange = this.onYamlChange.bind(this); - } - - public typeChanged(ev) { - const type = ev.target.selectedItem.attributes.condition.value; - - if (type !== this.props.condition.condition) { - this.props.onChange(this.props.index, { - condition: type, - ...TYPES[type].defaultConfig, - }); - } - } - - public render({ index, condition, onChange, hass, localize, yamlMode }) { - // tslint:disable-next-line: variable-name - const Comp = TYPES[condition.condition]; - const selected = OPTIONS.indexOf(condition.condition); - - if (yamlMode || !Comp) { - return ( -
- {!Comp && ( -
- {localize( - "ui.panel.config.automation.editor.conditions.unsupported_condition", - "condition", - condition.condition - )} -
- )} - -
- ); - } - - return ( -
- - - {OPTIONS.map((opt) => ( - - {localize( - `ui.panel.config.automation.editor.conditions.type.${opt}.label` - )} - - ))} - - - -
- ); - } - - private onYamlChange(condition) { - this.props.onChange(this.props.index, condition); - } -} diff --git a/src/panels/config/js/condition/condition_row.tsx b/src/panels/config/js/condition/condition_row.tsx deleted file mode 100644 index 386b469a0d..0000000000 --- a/src/panels/config/js/condition/condition_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-listbox/paper-listbox"; -import "@polymer/paper-item/paper-item"; -import "../../../../components/ha-card"; - -import ConditionEdit from "./condition_edit"; - -export default class ConditionRow 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 onDelete() { - // eslint-disable-next-line - if ( - confirm( - this.props.localize( - "ui.panel.config.automation.editor.conditions.delete_confirm" - ) - ) - ) { - this.props.onChange(this.props.index, null); - } - } - - 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.conditions.duplicate" - )} - - - {props.localize( - "ui.panel.config.automation.editor.conditions.delete" - )} - - - -
- -
-
- ); - } - - private switchYamlMode() { - this.setState({ - yamlMode: !this.state.yamlMode, - }); - } -} diff --git a/src/panels/config/js/condition/device.tsx b/src/panels/config/js/condition/device.tsx deleted file mode 100644 index fb93f9ae6a..0000000000 --- a/src/panels/config/js/condition/device.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { h } from "preact"; - -import "../../../../components/device/ha-device-picker"; -import "../../../../components/device/ha-device-condition-picker"; -import "../../../../components/ha-form/ha-form"; - -import { - fetchDeviceConditionCapabilities, - deviceAutomationsEqual, -} from "../../../../data/device_automation"; - -import { AutomationComponent } from "../automation-component"; - -export default class DeviceCondition extends AutomationComponent { - private _origCondition; - - constructor() { - super(); - this.devicePicked = this.devicePicked.bind(this); - this.deviceConditionPicked = this.deviceConditionPicked.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 deviceConditionPicked(ev) { - if (!this.initialized) { - return; - } - let condition = ev.target.value; - if ( - this._origCondition && - deviceAutomationsEqual(this._origCondition, condition) - ) { - condition = this._origCondition; - } - this.props.onChange(this.props.index, condition); - } - - /* eslint-disable camelcase */ - public render({ condition, hass }, { device_id, capabilities }) { - if (device_id === undefined) { - device_id = condition.device_id; - } - const extraFieldsData = - capabilities && capabilities.extra_fields - ? capabilities.extra_fields.map((item) => { - return { [item.name]: this.props.condition[item.name] }; - }) - : undefined; - - return ( -
- - - {extraFieldsData && ( - - )} -
- ); - } - - public componentDidMount() { - this.initialized = true; - if (!this.state.capabilities) { - this._getCapabilities(); - } - if (this.props.condition) { - this._origCondition = this.props.condition; - } - } - - public componentDidUpdate(prevProps) { - if (!deviceAutomationsEqual(prevProps.condition, this.props.condition)) { - this._getCapabilities(); - } - } - - private async _getCapabilities() { - const condition = this.props.condition; - - const capabilities = condition.domain - ? await fetchDeviceConditionCapabilities(this.props.hass, condition) - : null; - this.setState({ ...this.state, capabilities }); - } - - private _extraFieldsChanged(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.condition, - ...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.condition.type.device.extra_fields.${schema.name}` - ) || schema.name; - } -} - -(DeviceCondition as any).defaultConfig = { - device_id: "", - domain: "", - entity_id: "", -}; diff --git a/src/panels/config/js/condition/index.tsx b/src/panels/config/js/condition/index.tsx deleted file mode 100644 index 16c4c05c57..0000000000 --- a/src/panels/config/js/condition/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { h, Component } from "preact"; -import "@material/mwc-button"; -import "../../../../components/ha-card"; - -import ConditionRow from "./condition_row"; - -export default class Condition extends Component { - constructor() { - super(); - - this.addCondition = this.addCondition.bind(this); - this.conditionChanged = this.conditionChanged.bind(this); - } - - public addCondition() { - const condition = this.props.condition.concat({ - condition: "state", - }); - - this.props.onChange(condition); - } - - public conditionChanged(index, newValue) { - const condition = this.props.condition.concat(); - - if (newValue === null) { - condition.splice(index, 1); - } else { - condition[index] = newValue; - } - - this.props.onChange(condition); - } - - public render({ condition, hass, localize }) { - return ( -
- {condition.map((cnd, idx) => ( - - ))} - -
- - {localize("ui.panel.config.automation.editor.conditions.add")} - -
-
-
- ); - } -} diff --git a/src/panels/config/js/condition/logical.tsx b/src/panels/config/js/condition/logical.tsx deleted file mode 100644 index 8484b155be..0000000000 --- a/src/panels/config/js/condition/logical.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { h } from "preact"; - -import Condition from "./index"; -import { AutomationComponent } from "../automation-component"; - -export default class LogicalCondition extends AutomationComponent { - constructor() { - super(); - this.conditionChanged = this.conditionChanged.bind(this); - } - - public conditionChanged(conditions) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.condition, - conditions, - }); - } - - /* eslint-disable camelcase */ - public render({ condition, hass, localize }) { - return ( -
- -
- ); - } -} - -(LogicalCondition as any).defaultConfig = { - conditions: [{ condition: "state" }], -}; diff --git a/src/panels/config/js/condition/numeric_state.tsx b/src/panels/config/js/condition/numeric_state.tsx deleted file mode 100644 index a850d3098b..0000000000 --- a/src/panels/config/js/condition/numeric_state.tsx +++ /dev/null @@ -1,71 +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 NumericStateCondition extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "condition"); - this.entityPicked = this.entityPicked.bind(this); - } - - public entityPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.condition, - entity_id: ev.target.value, - }); - } - - /* eslint-disable camelcase */ - public render({ condition, hass, localize }) { - const { value_template, entity_id, below, above } = condition; - return ( -
- - - - -
- ); - } -} - -(NumericStateCondition as any).defaultConfig = { - entity_id: "", -}; diff --git a/src/panels/config/js/condition/state.tsx b/src/panels/config/js/condition/state.tsx deleted file mode 100644 index 4c6e5d8dbc..0000000000 --- a/src/panels/config/js/condition/state.tsx +++ /dev/null @@ -1,56 +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 StateCondition extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "condition"); - this.entityPicked = this.entityPicked.bind(this); - } - - public entityPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.condition, - entity_id: ev.target.value, - }); - } - - /* eslint-disable camelcase */ - public render({ condition, hass, localize }) { - const { entity_id, state } = condition; - const cndFor = condition.for; - return ( -
- - - {cndFor &&
For: {JSON.stringify(cndFor, null, 2)}
} -
- ); - } -} - -(StateCondition as any).defaultConfig = { - entity_id: "", - state: "", -}; diff --git a/src/panels/config/js/condition/sun.tsx b/src/panels/config/js/condition/sun.tsx deleted file mode 100644 index 104e9f7b27..0000000000 --- a/src/panels/config/js/condition/sun.tsx +++ /dev/null @@ -1,112 +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 SunCondition extends AutomationComponent { - private onChange: (obj: any) => void; - private afterPicked: (obj: any) => void; - private beforePicked: (obj: any) => void; - - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "condition"); - this.afterPicked = this.radioGroupPicked.bind(this, "after"); - this.beforePicked = this.radioGroupPicked.bind(this, "before"); - } - - public radioGroupPicked(key, ev) { - if (!this.initialized) { - return; - } - const condition = { ...this.props.condition }; - - if (ev.target.selected) { - condition[key] = ev.target.selected; - } else { - delete condition[key]; - } - - this.props.onChange(this.props.index, condition); - } - - public render({ condition, localize }) { - /* eslint-disable camelcase */ - const { after, after_offset, before, before_offset } = condition; - return ( -
- - - - {localize( - "ui.panel.config.automation.editor.conditions.type.sun.sunrise" - )} - - - {localize( - "ui.panel.config.automation.editor.conditions.type.sun.sunset" - )} - - - - - - - - - {localize( - "ui.panel.config.automation.editor.conditions.type.sun.sunrise" - )} - - - {localize( - "ui.panel.config.automation.editor.conditions.type.sun.sunset" - )} - - - - -
- ); - } -} - -(SunCondition as any).defaultConfig = {}; diff --git a/src/panels/config/js/condition/template.tsx b/src/panels/config/js/condition/template.tsx deleted file mode 100644 index 4d73ff2913..0000000000 --- a/src/panels/config/js/condition/template.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { h } from "preact"; -import "../../../../components/ha-textarea"; - -import { onChangeEvent } from "../../../../common/preact/event"; -import { AutomationComponent } from "../automation-component"; - -export default class TemplateCondition extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "condition"); - } - - public render({ condition, localize }) { - /* eslint-disable camelcase */ - const { value_template } = condition; - return ( -
- -
- ); - } -} - -(TemplateCondition as any).defaultConfig = { - value_template: "", -}; diff --git a/src/panels/config/js/condition/time.tsx b/src/panels/config/js/condition/time.tsx deleted file mode 100644 index f9891bc025..0000000000 --- a/src/panels/config/js/condition/time.tsx +++ /dev/null @@ -1,41 +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 TimeCondition extends AutomationComponent { - private onChange: (obj: any) => void; - constructor() { - super(); - - this.onChange = onChangeEvent.bind(this, "condition"); - } - - /* eslint-disable camelcase */ - public render({ condition, localize }) { - const { after, before } = condition; - return ( -
- - -
- ); - } -} - -(TimeCondition as any).defaultConfig = {}; diff --git a/src/panels/config/js/condition/zone.tsx b/src/panels/config/js/condition/zone.tsx deleted file mode 100644 index 881482f3cb..0000000000 --- a/src/panels/config/js/condition/zone.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { h } from "preact"; -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 ZoneCondition extends AutomationComponent { - constructor() { - super(); - - this.entityPicked = this.entityPicked.bind(this); - this.zonePicked = this.zonePicked.bind(this); - } - - public entityPicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.condition, - entity_id: ev.target.value, - }); - } - - public zonePicked(ev) { - if (!this.initialized) { - return; - } - this.props.onChange(this.props.index, { - ...this.props.condition, - zone: ev.target.value, - }); - } - - /* eslint-disable camelcase */ - public render({ condition, hass, localize }) { - const { entity_id, zone } = condition; - return ( -
- - -
- ); - } -} - -(ZoneCondition as any).defaultConfig = { - entity_id: "", - zone: "", -}; diff --git a/src/panels/config/js/preact-types.ts b/src/panels/config/js/preact-types.ts index 25f7ba08eb..09aaf79064 100644 --- a/src/panels/config/js/preact-types.ts +++ b/src/panels/config/js/preact-types.ts @@ -24,6 +24,8 @@ declare global { "ha-service-picker": any; "mwc-button": any; "ha-automation-trigger": any; + "ha-automation-condition": any; + "ha-automation-condition-editor": any; "ha-device-trigger-picker": any; "ha-device-action-picker": any; "ha-form": any; diff --git a/src/panels/config/js/script/condition.tsx b/src/panels/config/js/script/condition.tsx index f930256f08..7a1568fe6d 100644 --- a/src/panels/config/js/script/condition.tsx +++ b/src/panels/config/js/script/condition.tsx @@ -1,24 +1,32 @@ import { h, Component } from "preact"; -import StateCondition from "../condition/state"; -import ConditionEdit from "../condition/condition_edit"; +import "../../automation/condition/ha-automation-condition-editor"; export default class ConditionAction extends Component { + constructor() { + super(); + + this.conditionChanged = this.conditionChanged.bind(this); + } + + public conditionChanged(ev) { + this.props.onChange(this.props.index, ev.detail.value); + } + // eslint-disable-next-line - public render({ action, index, onChange, hass, localize }) { + public render({ action, hass }) { return ( - +
+ +
); } } (ConditionAction as any).defaultConfig = { condition: "state", - ...(StateCondition as any).defaultConfig, };