frontend/src/dialogs/tts-try/dialog-tts-try.ts

189 lines
5.1 KiB
TypeScript

import { mdiPlayCircleOutline } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { storage } from "../../common/decorators/storage";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-button";
import { createCloseHeading } from "../../components/ha-dialog";
import "../../components/ha-textarea";
import type { HaTextArea } from "../../components/ha-textarea";
import { convertTextToSpeech } from "../../data/tts";
import { HomeAssistant } from "../../types";
import { showAlertDialog } from "../generic/show-dialog-box";
import { TTSTryDialogParams } from "./show-dialog-tts-try";
import "../../components/ha-circular-progress";
@customElement("dialog-tts-try")
export class TTSTryDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _loadingExample = false;
@state() private _params?: TTSTryDialogParams;
@state() private _valid = false;
@query("#message") private _messageInput?: HaTextArea;
@storage({
key: "ttsTryMessages",
state: false,
subscribe: false,
})
private _messages?: Record<string, string>;
public showDialog(params: TTSTryDialogParams) {
this._params = params;
this._valid = Boolean(this._defaultMessage);
}
public closeDialog() {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
private get _defaultMessage() {
const language = this._params!.language?.substring(0, 2);
const userLanguage = this.hass.locale.language.substring(0, 2);
// Load previous message in the right language
if (language && this._messages?.[language]) {
return this._messages[language];
}
// Only display example message if it's interface language
if (language === userLanguage) {
return this.hass.localize("ui.dialogs.tts-try.message_example");
}
return "";
}
protected render() {
if (!this._params) {
return nothing;
}
return html`
<ha-dialog
open
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.dialogs.tts-try.header")
)}
>
<ha-textarea
autogrow
id="message"
.label=${this.hass.localize("ui.dialogs.tts-try.message")}
.placeholder=${this.hass.localize(
"ui.dialogs.tts-try.message_placeholder"
)}
.value=${this._defaultMessage}
@input=${this._inputChanged}
?dialogInitialFocus=${!this._defaultMessage}
>
</ha-textarea>
${this._loadingExample
? html`
<ha-circular-progress
size="small"
indeterminate
slot="primaryAction"
class="loading"
></ha-circular-progress>
`
: html`
<ha-button
?dialogInitialFocus=${Boolean(this._defaultMessage)}
slot="primaryAction"
.label=${this.hass.localize("ui.dialogs.tts-try.play")}
@click=${this._playExample}
.disabled=${!this._valid}
>
<ha-svg-icon
slot="icon"
.path=${mdiPlayCircleOutline}
></ha-svg-icon>
</ha-button>
`}
</ha-dialog>
`;
}
private async _inputChanged() {
this._valid = Boolean(this._messageInput?.value);
}
private async _playExample() {
const message = this._messageInput?.value;
if (!message) {
return;
}
const platform = this._params!.engine;
const language = this._params!.language;
const voice = this._params!.voice;
if (language) {
this._messages = {
...this._messages,
[language.substring(0, 2)]: message,
};
}
this._loadingExample = true;
const audio = new Audio();
audio.play();
let url;
try {
const result = await convertTextToSpeech(this.hass, {
platform,
message,
language,
options: { voice },
});
url = result.path;
} catch (err: any) {
this._loadingExample = false;
showAlertDialog(this, {
text: `Unable to load example. ${err.error || err.body || err}`,
warning: true,
});
return;
}
audio.src = url;
audio.addEventListener("canplaythrough", () => audio.play());
audio.addEventListener("playing", () => {
this._loadingExample = false;
});
audio.addEventListener("error", () => {
showAlertDialog(this, { title: "Error playing audio." });
this._loadingExample = false;
});
}
static get styles(): CSSResultGroup {
return css`
ha-dialog {
--mdc-dialog-max-width: 500px;
}
ha-textarea,
ha-select {
width: 100%;
}
ha-select {
margin-top: 8px;
}
.loading {
height: 36px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-tts-try": TTSTryDialog;
}
}