Add UI for conversation trigger (#17037)

This commit is contained in:
Bram Kragten 2023-06-27 10:58:27 +02:00 committed by GitHub
parent d656269d75
commit 87aad75cc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 228 additions and 0 deletions

View File

@ -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" },
];

View File

@ -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")

View File

@ -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

View File

@ -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";

View File

@ -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,

View File

@ -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";

View File

@ -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";

View File

@ -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<ConversationTrigger, "platform"> {
return { command: "" };
}
protected render() {
const { command } = this.trigger;
const commands = command ? ensureArray(command) : [];
return html`${commands.length
? commands.map(
(option, index) => html`
<ha-textfield
class="option"
iconTrailing
.index=${index}
.value=${option}
@change=${this._updateOption}
>
<ha-icon-button
@click=${this._removeOption}
slot="trailingIcon"
.path=${mdiClose}
></ha-icon-button>
</ha-textfield>
`
)
: nothing}
<ha-textfield
class="flex-auto"
id="option_input"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.conversation.add_sentence"
)}
@keydown=${this._handleKeyAdd}
@change=${this._addOption}
></ha-textfield>`;
}
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;
}
}

View File

@ -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": {