189 lines
5.1 KiB
TypeScript
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;
|
|
}
|
|
}
|