Assist pipeline language voice (#16255)

* Update types

* Split form into multiple components

* Improve design

* Send all data

* Update wording
This commit is contained in:
Paul Bottein 2023-04-20 17:02:48 +02:00 committed by GitHub
parent 65161ce581
commit ea0f29782d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 498 additions and 74 deletions

View File

@ -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<AssistPipelineMutableParams>
pipeline: AssistPipelineMutableParams
) =>
hass.callWS<AssistPipeline>({
type: "assist_pipeline/pipeline/update",

View File

@ -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<AssistPipeline>;
@property() public error?: Record<string, string>;
@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<ReturnType<typeof this._schema>>
): string =>
this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}`
);
protected render() {
return html`
<div class="section">
<div class="intro">
<h3>Configuration</h3>
<p>Main configuration of your assistant</p>
</div>
<ha-form
.schema=${this._schema(this.supportedLanguages)}
.data=${this.data}
.error=${this.error}
.hass=${this.hass}
.computeLabel=${this._computeLabel}
></ha-form>
</div>
`;
}
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;
}
}

View File

@ -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<AssistPipeline>;
@property() public error?: Record<string, string>;
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<ReturnType<typeof this._schema>>
): string =>
this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}`
);
protected render() {
return html`
<div class="section">
<div class="intro">
<h3>Conversation agent</h3>
<p>
The conversation agent is the brains of your voice assistant and
will process the incoming commands.
</p>
</div>
<ha-form
.schema=${this._schema(this.data?.language)}
.data=${this.data}
.error=${this.error}
.hass=${this.hass}
.computeLabel=${this._computeLabel}
></ha-form>
</div>
`;
}
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;
}
}

View File

@ -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<AssistPipeline>;
@property() public error?: Record<string, string>;
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<ReturnType<typeof this._schema>>
): string =>
this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}`
);
protected render() {
return html`
<div class="section">
<div class="intro">
<h3>Speech-to-text</h3>
<p>
When you are using the pipeline as a voice assistant, the
speech-to-text engine turns your voice command into text.
</p>
</div>
<ha-form
.schema=${this._schema(this.data?.language)}
.data=${this.data}
.error=${this.error}
.hass=${this.hass}
.computeLabel=${this._computeLabel}
></ha-form>
</div>
`;
}
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;
}
}

View File

@ -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<AssistPipeline>;
@property() public error?: Record<string, string>;
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<ReturnType<typeof this._schema>>
): string =>
this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}`
);
protected render() {
return html`
<div class="section">
<div class="intro">
<h3>Text-to-speech</h3>
<p>
When you are using the pipeline as a voice assistant, the
text-to-speech engine turns the conversation text responses into
audio.
</p>
</div>
<ha-form
.schema=${this._schema(this.data?.language)}
.data=${this.data}
.error=${this.error}
.hass=${this.hass}
.computeLabel=${this._computeLabel}
></ha-form>
</div>
`;
}
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;
}
}

View File

@ -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 {
)}
>
<div>
<ha-form
.schema=${this._schema(this._supportedLanguages)}
.data=${this._data}
<assist-pipeline-detail-config
.hass=${this.hass}
.data=${this._data}
.error=${this._error}
.computeLabel=${this._computeLabel}
.supportedLanguages=${this._supportedLanguages}
@value-changed=${this._valueChanged}
></ha-form>
></assist-pipeline-detail-config>
<assist-pipeline-detail-conversation
.hass=${this.hass}
.data=${this._data}
.error=${this._error}
@value-changed=${this._valueChanged}
></assist-pipeline-detail-conversation>
<assist-pipeline-detail-stt
.hass=${this.hass}
.data=${this._data}
.error=${this._error}
@value-changed=${this._valueChanged}
></assist-pipeline-detail-stt>
<assist-pipeline-detail-tts
.hass=${this.hass}
.data=${this._data}
.error=${this._error}
@value-changed=${this._valueChanged}
></assist-pipeline-detail-tts>
</div>
${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<ReturnType<typeof this._schema>>
): 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<AssistPipelineMutableParams> = {
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;
}
`,
];
}
}

View File

@ -8,9 +8,7 @@ export interface VoiceAssistantPipelineDetailsDialogParams {
pipeline?: AssistPipeline;
preferred?: boolean;
createPipeline: (values: AssistPipelineMutableParams) => Promise<unknown>;
updatePipeline: (
updates: Partial<AssistPipelineMutableParams>
) => Promise<unknown>;
updatePipeline: (updates: AssistPipelineMutableParams) => Promise<unknown>;
setPipelinePreferred: () => Promise<unknown>;
deletePipeline: () => Promise<boolean>;
}

View File

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