Add wake word to assist pipeline settings (#18019)

* Add wake word to assist pipeline settings

* Update assist-pipeline-detail-wakeword.ts

* Add icon for wake word domain

* format state

* implement `wake_word/info` command
This commit is contained in:
Bram Kragten 2023-09-26 18:17:45 +02:00 committed by GitHub
parent 2a8d98307e
commit c567a61dd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 173 additions and 1 deletions

View File

@ -15,6 +15,7 @@ import {
mdiCalendarClock,
mdiCarCoolantLevel,
mdiCash,
mdiChatSleep,
mdiClock,
mdiCloudUpload,
mdiCog,
@ -124,6 +125,7 @@ export const FIXED_DOMAIN_ICONS = {
tts: mdiSpeakerMessage,
updater: mdiCloudUpload,
vacuum: mdiRobotVacuum,
wake_word: mdiChatSleep,
zone: mdiMapMarkerRadius,
};

View File

@ -193,6 +193,7 @@ export const computeStateDisplayFromEntityAttributes = (
"scene",
"stt",
"tts",
"wake_word",
].includes(domain) ||
(domain === "sensor" && attributes.device_class === "timestamp")
) {

View File

@ -14,6 +14,8 @@ export interface AssistPipeline {
tts_engine: string | null;
tts_language: string | null;
tts_voice: string | null;
wake_word_entity: string | null;
wake_word_id: string | null;
}
export interface AssistPipelineMutableParams {
@ -26,6 +28,8 @@ export interface AssistPipelineMutableParams {
tts_engine: string | null;
tts_language: string | null;
tts_voice: string | null;
wake_word_entity: string | null;
wake_word_id: string | null;
}
export interface assistRunListing {

12
src/data/wake_word.ts Normal file
View File

@ -0,0 +1,12 @@
import type { HomeAssistant } from "../types";
export interface WakeWord {
id: string;
name: string;
}
export const fetchWakeWordInfo = (hass: HomeAssistant, entity_id: string) =>
hass.callWS<{ wake_words: WakeWord[] }>({
type: "wake_word/info",
entity_id,
});

View File

@ -0,0 +1,138 @@
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { LocalizeKeys } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import { AssistPipeline } from "../../../../data/assist_pipeline";
import { HomeAssistant } from "../../../../types";
import { fetchWakeWordInfo, WakeWord } from "../../../../data/wake_word";
@customElement("assist-pipeline-detail-wakeword")
export class AssistPipelineDetailWakeWord extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public data?: Partial<AssistPipeline>;
@state() private _wakeWords?: WakeWord[];
private _schema = memoizeOne(
(wakeWords?: WakeWord[]) =>
[
{
name: "",
type: "grid",
schema: [
{
name: "wake_word_entity",
selector: {
entity: {
domain: "wake_word",
},
},
},
wakeWords?.length
? {
name: "wake_word_id",
required: true,
selector: {
select: {
options: wakeWords.map((ww) => ({
value: ww.id,
label: ww.name,
})),
},
},
}
: { name: "", type: "constant" },
] as const,
},
] as const
);
private _computeLabel = (schema): string =>
schema.name
? this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.form.${schema.name}` as LocalizeKeys
)
: "";
protected willUpdate(changedProps: PropertyValues) {
if (
changedProps.has("data") &&
changedProps.get("data")?.wake_word_entity !== this.data?.wake_word_entity
) {
this._fetchWakeWords();
}
}
protected render() {
return html`
<div class="section">
<div class="content">
<div class="intro">
<h3>
${this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.title`
)}
</h3>
<p>
${this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.description`
)}
</p>
</div>
<ha-form
.schema=${this._schema(this._wakeWords)}
.data=${this.data}
.hass=${this.hass}
.computeLabel=${this._computeLabel}
></ha-form>
</div>
</div>
`;
}
private async _fetchWakeWords() {
if (!this.data?.wake_word_entity) {
this._wakeWords = undefined;
return;
}
this._wakeWords = (
await fetchWakeWordInfo(this.hass, this.data.wake_word_entity)
).wake_words;
}
static get styles(): CSSResultGroup {
return css`
.section {
border: 1px solid var(--divider-color);
border-radius: 8px;
}
.content {
padding: 16px;
}
.intro {
margin-bottom: 16px;
}
h3 {
font-weight: normal;
font-size: 22px;
line-height: 28px;
margin-top: 0;
margin-bottom: 4px;
}
p {
color: var(--secondary-text-color);
font-size: var(--mdc-typography-body2-font-size, 0.875rem);
margin-top: 0;
margin-bottom: 0;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"assist-pipeline-detail-wakeword": AssistPipelineDetailWakeWord;
}
}

View File

@ -25,6 +25,7 @@ import "./assist-pipeline-detail/assist-pipeline-detail-config";
import "./assist-pipeline-detail/assist-pipeline-detail-conversation";
import "./assist-pipeline-detail/assist-pipeline-detail-stt";
import "./assist-pipeline-detail/assist-pipeline-detail-tts";
import "./assist-pipeline-detail/assist-pipeline-detail-wakeword";
import "./debug/assist-render-pipeline-events";
import { VoiceAssistantPipelineDetailsDialogParams } from "./show-dialog-voice-assistant-pipeline-detail";
@ -192,6 +193,12 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
keys="tts_engine,tts_language,tts_voice"
@value-changed=${this._valueChanged}
></assist-pipeline-detail-tts>
<assist-pipeline-detail-wakeword
.hass=${this.hass}
.data=${this._data}
keys="wake_word_entity,wake_word_id"
@value-changed=${this._valueChanged}
></assist-pipeline-detail-wakeword>
</div>
${this._params.pipeline?.id
? html`
@ -249,6 +256,8 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
tts_engine: data.tts_engine ?? null,
tts_language: data.tts_language ?? null,
tts_voice: data.tts_voice ?? null,
wake_word_entity: data.wake_word_entity ?? null,
wake_word_id: data.wake_word_id ?? null,
};
if (this._params!.pipeline?.id) {
await this._params!.updatePipeline(values);

View File

@ -2173,7 +2173,9 @@
"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"
"tts_voice": "Voice",
"wake_word_entity": "Wake word engine",
"wake_word_id": "Wake word"
},
"steps": {
"config": {
@ -2191,6 +2193,10 @@
"tts": {
"title": "Text-to-speech",
"description": "When you are controlling your assistant with voice, the text-to-speech engine turns the conversation text responses into audio."
},
"wakeword": {
"title": "Wake word",
"description": "If a device supports wake words, you can activate Assist by saying this word."
}
},
"no_cloud_message": "You should have an active cloud subscription to use cloud speech services.",