mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Add UI for conversation trigger (#17037)
This commit is contained in:
parent
d656269d75
commit
87aad75cc7
@ -51,6 +51,11 @@ const triggers = [
|
|||||||
{ platform: "tag" },
|
{ platform: "tag" },
|
||||||
{ platform: "time", at: "15:32" },
|
{ platform: "time", at: "15:32" },
|
||||||
{ platform: "template" },
|
{ 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" },
|
{ platform: "event", event_type: "homeassistant_started" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -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 { 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 { HaMQTTTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt";
|
||||||
import "../../../../src/panels/config/automation/trigger/ha-automation-trigger";
|
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[] }[] = [
|
const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
|
||||||
{
|
{
|
||||||
@ -112,6 +113,16 @@ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
|
|||||||
name: "Device Trigger",
|
name: "Device Trigger",
|
||||||
triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }],
|
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")
|
@customElement("demo-automation-editor-trigger")
|
||||||
|
@ -107,6 +107,11 @@ export interface NumericStateTrigger extends BaseTrigger {
|
|||||||
for?: string | number | ForDict;
|
for?: string | number | ForDict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConversationTrigger extends BaseTrigger {
|
||||||
|
platform: "conversation";
|
||||||
|
command: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface SunTrigger extends BaseTrigger {
|
export interface SunTrigger extends BaseTrigger {
|
||||||
platform: "sun";
|
platform: "sun";
|
||||||
offset: number;
|
offset: number;
|
||||||
@ -178,6 +183,7 @@ export type Trigger =
|
|||||||
| HassTrigger
|
| HassTrigger
|
||||||
| NumericStateTrigger
|
| NumericStateTrigger
|
||||||
| SunTrigger
|
| SunTrigger
|
||||||
|
| ConversationTrigger
|
||||||
| TimePatternTrigger
|
| TimePatternTrigger
|
||||||
| WebhookTrigger
|
| WebhookTrigger
|
||||||
| PersistentNotificationTrigger
|
| PersistentNotificationTrigger
|
||||||
|
@ -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
|
// Persistent Notification Trigger
|
||||||
if (trigger.platform === "persistent_notification") {
|
if (trigger.platform === "persistent_notification") {
|
||||||
return "When a persistent notification is updated";
|
return "When a persistent notification is updated";
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
mdiMapMarker,
|
mdiMapMarker,
|
||||||
mdiMapMarkerRadius,
|
mdiMapMarkerRadius,
|
||||||
mdiMessageAlert,
|
mdiMessageAlert,
|
||||||
|
mdiMicrophoneMessage,
|
||||||
mdiNfcVariant,
|
mdiNfcVariant,
|
||||||
mdiNumeric,
|
mdiNumeric,
|
||||||
mdiStateMachine,
|
mdiStateMachine,
|
||||||
@ -27,6 +28,7 @@ export const TRIGGER_TYPES = {
|
|||||||
mqtt: mdiSwapHorizontal,
|
mqtt: mdiSwapHorizontal,
|
||||||
numeric_state: mdiNumeric,
|
numeric_state: mdiNumeric,
|
||||||
sun: mdiWeatherSunny,
|
sun: mdiWeatherSunny,
|
||||||
|
conversation: mdiMicrophoneMessage,
|
||||||
tag: mdiNfcVariant,
|
tag: mdiNfcVariant,
|
||||||
template: mdiCodeBraces,
|
template: mdiCodeBraces,
|
||||||
time: mdiClockOutline,
|
time: mdiClockOutline,
|
||||||
|
@ -53,6 +53,7 @@ import "./types/ha-automation-trigger-homeassistant";
|
|||||||
import "./types/ha-automation-trigger-mqtt";
|
import "./types/ha-automation-trigger-mqtt";
|
||||||
import "./types/ha-automation-trigger-numeric_state";
|
import "./types/ha-automation-trigger-numeric_state";
|
||||||
import "./types/ha-automation-trigger-persistent_notification";
|
import "./types/ha-automation-trigger-persistent_notification";
|
||||||
|
import "./types/ha-automation-trigger-conversation";
|
||||||
import "./types/ha-automation-trigger-state";
|
import "./types/ha-automation-trigger-state";
|
||||||
import "./types/ha-automation-trigger-sun";
|
import "./types/ha-automation-trigger-sun";
|
||||||
import "./types/ha-automation-trigger-tag";
|
import "./types/ha-automation-trigger-tag";
|
||||||
|
@ -43,6 +43,7 @@ import "./types/ha-automation-trigger-homeassistant";
|
|||||||
import "./types/ha-automation-trigger-mqtt";
|
import "./types/ha-automation-trigger-mqtt";
|
||||||
import "./types/ha-automation-trigger-numeric_state";
|
import "./types/ha-automation-trigger-numeric_state";
|
||||||
import "./types/ha-automation-trigger-persistent_notification";
|
import "./types/ha-automation-trigger-persistent_notification";
|
||||||
|
import "./types/ha-automation-trigger-conversation";
|
||||||
import "./types/ha-automation-trigger-state";
|
import "./types/ha-automation-trigger-state";
|
||||||
import "./types/ha-automation-trigger-sun";
|
import "./types/ha-automation-trigger-sun";
|
||||||
import "./types/ha-automation-trigger-tag";
|
import "./types/ha-automation-trigger-tag";
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -2378,6 +2378,16 @@
|
|||||||
"rises": "When the sun rises{hasDuration, select, \n true { offset by {duration}} \n other {}\n }"
|
"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": {
|
"tag": {
|
||||||
"label": "Tag",
|
"label": "Tag",
|
||||||
"description": {
|
"description": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user