243 lines
7.0 KiB
TypeScript
243 lines
7.0 KiB
TypeScript
import "@material/mwc-button";
|
|
import "@material/mwc-list/mwc-list-item";
|
|
import { mdiPlayCircleOutline, mdiRobot } 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 { stopPropagation } from "../../../../common/dom/stop_propagation";
|
|
import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
|
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
|
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
|
import "../../../../components/ha-select";
|
|
import "../../../../components/ha-textarea";
|
|
import type { HaTextArea } from "../../../../components/ha-textarea";
|
|
import { showAutomationEditor } from "../../../../data/automation";
|
|
import { MediaPlayerEntityFeature } from "../../../../data/media-player";
|
|
import { convertTextToSpeech } from "../../../../data/tts";
|
|
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
|
import { haStyleDialog } from "../../../../resources/styles";
|
|
import { HomeAssistant } from "../../../../types";
|
|
import { TryTtsDialogParams } from "./show-dialog-cloud-tts-try";
|
|
|
|
@customElement("dialog-cloud-try-tts")
|
|
export class DialogTryTts extends LitElement {
|
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
|
|
@state() private _loadingExample = false;
|
|
|
|
@state() private _params?: TryTtsDialogParams;
|
|
|
|
@query("#message") private _messageInput?: HaTextArea;
|
|
|
|
@storage({
|
|
key: "cloudTtsTryMessage",
|
|
state: false,
|
|
subscribe: false,
|
|
})
|
|
private _message!: string;
|
|
|
|
@storage({
|
|
key: "cloudTtsTryTarget",
|
|
state: false,
|
|
subscribe: false,
|
|
})
|
|
private _target!: string;
|
|
|
|
public showDialog(params: TryTtsDialogParams) {
|
|
this._params = params;
|
|
}
|
|
|
|
public closeDialog() {
|
|
this._params = undefined;
|
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
|
}
|
|
|
|
protected render() {
|
|
if (!this._params) {
|
|
return nothing;
|
|
}
|
|
const target = this._target || "browser";
|
|
return html`
|
|
<ha-dialog
|
|
open
|
|
@closed=${this.closeDialog}
|
|
scrimClickAction
|
|
escapeKeyAction
|
|
.heading=${createCloseHeading(
|
|
this.hass,
|
|
this.hass.localize("ui.panel.config.cloud.account.tts.dialog.header")
|
|
)}
|
|
>
|
|
<div>
|
|
<ha-textarea
|
|
autogrow
|
|
id="message"
|
|
label="Message"
|
|
.value=${this._message ||
|
|
this.hass.localize(
|
|
"ui.panel.config.cloud.account.tts.dialog.example_message",
|
|
{ name: this.hass.user!.name }
|
|
)}
|
|
>
|
|
</ha-textarea>
|
|
|
|
<ha-select
|
|
.label=${this.hass.localize(
|
|
"ui.panel.config.cloud.account.tts.dialog.target"
|
|
)}
|
|
id="target"
|
|
.value=${target}
|
|
@selected=${this._handleTargetChanged}
|
|
fixedMenuPosition
|
|
naturalMenuWidth
|
|
@closed=${stopPropagation}
|
|
>
|
|
<mwc-list-item value="browser">
|
|
${this.hass.localize(
|
|
"ui.panel.config.cloud.account.tts.dialog.target_browser"
|
|
)}
|
|
</mwc-list-item>
|
|
${Object.values(this.hass.states)
|
|
.filter(
|
|
(entity) =>
|
|
computeStateDomain(entity) === "media_player" &&
|
|
supportsFeature(entity, MediaPlayerEntityFeature.PLAY_MEDIA)
|
|
)
|
|
.map(
|
|
(entity) => html`
|
|
<mwc-list-item .value=${entity.entity_id}>
|
|
${computeStateName(entity)}
|
|
</mwc-list-item>
|
|
`
|
|
)}
|
|
</ha-select>
|
|
</div>
|
|
<mwc-button
|
|
slot="primaryAction"
|
|
.label=${this.hass.localize(
|
|
"ui.panel.config.cloud.account.tts.dialog.play"
|
|
)}
|
|
@click=${this._playExample}
|
|
.disabled=${this._loadingExample}
|
|
>
|
|
<ha-svg-icon slot="icon" .path=${mdiPlayCircleOutline}></ha-svg-icon>
|
|
</mwc-button>
|
|
<mwc-button
|
|
slot="secondaryAction"
|
|
.disabled=${target === "browser"}
|
|
.label=${this.hass.localize(
|
|
"ui.panel.config.cloud.account.tts.dialog.create_automation"
|
|
)}
|
|
@click=${this._createAutomation}
|
|
>
|
|
<ha-svg-icon slot="icon" .path=${mdiRobot}></ha-svg-icon>
|
|
</mwc-button>
|
|
</ha-dialog>
|
|
`;
|
|
}
|
|
|
|
private _handleTargetChanged(ev) {
|
|
this._target = ev.target.value;
|
|
this.requestUpdate("_target");
|
|
}
|
|
|
|
private async _playExample() {
|
|
const message = this._messageInput?.value;
|
|
if (!message) {
|
|
return;
|
|
}
|
|
this._message = message;
|
|
|
|
if (this._target === "browser") {
|
|
// We create the audio element here + do a play, because iOS requires it to be done by user action
|
|
const audio = new Audio();
|
|
audio.play();
|
|
this._playBrowser(message, audio);
|
|
} else {
|
|
this.hass.callService("tts", "cloud_say", {
|
|
entity_id: this._target,
|
|
message,
|
|
});
|
|
}
|
|
}
|
|
|
|
private _createAutomation() {
|
|
const message = this._messageInput!.value!;
|
|
this._message = message;
|
|
showAutomationEditor({
|
|
action: [
|
|
{
|
|
service: "tts.cloud_say",
|
|
data: {
|
|
entity_id: this._target,
|
|
message: message,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
this.closeDialog();
|
|
}
|
|
|
|
private async _playBrowser(message: string, audio: HTMLAudioElement) {
|
|
this._loadingExample = true;
|
|
|
|
const language = this._params!.defaultVoice[0];
|
|
const voice = this._params!.defaultVoice[1];
|
|
|
|
let url;
|
|
try {
|
|
const result = await convertTextToSpeech(this.hass, {
|
|
platform: "cloud",
|
|
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 [
|
|
haStyleDialog,
|
|
css`
|
|
ha-dialog {
|
|
--mdc-dialog-max-width: 500px;
|
|
}
|
|
ha-textarea,
|
|
ha-select {
|
|
width: 100%;
|
|
}
|
|
ha-select {
|
|
margin-top: 8px;
|
|
}
|
|
`,
|
|
];
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"dialog-cloud-try-tts": DialogTryTts;
|
|
}
|
|
}
|