Update voice wizard (#22472)
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.4 KiB |
BIN
public/static/images/logo_nabu_casa_dark.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
public/static/images/voice-assistant/area.gif
Normal file
After Width: | Height: | Size: 372 KiB |
BIN
public/static/images/voice-assistant/change-wake-word.gif
Normal file
After Width: | Height: | Size: 383 KiB |
BIN
public/static/images/voice-assistant/error.gif
Normal file
After Width: | Height: | Size: 377 KiB |
BIN
public/static/images/voice-assistant/heart.gif
Normal file
After Width: | Height: | Size: 389 KiB |
BIN
public/static/images/voice-assistant/hi.gif
Normal file
After Width: | Height: | Size: 379 KiB |
BIN
public/static/images/voice-assistant/ok-nabu.gif
Normal file
After Width: | Height: | Size: 381 KiB |
BIN
public/static/images/voice-assistant/sleep.gif
Normal file
After Width: | Height: | Size: 374 KiB |
BIN
public/static/images/voice-assistant/update.gif
Normal file
After Width: | Height: | Size: 379 KiB |
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
`;
|
`;
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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 &&
|
||||||
|
@ -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",
|
||||||
|
@ -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>`;
|
||||||
}
|
}
|
||||||
|