From ea0f29782d374de7fabf8e08227e05cc69079b94 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 20 Apr 2023 17:02:48 +0200 Subject: [PATCH] Assist pipeline language voice (#16255) * Update types * Split form into multiple components * Improve design * Send all data * Update wording --- src/data/assist_pipeline.ts | 18 ++- .../assist-pipeline-detail-config.ts | 106 ++++++++++++++++ .../assist-pipeline-detail-conversation.ts | 106 ++++++++++++++++ .../assist-pipeline-detail-stt.ts | 105 ++++++++++++++++ .../assist-pipeline-detail-tts.ts | 113 +++++++++++++++++ .../dialog-voice-assistant-pipeline-detail.ts | 114 ++++++++---------- ...-dialog-voice-assistant-pipeline-detail.ts | 4 +- src/translations/en.json | 6 +- 8 files changed, 498 insertions(+), 74 deletions(-) create mode 100644 src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-config.ts create mode 100644 src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-conversation.ts create mode 100644 src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-stt.ts create mode 100644 src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-tts.ts diff --git a/src/data/assist_pipeline.ts b/src/data/assist_pipeline.ts index abf370681f..517990f8ba 100644 --- a/src/data/assist_pipeline.ts +++ b/src/data/assist_pipeline.ts @@ -6,18 +6,26 @@ import type { SpeechMetadata } from "./stt"; export interface AssistPipeline { id: string; conversation_engine: string; + conversation_language: string | null; language: string; name: string; - stt_engine: string; - tts_engine: string; + stt_engine: string | null; + stt_language: string | null; + tts_engine: string | null; + tts_language: string | null; + tts_voice: string | null; } export interface AssistPipelineMutableParams { conversation_engine: string; + conversation_language?: string | null; language: string; name: string; - stt_engine: string; - tts_engine: string; + stt_engine?: string | null; + stt_language?: string | null; + tts_engine?: string | null; + tts_language?: string | null; + tts_voice?: string | null; } export interface assistRunListing { @@ -274,7 +282,7 @@ export const createAssistPipeline = ( export const updateAssistPipeline = ( hass: HomeAssistant, pipeline_id: string, - pipeline: Partial + pipeline: AssistPipelineMutableParams ) => hass.callWS({ type: "assist_pipeline/pipeline/update", diff --git a/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-config.ts b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-config.ts new file mode 100644 index 0000000000..2779bc37e4 --- /dev/null +++ b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-config.ts @@ -0,0 +1,106 @@ +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { SchemaUnion } from "../../../../components/ha-form/types"; +import { AssistPipeline } from "../../../../data/assist_pipeline"; +import { HomeAssistant } from "../../../../types"; + +@customElement("assist-pipeline-detail-config") +export class AssistPipelineDetailConfig extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public data?: Partial; + + @property() public error?: Record; + + @property() public supportedLanguages?: string[]; + + private _schema = memoizeOne( + (supportedLanguages?: string[]) => + [ + { + name: "", + type: "grid", + schema: [ + { + name: "name", + required: true, + selector: { + text: {}, + }, + }, + { + name: "language", + required: true, + selector: { + language: { + languages: supportedLanguages ?? [], + }, + }, + }, + ] as const, + }, + ] as const + ); + + private _computeLabel = ( + schema: SchemaUnion> + ): string => + this.hass.localize( + `ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}` + ); + + protected render() { + return html` +
+
+

Configuration

+

Main configuration of your assistant

+
+ +
+ `; + } + + static get styles(): CSSResultGroup { + return css` + .section { + border: 1px solid var(--divider-color); + border-radius: 8px; + box-sizing: border-box; + padding: 16px; + } + .intro { + margin-bottom: 16px; + } + h3 { + font-weight: normal; + font-size: 22px; + line-height: 28px; + margin-top: 0; + margin-bottom: 4px; + } + p { + font-weight: normal; + color: var(--secondary-text-color); + font-size: 16px; + line-height: 24px; + letter-spacing: 0.5px; + margin-top: 0; + margin-bottom: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "assist-pipeline-detail-config": AssistPipelineDetailConfig; + } +} diff --git a/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-conversation.ts b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-conversation.ts new file mode 100644 index 0000000000..6923ce4c47 --- /dev/null +++ b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-conversation.ts @@ -0,0 +1,106 @@ +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { SchemaUnion } from "../../../../components/ha-form/types"; +import { AssistPipeline } from "../../../../data/assist_pipeline"; +import { HomeAssistant } from "../../../../types"; + +@customElement("assist-pipeline-detail-conversation") +export class AssistPipelineDetailConversation extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public data?: Partial; + + @property() public error?: Record; + + private _schema = memoizeOne( + (language?: string) => + [ + { + name: "", + type: "grid", + schema: [ + { + name: "conversation_engine", + required: true, + selector: { + conversation_agent: { + language, + }, + }, + }, + { + name: "conversation_language", + selector: { + text: {}, + }, + }, + ] as const, + }, + ] as const + ); + + private _computeLabel = ( + schema: SchemaUnion> + ): string => + this.hass.localize( + `ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}` + ); + + protected render() { + return html` +
+
+

Conversation agent

+

+ The conversation agent is the brains of your voice assistant and + will process the incoming commands. +

+
+ +
+ `; + } + + static get styles(): CSSResultGroup { + return css` + .section { + border: 1px solid var(--divider-color); + border-radius: 8px; + box-sizing: border-box; + padding: 16px; + } + .intro { + margin-bottom: 16px; + } + h3 { + font-weight: normal; + font-size: 22px; + line-height: 28px; + margin-top: 0; + margin-bottom: 4px; + } + p { + font-weight: normal; + color: var(--secondary-text-color); + font-size: 16px; + line-height: 24px; + letter-spacing: 0.5px; + margin-top: 0; + margin-bottom: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "assist-pipeline-detail-conversation": AssistPipelineDetailConversation; + } +} diff --git a/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-stt.ts b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-stt.ts new file mode 100644 index 0000000000..dc8a019a63 --- /dev/null +++ b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-stt.ts @@ -0,0 +1,105 @@ +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { SchemaUnion } from "../../../../components/ha-form/types"; +import { AssistPipeline } from "../../../../data/assist_pipeline"; +import { HomeAssistant } from "../../../../types"; + +@customElement("assist-pipeline-detail-stt") +export class AssistPipelineDetailSTT extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public data?: Partial; + + @property() public error?: Record; + + private _schema = memoizeOne( + (language?: string) => + [ + { + name: "", + type: "grid", + schema: [ + { + name: "stt_engine", + selector: { + stt: { + language, + }, + }, + }, + { + name: "stt_language", + selector: { + text: {}, + }, + }, + ] as const, + }, + ] as const + ); + + private _computeLabel = ( + schema: SchemaUnion> + ): string => + this.hass.localize( + `ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}` + ); + + protected render() { + return html` +
+
+

Speech-to-text

+

+ When you are using the pipeline as a voice assistant, the + speech-to-text engine turns your voice command into text. +

+
+ +
+ `; + } + + static get styles(): CSSResultGroup { + return css` + .section { + border: 1px solid var(--divider-color); + border-radius: 8px; + box-sizing: border-box; + padding: 16px; + } + .intro { + margin-bottom: 16px; + } + h3 { + font-weight: normal; + font-size: 22px; + line-height: 28px; + margin-top: 0; + margin-bottom: 4px; + } + p { + font-weight: normal; + color: var(--secondary-text-color); + font-size: 16px; + line-height: 24px; + letter-spacing: 0.5px; + margin-top: 0; + margin-bottom: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "assist-pipeline-detail-stt": AssistPipelineDetailSTT; + } +} diff --git a/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-tts.ts b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-tts.ts new file mode 100644 index 0000000000..c8eb59b601 --- /dev/null +++ b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-tts.ts @@ -0,0 +1,113 @@ +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { SchemaUnion } from "../../../../components/ha-form/types"; +import { AssistPipeline } from "../../../../data/assist_pipeline"; +import { HomeAssistant } from "../../../../types"; + +@customElement("assist-pipeline-detail-tts") +export class AssistPipelineDetailTTS extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public data?: Partial; + + @property() public error?: Record; + + private _schema = memoizeOne( + (language?: string) => + [ + { + name: "", + type: "grid", + schema: [ + { + name: "tts_engine", + selector: { + tts: { + language, + }, + }, + }, + + { + name: "tts_language", + selector: { + text: {}, + }, + }, + { + name: "tts_voice", + selector: { + text: {}, + }, + }, + ] as const, + }, + ] as const + ); + + private _computeLabel = ( + schema: SchemaUnion> + ): string => + this.hass.localize( + `ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}` + ); + + protected render() { + return html` +
+
+

Text-to-speech

+

+ When you are using the pipeline as a voice assistant, the + text-to-speech engine turns the conversation text responses into + audio. +

+
+ +
+ `; + } + + static get styles(): CSSResultGroup { + return css` + .section { + border: 1px solid var(--divider-color); + border-radius: 8px; + box-sizing: border-box; + padding: 16px; + } + .intro { + margin-bottom: 16px; + } + h3 { + font-weight: normal; + font-size: 22px; + line-height: 28px; + margin-top: 0; + margin-bottom: 4px; + } + p { + font-weight: normal; + color: var(--secondary-text-color); + font-size: 16px; + line-height: 24px; + letter-spacing: 0.5px; + margin-top: 0; + margin-bottom: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "assist-pipeline-detail-tts": AssistPipelineDetailTTS; + } +} diff --git a/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts b/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts index b0fe424abc..b1948be042 100644 --- a/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts +++ b/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts @@ -1,11 +1,9 @@ import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-button"; import { createCloseHeading } from "../../../components/ha-dialog"; import "../../../components/ha-form/ha-form"; -import { SchemaUnion } from "../../../components/ha-form/types"; import { AssistPipeline, AssistPipelineMutableParams, @@ -13,6 +11,10 @@ import { } from "../../../data/assist_pipeline"; import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; +import "./assist-pipeline-detail/assist-pipeline-detail-conversation"; +import "./assist-pipeline-detail/assist-pipeline-detail-config"; +import "./assist-pipeline-detail/assist-pipeline-detail-stt"; +import "./assist-pipeline-detail/assist-pipeline-detail-tts"; import "./debug/assist-render-pipeline-events"; import { VoiceAssistantPipelineDetailsDialogParams } from "./show-dialog-voice-assistant-pipeline-detail"; @@ -79,14 +81,31 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement { )} >
- + > + + +
${this._params.pipeline?.id ? html` @@ -131,57 +150,6 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement { `; } - private _schema = memoizeOne( - (supportedLanguages: string[]) => - [ - { - name: "name", - required: true, - selector: { - text: {}, - }, - }, - { - name: "language", - required: true, - selector: { - language: { - languages: supportedLanguages, - }, - }, - }, - { - name: "conversation_engine", - required: true, - selector: { - conversation_agent: {}, - }, - context: { language: "language" }, - }, - { - name: "stt_engine", - selector: { - stt: {}, - }, - context: { language: "language" }, - }, - { - name: "tts_engine", - selector: { - tts: {}, - }, - context: { language: "language" }, - }, - ] as const - ); - - private _computeLabel = ( - schema: SchemaUnion> - ): string => - this.hass.localize( - `ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}` - ); - private _valueChanged(ev: CustomEvent) { this._error = undefined; const value = ev.detail.value; @@ -192,12 +160,17 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement { this._submitting = true; try { if (this._params!.pipeline?.id) { - const values: Partial = { - name: this._data!.name, - conversation_engine: this._data!.conversation_engine, - language: this._data!.language, - stt_engine: this._data!.stt_engine, - tts_engine: this._data!.tts_engine, + const data = this._data!; + const values: AssistPipelineMutableParams = { + name: data.name!, + language: data.language!, + conversation_engine: data.conversation_engine!, + conversation_language: data.conversation_language, + stt_engine: data.stt_engine, + stt_language: data.stt_language, + tts_engine: data.tts_engine, + tts_language: data.tts_language, + tts_voice: data.tts_voice, }; await this._params!.updatePipeline(values); } else { @@ -239,7 +212,18 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement { } static get styles(): CSSResultGroup { - return [haStyleDialog, css``]; + return [ + haStyleDialog, + css` + assist-pipeline-detail-config, + assist-pipeline-detail-conversation, + assist-pipeline-detail-stt, + assist-pipeline-detail-tts { + margin-bottom: 16px; + display: block; + } + `, + ]; } } diff --git a/src/panels/config/voice-assistants/show-dialog-voice-assistant-pipeline-detail.ts b/src/panels/config/voice-assistants/show-dialog-voice-assistant-pipeline-detail.ts index 3340dce424..f0e6b7d5c6 100644 --- a/src/panels/config/voice-assistants/show-dialog-voice-assistant-pipeline-detail.ts +++ b/src/panels/config/voice-assistants/show-dialog-voice-assistant-pipeline-detail.ts @@ -8,9 +8,7 @@ export interface VoiceAssistantPipelineDetailsDialogParams { pipeline?: AssistPipeline; preferred?: boolean; createPipeline: (values: AssistPipelineMutableParams) => Promise; - updatePipeline: ( - updates: Partial - ) => Promise; + updatePipeline: (updates: AssistPipelineMutableParams) => Promise; setPipelinePreferred: () => Promise; deletePipeline: () => Promise; } diff --git a/src/translations/en.json b/src/translations/en.json index bf651a926c..32c5eaa2f5 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2030,9 +2030,13 @@ "form": { "name": "Name", "conversation_engine": "Conversation agent", + "conversation_language": "[%key:ui::panel::config::voice_assistants::assistants::pipeline::detail::form::language%]", "language": "Language", "stt_engine": "Speech to text", - "tts_engine": "Text to speech" + "stt_language": "[%key:ui::panel::config::voice_assistants::assistants::pipeline::detail::form::language%]", + "tts_engine": "Text to speech", + "tts_language": "[%key:ui::panel::config::voice_assistants::assistants::pipeline::detail::form::language%]", + "tts_voice": "Voice" } } }