From 87aad75cc78ad742f747eac705e008f47c55cc55 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 27 Jun 2023 10:58:27 +0200 Subject: [PATCH] Add UI for conversation trigger (#17037) --- .../src/pages/automation/describe-trigger.ts | 5 + .../src/pages/automation/editor-trigger.ts | 11 ++ src/data/automation.ts | 6 + src/data/automation_i18n.ts | 18 ++ src/data/trigger.ts | 2 + .../trigger/ha-automation-trigger-row.ts | 1 + .../trigger/ha-automation-trigger.ts | 1 + .../ha-automation-trigger-conversation.ts | 174 ++++++++++++++++++ src/translations/en.json | 10 + 9 files changed, 228 insertions(+) create mode 100644 src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts diff --git a/gallery/src/pages/automation/describe-trigger.ts b/gallery/src/pages/automation/describe-trigger.ts index 3ebb26f017..f86c452340 100644 --- a/gallery/src/pages/automation/describe-trigger.ts +++ b/gallery/src/pages/automation/describe-trigger.ts @@ -51,6 +51,11 @@ const triggers = [ { platform: "tag" }, { platform: "time", at: "15:32" }, { platform: "template" }, + { platform: "conversation", command: "Turn on the lights" }, + { + platform: "conversation", + command: ["Turn on the lights", "Turn the lights on"], + }, { platform: "event", event_type: "homeassistant_started" }, ]; diff --git a/gallery/src/pages/automation/editor-trigger.ts b/gallery/src/pages/automation/editor-trigger.ts index 5dccee4fa0..cdc9ed2c83 100644 --- a/gallery/src/pages/automation/editor-trigger.ts +++ b/gallery/src/pages/automation/editor-trigger.ts @@ -25,6 +25,7 @@ import { HaDeviceTrigger } from "../../../../src/panels/config/automation/trigge import { HaStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state"; import { HaMQTTTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt"; import "../../../../src/panels/config/automation/trigger/ha-automation-trigger"; +import { HaConversationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-conversation"; const SCHEMAS: { name: string; triggers: Trigger[] }[] = [ { @@ -112,6 +113,16 @@ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [ name: "Device Trigger", triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }], }, + { + name: "Sentence", + triggers: [ + { platform: "conversation", ...HaConversationTrigger.defaultConfig }, + { + platform: "conversation", + command: ["Turn on the lights", "Turn the lights on"], + }, + ], + }, ]; @customElement("demo-automation-editor-trigger") diff --git a/src/data/automation.ts b/src/data/automation.ts index 3bb0dfff66..eff766e10d 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -107,6 +107,11 @@ export interface NumericStateTrigger extends BaseTrigger { for?: string | number | ForDict; } +export interface ConversationTrigger extends BaseTrigger { + platform: "conversation"; + command: string | string[]; +} + export interface SunTrigger extends BaseTrigger { platform: "sun"; offset: number; @@ -178,6 +183,7 @@ export type Trigger = | HassTrigger | NumericStateTrigger | SunTrigger + | ConversationTrigger | TimePatternTrigger | WebhookTrigger | PersistentNotificationTrigger diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index f2ee1e0f17..7b4e249386 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -610,6 +610,24 @@ const tryDescribeTrigger = ( ); } + // Conversation Trigger + if (trigger.platform === "conversation") { + if (!trigger.command) { + return hass.localize( + `${triggerTranslationBaseKey}.conversation.description.empty` + ); + } + + return hass.localize( + `${triggerTranslationBaseKey}.conversation.description.full`, + { + sentence: disjunctionFormatter.format( + ensureArray(trigger.command).map((cmd) => `'${cmd}'`) + ), + } + ); + } + // Persistent Notification Trigger if (trigger.platform === "persistent_notification") { return "When a persistent notification is updated"; diff --git a/src/data/trigger.ts b/src/data/trigger.ts index 3d6cf2558b..65ce25d726 100644 --- a/src/data/trigger.ts +++ b/src/data/trigger.ts @@ -9,6 +9,7 @@ import { mdiMapMarker, mdiMapMarkerRadius, mdiMessageAlert, + mdiMicrophoneMessage, mdiNfcVariant, mdiNumeric, mdiStateMachine, @@ -27,6 +28,7 @@ export const TRIGGER_TYPES = { mqtt: mdiSwapHorizontal, numeric_state: mdiNumeric, sun: mdiWeatherSunny, + conversation: mdiMicrophoneMessage, tag: mdiNfcVariant, template: mdiCodeBraces, time: mdiClockOutline, 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 4719bc4074..cbc1e7bff3 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -53,6 +53,7 @@ import "./types/ha-automation-trigger-homeassistant"; import "./types/ha-automation-trigger-mqtt"; import "./types/ha-automation-trigger-numeric_state"; import "./types/ha-automation-trigger-persistent_notification"; +import "./types/ha-automation-trigger-conversation"; import "./types/ha-automation-trigger-state"; import "./types/ha-automation-trigger-sun"; import "./types/ha-automation-trigger-tag"; diff --git a/src/panels/config/automation/trigger/ha-automation-trigger.ts b/src/panels/config/automation/trigger/ha-automation-trigger.ts index fbe0539f8d..31acd3b0c4 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger.ts @@ -43,6 +43,7 @@ import "./types/ha-automation-trigger-homeassistant"; import "./types/ha-automation-trigger-mqtt"; import "./types/ha-automation-trigger-numeric_state"; import "./types/ha-automation-trigger-persistent_notification"; +import "./types/ha-automation-trigger-conversation"; import "./types/ha-automation-trigger-state"; import "./types/ha-automation-trigger-sun"; import "./types/ha-automation-trigger-tag"; diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts new file mode 100644 index 0000000000..f5cc84d862 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts @@ -0,0 +1,174 @@ +import { mdiClose } from "@mdi/js"; +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import { ensureArray } from "../../../../../common/array/ensure-array"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import "../../../../../components/ha-textfield"; +import type { HaTextField } from "../../../../../components/ha-textfield"; +import { ConversationTrigger } from "../../../../../data/automation"; +import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box"; +import { HomeAssistant } from "../../../../../types"; +import { TriggerElement } from "../ha-automation-trigger-row"; + +@customElement("ha-automation-trigger-conversation") +export class HaConversationTrigger + extends LitElement + implements TriggerElement +{ + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public trigger!: ConversationTrigger; + + @property({ type: Boolean }) public disabled = false; + + @query("#option_input", true) private _optionInput?: HaTextField; + + public static get defaultConfig(): Omit { + return { command: "" }; + } + + protected render() { + const { command } = this.trigger; + const commands = command ? ensureArray(command) : []; + + return html`${commands.length + ? commands.map( + (option, index) => html` + + + + ` + ) + : nothing} + `; + } + + private _handleKeyAdd(ev: KeyboardEvent) { + ev.stopPropagation(); + if (ev.key !== "Enter") { + return; + } + this._addOption(); + } + + private _addOption() { + const input = this._optionInput; + if (!input?.value) { + return; + } + fireEvent(this, "value-changed", { + value: { + ...this.trigger, + command: this.trigger.command.length + ? [ + ...(Array.isArray(this.trigger.command) + ? this.trigger.command + : [this.trigger.command]), + input.value, + ] + : input.value, + }, + }); + input.value = ""; + } + + private async _updateOption(ev: Event) { + const index = (ev.target as any).index; + const command = [...this.trigger.command]; + command.splice(index, 1, (ev.target as HaTextField).value); + fireEvent(this, "value-changed", { + value: { ...this.trigger, command }, + }); + } + + private async _removeOption(ev: Event) { + const index = (ev.target as any).parentElement.index; + if ( + !(await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.conversation.delete" + ), + text: this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.conversation.confirm_delete" + ), + destructive: true, + })) + ) { + return; + } + let command: string[] | string; + if (!Array.isArray(this.trigger.command)) { + command = ""; + } else { + command = [...this.trigger.command]; + command.splice(index, 1); + } + fireEvent(this, "value-changed", { + value: { ...this.trigger, command }, + }); + } + + static get styles(): CSSResultGroup { + return css` + .layout { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + justify-content: flex-start; + } + .option { + margin-top: 4px; + } + mwc-button { + margin-left: 8px; + } + ha-textfield { + display: block; + margin-bottom: 8px; + --textfield-icon-trailing-padding: 0; + } + ha-textfield > ha-icon-button { + position: relative; + right: -8px; + --mdc-icon-button-size: 36px; + --mdc-icon-size: 20px; + color: var(--secondary-text-color); + inset-inline-start: initial; + inset-inline-end: -8px; + direction: var(--direction); + } + #option_input { + margin-top: 8px; + } + .header { + margin-top: 8px; + margin-bottom: 8px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-conversation": HaConversationTrigger; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index 4479abbf2d..14c9a0123b 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2378,6 +2378,16 @@ "rises": "When the sun rises{hasDuration, select, \n true { offset by {duration}} \n other {}\n }" } }, + "conversation": { + "label": "Sentence", + "add_sentence": "Add sentence", + "delete": "Delete sentence", + "confirm_delete": "Are you sure you want to delete this sentence?", + "description": { + "empty": "When a sentence is said", + "full": "When the sentence {sentence} is said" + } + }, "tag": { "label": "Tag", "description": {