Skip to content

Commit

Permalink
Add wake word to assist pipeline settings (#18019)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bramkragten authored Sep 26, 2023
1 parent 2a8d983 commit c567a61
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/common/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
mdiCalendarClock,
mdiCarCoolantLevel,
mdiCash,
mdiChatSleep,
mdiClock,
mdiCloudUpload,
mdiCog,
Expand Down Expand Up @@ -124,6 +125,7 @@ export const FIXED_DOMAIN_ICONS = {
tts: mdiSpeakerMessage,
updater: mdiCloudUpload,
vacuum: mdiRobotVacuum,
wake_word: mdiChatSleep,
zone: mdiMapMarkerRadius,
};

Expand Down
1 change: 1 addition & 0 deletions src/common/entity/compute_state_display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export const computeStateDisplayFromEntityAttributes = (
"scene",
"stt",
"tts",
"wake_word",
].includes(domain) ||
(domain === "sensor" && attributes.device_class === "timestamp")
) {
Expand Down
4 changes: 4 additions & 0 deletions src/data/assist_pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions src/data/wake_word.ts
Original file line number Diff line number Diff line change
@@ -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,
});
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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.",
Expand Down

0 comments on commit c567a61

Please sign in to comment.