Update voice wizard (#22472)

This commit is contained in:
Bram Kragten 2024-10-23 15:14:11 +02:00
parent f4d9d55ecd
commit 58f210f45b
26 changed files with 305 additions and 311 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

View File

@ -39,6 +39,9 @@ export const AssistantSetupStyles = [
.footer.full-width ha-button { .footer.full-width ha-button {
width: 100%; width: 100%;
} }
.footer.centered {
justify-content: center;
}
.footer.side-by-side { .footer.side-by-side {
justify-content: space-between; justify-content: space-between;
} }

View File

@ -14,7 +14,6 @@ import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { haStyleDialog } from "../../resources/styles"; import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { VoiceAssistantSetupDialogParams } from "./show-voice-assistant-setup-dialog"; import { VoiceAssistantSetupDialogParams } from "./show-voice-assistant-setup-dialog";
import "./voice-assistant-setup-step-addons";
import "./voice-assistant-setup-step-area"; import "./voice-assistant-setup-step-area";
import "./voice-assistant-setup-step-change-wake-word"; import "./voice-assistant-setup-step-change-wake-word";
import "./voice-assistant-setup-step-check"; import "./voice-assistant-setup-step-check";
@ -34,7 +33,6 @@ export const enum STEP {
PIPELINE, PIPELINE,
SUCCESS, SUCCESS,
CLOUD, CLOUD,
ADDONS,
CHANGE_WAKEWORD, CHANGE_WAKEWORD,
} }
@ -210,22 +208,18 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
? html`<ha-voice-assistant-setup-step-cloud ? html`<ha-voice-assistant-setup-step-cloud
.hass=${this.hass} .hass=${this.hass}
></ha-voice-assistant-setup-step-cloud>` ></ha-voice-assistant-setup-step-cloud>`
: this._step === STEP.ADDONS : this._step === STEP.SUCCESS
? html`<ha-voice-assistant-setup-step-addons ? html`<ha-voice-assistant-setup-step-success
.hass=${this.hass} .hass=${this.hass}
></ha-voice-assistant-setup-step-addons>` .assistConfiguration=${this
: this._step === STEP.SUCCESS ._assistConfiguration}
? html`<ha-voice-assistant-setup-step-success .assistEntityId=${this._findDomainEntityId(
.hass=${this.hass} this._params.deviceId,
.assistConfiguration=${this this.hass.entities,
._assistConfiguration} "assist_satellite"
.assistEntityId=${this._findDomainEntityId( )}
this._params.deviceId, ></ha-voice-assistant-setup-step-success>`
this.hass.entities, : nothing}
"assist_satellite"
)}
></ha-voice-assistant-setup-step-success>`
: nothing}
</div> </div>
</ha-dialog> </ha-dialog>
`; `;

View File

@ -1,185 +0,0 @@
import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { HomeAssistant } from "../../types";
import { AssistantSetupStyles } from "./styles";
import { STEP } from "./voice-assistant-setup-dialog";
import { documentationUrl } from "../../util/documentation-url";
@customElement("ha-voice-assistant-setup-step-addons")
export class HaVoiceAssistantSetupStepAddons extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _showFirst = false;
@state() private _showSecond = false;
@state() private _showThird = false;
@state() private _showFourth = false;
protected override firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
setTimeout(() => {
this._showFirst = true;
}, 200);
setTimeout(() => {
this._showSecond = true;
}, 600);
setTimeout(() => {
this._showThird = true;
}, 3000);
setTimeout(() => {
this._showFourth = true;
}, 8000);
}
protected override render() {
return html`<div class="content">
<h1>Local</h1>
<p class="secondary">
Are you sure you want to use the local voice assistant? It requires a
powerful device to run. If you device is not powerful enough, Home
Assistant cloud might be a better option.
</p>
<h3>Raspberry Pi 4</h3>
<div class="messages-container rpi">
<div class="message user ${this._showThird ? "show" : ""}">
${!this._showThird ? "…" : "Turn on the lights in the bedroom"}
</div>
${this._showThird
? html`<div class="timing user">3 seconds</div>`
: nothing}
${this._showThird
? html`<div class="message hass ${this._showFourth ? "show" : ""}">
${!this._showFourth ? "…" : "Turned on the lights"}
</div>`
: nothing}
${this._showFourth
? html`<div class="timing hass">5 seconds</div>`
: nothing}
</div>
<h3>Home Assistant Cloud</h3>
<div class="messages-container cloud">
<div class="message user ${this._showFirst ? "show" : ""}">
${!this._showFirst ? "…" : "Turn on the lights in the bedroom"}
</div>
${this._showFirst
? html`<div class="timing user">0.2 seconds</div>`
: nothing}
${this._showFirst
? html` <div class="message hass ${this._showSecond ? "show" : ""}">
${!this._showSecond ? "…" : "Turned on the lights"}
</div>`
: nothing}
${this._showSecond
? html`<div class="timing hass">0.4 seconds</div>`
: nothing}
</div>
</div>
<div class="footer side-by-side">
<ha-button @click=${this._goToCloud}
>Try Home Assistant Cloud</ha-button
>
<a
href=${documentationUrl(
this.hass,
"/voice_control/voice_remote_local_assistant/"
)}
target="_blank"
rel="noreferrer noopenner"
>
<ha-button @click=${this._skip} unelevated>Learn more</ha-button>
</a>
</div>`;
}
private _goToCloud() {
fireEvent(this, "next-step", { step: STEP.CLOUD });
}
private _skip() {
fireEvent(this, "next-step", { step: STEP.SUCCESS });
}
static styles = [
AssistantSetupStyles,
css`
.messages-container {
padding: 24px;
box-sizing: border-box;
height: 195px;
background: var(--input-fill-color);
border-radius: 16px;
border: 1px solid var(--divider-color);
display: flex;
flex-direction: column;
}
.message {
white-space: nowrap;
font-size: 18px;
clear: both;
margin: 8px 0;
padding: 8px;
border-radius: 15px;
height: 36px;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
width: 30px;
}
.rpi .message {
transition: width 1s;
}
.cloud .message {
transition: width 0.5s;
}
.message.user {
margin-left: 24px;
margin-inline-start: 24px;
margin-inline-end: initial;
align-self: self-end;
text-align: right;
border-bottom-right-radius: 0px;
background-color: var(--primary-color);
color: var(--text-primary-color);
direction: var(--direction);
}
.timing.user {
align-self: self-end;
}
.message.user.show {
width: 295px;
}
.message.hass {
margin-right: 24px;
margin-inline-end: 24px;
margin-inline-start: initial;
align-self: self-start;
border-bottom-left-radius: 0px;
background-color: var(--secondary-background-color);
color: var(--primary-text-color);
direction: var(--direction);
}
.timing.hass {
align-self: self-start;
}
.message.hass.show {
width: 184px;
}
.footer {
margin-top: 24px;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-voice-assistant-setup-step-addons": HaVoiceAssistantSetupStepAddons;
}
}

View File

@ -16,7 +16,7 @@ export class HaVoiceAssistantSetupStepArea extends LitElement {
const device = this.hass.devices[this.deviceId]; const device = this.hass.devices[this.deviceId];
return html`<div class="content"> return html`<div class="content">
<img src="/static/icons/casita/loving.png" /> <img src="/static/images/voice-assistant/area.gif" />
<h1>Select area</h1> <h1>Select area</h1>
<p class="secondary"> <p class="secondary">
When you voice assistant knows where it is, it can better control the When you voice assistant knows where it is, it can better control the

View File

@ -10,6 +10,7 @@ import { STEP } from "./voice-assistant-setup-dialog";
import { AssistantSetupStyles } from "./styles"; import { AssistantSetupStyles } from "./styles";
import "../../components/ha-md-list"; import "../../components/ha-md-list";
import "../../components/ha-md-list-item"; import "../../components/ha-md-list-item";
import { formatLanguageCode } from "../../common/language/format_language";
@customElement("ha-voice-assistant-setup-step-change-wake-word") @customElement("ha-voice-assistant-setup-step-change-wake-word")
export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement { export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement {
@ -22,11 +23,12 @@ export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement {
protected override render() { protected override render() {
return html`<div class="padding content"> return html`<div class="padding content">
<img src="/static/icons/casita/smiling.png" /> <img src="/static/images/voice-assistant/change-wake-word.gif" />
<h1>Change wake word</h1> <h1>Change wake word</h1>
<p class="secondary"> <p class="secondary">
Some wake words are better for [your language] and voice than others. Some wake words are better for
Please try them out. ${formatLanguageCode(this.hass.locale.language, this.hass.locale)} and
voice than others. Please try them out.
</p> </p>
</div> </div>
<ha-md-list> <ha-md-list>

View File

@ -1,4 +1,4 @@
import { html, LitElement, PropertyValues } from "lit"; import { html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { testAssistSatelliteConnection } from "../../data/assist_satellite"; import { testAssistSatelliteConnection } from "../../data/assist_satellite";
@ -13,6 +13,8 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
@state() private _status?: "success" | "timeout"; @state() private _status?: "success" | "timeout";
@state() private _showLoader = false;
protected override willUpdate(changedProperties: PropertyValues): void { protected override willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties); super.willUpdate(changedProperties);
if (!this.hasUpdated) { if (!this.hasUpdated) {
@ -30,39 +32,48 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
protected override render() { protected override render() {
return html`<div class="content"> return html`<div class="content">
${this._status === "success" ${this._status === "timeout"
? html`<img src="/static/icons/casita/smiling.png" /> ? html`<img src="/static/images/voice-assistant/error.gif" />
<h1>The voice assistant is unable to connect to Home Assistant</h1>
<p class="secondary">
To play audio, the voice assistant device has to connect to Home
Assistant to fetch the files. Our test shows that the device is
unable to reach the Home Assistant server.
</p>
<div class="footer">
<a
href="https://www.home-assistant.io/docs/configuration/remote/#adding-a-remote-url-to-home-assistant"
><ha-button>Help me</ha-button></a
>
<ha-button @click=${this._testConnection}>Retry</ha-button>
</div>`
: html`<img src="/static/images/voice-assistant/hi.gif" />
<h1>Hi</h1> <h1>Hi</h1>
<p class="secondary"> <p class="secondary">
With a couple of steps we are going to setup your voice assistant. Over the next couple steps we're going to personalize your voice
</p>` assistant.
: this._status === "timeout" </p>
? html`<img src="/static/icons/casita/sad.png" />
<h1>Voice assistant can not connect to Home Assistant</h1> ${this._showLoader
<p class="secondary"> ? html`<ha-circular-progress
A good explanation what is happening and what action you should indeterminate
take. ></ha-circular-progress>`
</p> : nothing} `}
<div class="footer">
<a href="#"><ha-button>Help me</ha-button></a>
<ha-button @click=${this._testConnection}>Retry</ha-button>
</div>`
: html`<img src="/static/icons/casita/loading.png" />
<h1>Checking...</h1>
<p class="secondary">
We are checking if the device can reach your Home Assistant
instance.
</p>
<ha-circular-progress indeterminate></ha-circular-progress>`}
</div>`; </div>`;
} }
private async _testConnection() { private async _testConnection() {
this._status = undefined; this._status = undefined;
this._showLoader = false;
const timeout = setTimeout(() => {
this._showLoader = true;
}, 3000);
const result = await testAssistSatelliteConnection( const result = await testAssistSatelliteConnection(
this.hass, this.hass,
this.assistEntityId! this.assistEntityId!
); );
clearTimeout(timeout);
this._showLoader = false;
this._status = result.status; this._status = result.status;
} }

View File

@ -1,7 +1,9 @@
import { html, LitElement } from "lit"; import { mdiEarth, mdiMicrophoneMessage, mdiOpenInNew } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { brandsUrl } from "../../util/brands-url";
import { AssistantSetupStyles } from "./styles"; import { AssistantSetupStyles } from "./styles";
@customElement("ha-voice-assistant-setup-step-cloud") @customElement("ha-voice-assistant-setup-step-cloud")
@ -10,22 +12,92 @@ export class HaVoiceAssistantSetupStepCloud extends LitElement {
protected override render() { protected override render() {
return html`<div class="content"> return html`<div class="content">
<img src="/static/images/logo_nabu_casa.png" /> <img
<h1>Supercharge your assistant with Home Assistant Cloud</h1> src=${`/static/images/logo_nabu_casa${this.hass.themes?.darkMode ? "_dark" : ""}.png`}
<p class="secondary"> alt="Nabu Casa logo"
Speed up and take the load off your system by running your />
text-to-speech and speech-to-text in our private and secure cloud. <h1>The power of Home Assistant Cloud</h1>
Cloud also includes secure remote access to your system while <div class="features">
supporting the development of Home Assistant. <div class="feature speech">
</p> <div class="logos">
<div class="round-icon">
<ha-svg-icon .path=${mdiMicrophoneMessage}></ha-svg-icon>
</div>
</div>
<h2>
${this.hass.localize(
"ui.panel.config.voice_assistants.assistants.cloud.features.speech.title"
)}
<span class="no-wrap"></span>
</h2>
<p>
${this.hass.localize(
"ui.panel.config.voice_assistants.assistants.cloud.features.speech.text"
)}
</p>
</div>
<div class="feature access">
<div class="logos">
<div class="round-icon">
<ha-svg-icon .path=${mdiEarth}></ha-svg-icon>
</div>
</div>
<h2>
Remote access
<span class="no-wrap"></span>
</h2>
<p>
Secure remote access to your system while supporting the
development of Home Assistant.
</p>
</div>
<div class="feature">
<div class="logos">
<img
alt="Google Assistant"
src=${brandsUrl({
domain: "google_assistant",
type: "icon",
darkOptimized: this.hass.themes?.darkMode,
})}
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<img
alt="Amazon Alexa"
src=${brandsUrl({
domain: "alexa",
type: "icon",
darkOptimized: this.hass.themes?.darkMode,
})}
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
</div>
<h2>
${this.hass.localize(
"ui.panel.config.voice_assistants.assistants.cloud.features.assistants.title"
)}
</h2>
<p>
${this.hass.localize(
"ui.panel.config.voice_assistants.assistants.cloud.features.assistants.text"
)}
</p>
</div>
</div>
</div> </div>
<div class="footer side-by-side"> <div class="footer side-by-side">
<a <a
href="https://www.nabucasa.com" href="https://www.nabucasa.com"
target="_blank" target="_blank"
rel="noreferrer noopenner" rel="noreferrer noopenner"
><ha-button>Learn more</ha-button></a
> >
<ha-button>
<ha-svg-icon .path=${mdiOpenInNew} slot="icon"></ha-svg-icon>
nabucasa.com
</ha-button>
</a>
<a href="/config/cloud/register" @click=${this._close} <a href="/config/cloud/register" @click=${this._close}
><ha-button unelevated>Try 1 month for free</ha-button></a ><ha-button unelevated>Try 1 month for free</ha-button></a
> >
@ -36,7 +108,58 @@ export class HaVoiceAssistantSetupStepCloud extends LitElement {
fireEvent(this, "closed"); fireEvent(this, "closed");
} }
static styles = AssistantSetupStyles; static styles = [
AssistantSetupStyles,
css`
.features {
display: flex;
flex-direction: column;
grid-gap: 16px;
padding: 16px;
}
.feature {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin-bottom: 16px;
}
.feature .logos {
margin-bottom: 16px;
}
.feature .logos > * {
width: 40px;
height: 40px;
margin: 0 4px;
}
.round-icon {
border-radius: 50%;
color: #6e41ab;
background-color: #e8dcf7;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
}
.access .round-icon {
color: #00aef8;
background-color: #cceffe;
}
.feature h2 {
font-weight: 500;
font-size: 16px;
line-height: 24px;
margin-top: 0;
margin-bottom: 8px;
}
.feature p {
font-weight: 400;
font-size: 14px;
line-height: 20px;
margin: 0;
}
`,
];
} }
declare global { declare global {

View File

@ -32,6 +32,10 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
@state() private _showSecond = false; @state() private _showSecond = false;
@state() private _showThird = false;
@state() private _showFourth = false;
protected override willUpdate(changedProperties: PropertyValues): void { protected override willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties); super.willUpdate(changedProperties);
@ -44,63 +48,83 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
super.firstUpdated(changedProperties); super.firstUpdated(changedProperties);
setTimeout(() => { setTimeout(() => {
this._showFirst = true; this._showFirst = true;
}, 1); }, 200);
setTimeout(() => { setTimeout(() => {
this._showSecond = true; this._showSecond = true;
}, 1500); }, 600);
setTimeout(() => {
this._showThird = true;
}, 3000);
setTimeout(() => {
this._showFourth = true;
}, 8000);
} }
protected override render() { protected override render() {
return html`<div class="padding content"> return html`<div class="content">
<div class="messages-container"> <h1>What hardware do you want to use?</h1>
<p class="secondary">
How quickly your assistant responds depends on the power of the
hardware.
</p>
<div class="container">
<div class="messages-container cloud">
<div class="message user ${this._showFirst ? "show" : ""}"> <div class="message user ${this._showFirst ? "show" : ""}">
${!this._showFirst ? "…" : "Turn on the lights in the bedroom"} ${!this._showFirst ? "…" : "Turn on the lights in the bedroom"}
</div> </div>
${this._showFirst
? html`<div class="timing user">0.2 seconds</div>`
: nothing}
${this._showFirst ${this._showFirst
? html` <div class="message hass ${this._showSecond ? "show" : ""}"> ? html` <div class="message hass ${this._showSecond ? "show" : ""}">
${!this._showSecond ? "…" : "Turned on the lights"} ${!this._showSecond ? "…" : "Turned on the lights"}
</div>` </div>`
: nothing} : nothing}
${this._showSecond
? html`<div class="timing hass">0.4 seconds</div>`
: nothing}
</div> </div>
<h1>Select system</h1> <h2>Home Assistant Cloud</h2>
<p class="secondary"> <p>Ideal if you don't have a powerful system at home.</p>
How quickly your voice assistant responds depends on the power of your <ha-button @click=${this._setupCloud}>Learn more</ha-button>
system.
</p>
</div> </div>
<ha-md-list> <div class="container">
<ha-md-list-item interactive type="button" @click=${this._setupCloud}> <div class="messages-container rpi">
Home Assistant Cloud <div class="message user ${this._showThird ? "show" : ""}">
<span slot="supporting-text" ${!this._showThird ? "…" : "Turn on the lights in the bedroom"}
>Ideal if you don't have a powerful system at home</span </div>
> ${this._showThird
<ha-icon-next slot="end"></ha-icon-next> ? html`<div class="timing user">3 seconds</div>`
</ha-md-list-item> : nothing}
<ha-md-list-item interactive type="button" @click=${this._thisSystem}> ${this._showThird
On this system ? html`<div class="message hass ${this._showFourth ? "show" : ""}">
<span slot="supporting-text" ${!this._showFourth ? "…" : "Turned on the lights"}
>Local setup with the Whisper and Piper add-ons</span </div>`
> : nothing}
<ha-icon-next slot="end"></ha-icon-next> ${this._showFourth
</ha-md-list-item> ? html`<div class="timing hass">5 seconds</div>`
<ha-md-list-item : nothing}
interactive </div>
type="link" <h2>Do-it-yourself</h2>
<p>
Install add-ons or containers to run it on your own system. Powerful
hardware is needed for fast responses.
</p>
<a
href=${documentationUrl( href=${documentationUrl(
this.hass, this.hass,
"/voice_control/voice_remote_local_assistant/" "/voice_control/voice_remote_local_assistant/"
)} )}
rel="noreferrer noopenner"
target="_blank" target="_blank"
@click=${this._skip} rel="noreferrer noopenner"
> >
Use external system <ha-button @click=${this._skip}>
<span slot="supporting-text" <ha-svg-icon .path=${mdiOpenInNew} slot="icon"></ha-svg-icon>
>Learn more about how to host it on another system</span Learn more</ha-button
> >
<ha-svg-icon slot="end" .path=${mdiOpenInNew}></ha-svg-icon> </a>
</ha-md-list-item> </div>
</ha-md-list>`; </div>`;
} }
private async _checkCloud() { private async _checkCloud() {
@ -217,10 +241,6 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
this._nextStep(STEP.CLOUD); this._nextStep(STEP.CLOUD);
} }
private async _thisSystem() {
this._nextStep(STEP.ADDONS);
}
private _skip() { private _skip() {
this._nextStep(STEP.SUCCESS); this._nextStep(STEP.SUCCESS);
} }
@ -232,21 +252,22 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
static styles = [ static styles = [
AssistantSetupStyles, AssistantSetupStyles,
css` css`
:host { .container {
padding: 0; border-radius: 16px;
border: 1px solid var(--divider-color);
overflow: hidden;
padding-bottom: 16px;
} }
.padding { .container:last-child {
padding: 24px; margin-top: 16px;
} }
ha-md-list {
width: 100%;
text-align: initial;
}
.messages-container { .messages-container {
padding: 24px; padding: 24px;
box-sizing: border-box; box-sizing: border-box;
height: 152px; height: 195px;
background: var(--input-fill-color);
display: flex;
flex-direction: column;
} }
.message { .message {
white-space: nowrap; white-space: nowrap;
@ -259,21 +280,29 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
transition: width 1s;
width: 30px; width: 30px;
} }
.rpi .message {
transition: width 1s;
}
.cloud .message {
transition: width 0.5s;
}
.message.user { .message.user {
margin-left: 24px; margin-left: 24px;
margin-inline-start: 24px; margin-inline-start: 24px;
margin-inline-end: initial; margin-inline-end: initial;
float: var(--float-end); align-self: self-end;
text-align: right; text-align: right;
border-bottom-right-radius: 0px; border-bottom-right-radius: 0px;
background-color: var(--primary-color); background-color: var(--primary-color);
color: var(--text-primary-color); color: var(--text-primary-color);
direction: var(--direction); direction: var(--direction);
} }
.timing.user {
align-self: self-end;
}
.message.user.show { .message.user.show {
width: 295px; width: 295px;
@ -283,12 +312,15 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
margin-right: 24px; margin-right: 24px;
margin-inline-end: 24px; margin-inline-end: 24px;
margin-inline-start: initial; margin-inline-start: initial;
float: var(--float-start); align-self: self-start;
border-bottom-left-radius: 0px; border-bottom-left-radius: 0px;
background-color: var(--secondary-background-color); background-color: var(--secondary-background-color);
color: var(--primary-text-color); color: var(--primary-text-color);
direction: var(--direction); direction: var(--direction);
} }
.timing.hass {
align-self: self-start;
}
.message.hass.show { .message.hass.show {
width: 184px; width: 184px;

View File

@ -67,11 +67,11 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
: undefined; : undefined;
return html`<div class="content"> return html`<div class="content">
<img src="/static/icons/casita/loving.png" /> <img src="/static/images/voice-assistant/heart.gif" />
<h1>Ready to assist!</h1> <h1>Ready to Assist!</h1>
<p class="secondary"> <p class="secondary">
Your device is all ready to go! If you want to tweak some more Make any final customizations here. You can always change these in the
settings, you can change that below. Voice Assistants section of the settings page.
</p> </p>
<div class="rows"> <div class="rows">
${this.assistConfiguration && ${this.assistConfiguration &&

View File

@ -2,7 +2,13 @@ import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-circular-progress"; import "../../components/ha-circular-progress";
import { OFF, ON, UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { ON, UNAVAILABLE } from "../../data/entity";
import {
updateCanInstall,
UpdateEntity,
updateIsInstalling,
updateUsesProgress,
} from "../../data/update";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { AssistantSetupStyles } from "./styles"; import { AssistantSetupStyles } from "./styles";
@ -51,17 +57,19 @@ export class HaVoiceAssistantSetupStepUpdate extends LitElement {
return nothing; return nothing;
} }
const stateObj = this.hass.states[this.updateEntityId]; const stateObj = this.hass.states[this.updateEntityId] as
| UpdateEntity
| undefined;
const progressIsNumeric = const progressIsNumeric = stateObj && updateUsesProgress(stateObj);
typeof stateObj?.attributes.in_progress === "number";
return html`<div class="content"> return html`<div class="content">
<img src="/static/icons/casita/loading.png" /> <img src="/static/images/voice-assistant/update.gif" />
<h1> <h1>
${stateObj.state === OFF || stateObj.state === UNKNOWN ${stateObj &&
? "Checking for updates" (stateObj.state === "unavailable" || updateIsInstalling(stateObj))
: "Updating your voice assistant"} ? "Updating your voice assistant"
: "Checking for updates"}
</h1> </h1>
<p class="secondary"> <p class="secondary">
We are making sure you have the latest and greatest version of your We are making sure you have the latest and greatest version of your
@ -69,12 +77,12 @@ export class HaVoiceAssistantSetupStepUpdate extends LitElement {
</p> </p>
<ha-circular-progress <ha-circular-progress
.value=${progressIsNumeric .value=${progressIsNumeric
? stateObj.attributes.in_progress / 100 ? (stateObj.attributes.in_progress as number) / 100
: undefined} : undefined}
.indeterminate=${!progressIsNumeric} .indeterminate=${!progressIsNumeric}
></ha-circular-progress> ></ha-circular-progress>
<p> <p>
${stateObj.state === "unavailable" ${stateObj?.state === UNAVAILABLE
? "Restarting voice assistant" ? "Restarting voice assistant"
: progressIsNumeric : progressIsNumeric
? `Installing ${stateObj.attributes.in_progress}%` ? `Installing ${stateObj.attributes.in_progress}%`
@ -88,8 +96,14 @@ export class HaVoiceAssistantSetupStepUpdate extends LitElement {
if (!this.updateEntityId) { if (!this.updateEntityId) {
return; return;
} }
const updateEntity = this.hass.states[this.updateEntityId]; const updateEntity = this.hass.states[this.updateEntityId] as
if (updateEntity && this.hass.states[updateEntity.entity_id].state === ON) { | UpdateEntity
| undefined;
if (
updateEntity &&
this.hass.states[updateEntity.entity_id].state === ON &&
updateCanInstall(updateEntity)
) {
this._updated = true; this._updated = true;
await this.hass.callService( await this.hass.callService(
"update", "update",

View File

@ -65,14 +65,14 @@ export class HaVoiceAssistantSetupStepWakeWord extends LitElement {
return html`<div class="content"> return html`<div class="content">
${!this._detected ${!this._detected
? html` ? html`
<img src="/static/icons/casita/sleeping.png" /> <img src="/static/images/voice-assistant/sleep.gif" />
<h1> <h1>
Say ${this._activeWakeWord(this.assistConfiguration)} to wake the Say ${this._activeWakeWord(this.assistConfiguration)} to wake the
device up device up
</h1> </h1>
<p class="secondary">Setup will continue once the device is awake.</p> <p class="secondary">Setup will continue once the device is awake.</p>
</div>` </div>`
: html`<img src="/static/icons/casita/normal.png" /> : html`<img src="/static/images/voice-assistant/ok-nabu.gif" />
<h1> <h1>
Say ${this._activeWakeWord(this.assistConfiguration)} again Say ${this._activeWakeWord(this.assistConfiguration)} again
</h1> </h1>
@ -80,7 +80,7 @@ export class HaVoiceAssistantSetupStepWakeWord extends LitElement {
To make sure the wake word works for you. To make sure the wake word works for you.
</p>`} </p>`}
</div> </div>
<div class="footer full-width"> <div class="footer centered">
<ha-button @click=${this._changeWakeWord}>Change wake word</ha-button> <ha-button @click=${this._changeWakeWord}>Change wake word</ha-button>
</div>`; </div>`;
} }