diff --git a/src/panels/config/cloud/account/cloud-tts-pref.ts b/src/panels/config/cloud/account/cloud-tts-pref.ts index 2042e6b459..828590f47d 100644 --- a/src/panels/config/cloud/account/cloud-tts-pref.ts +++ b/src/panels/config/cloud/account/cloud-tts-pref.ts @@ -23,12 +23,12 @@ import { updateCloudPref, } from "../../../../data/cloud"; import type { HomeAssistant } from "../../../../types"; -import { convertTextToSpeech } from "../../../../data/tts"; import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box"; import { translationMetadata } from "../../../../resources/translations-metadata"; import { caseInsensitiveCompare } from "../../../../common/string/compare"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; +import { showTryTtsDialog } from "./show-dialog-cloud-tts-try"; @customElement("cloud-tts-pref") export class CloudTTSPref extends LitElement { @@ -36,8 +36,6 @@ export class CloudTTSPref extends LitElement { @property() public cloudStatus?: CloudStatusLoggedIn; - @internalProperty() private loadingExample = false; - @internalProperty() private savingPreferences = false; @internalProperty() private ttsInfo?: CloudTTSInfo; @@ -62,12 +60,9 @@ export class CloudTTSPref extends LitElement { header=${this.hass.localize("ui.panel.config.cloud.account.tts.title")} >
- + -  Example +  ${this.hass.localize("ui.panel.config.cloud.account.tts.try")}
@@ -191,46 +186,9 @@ export class CloudTTSPref extends LitElement { } ); - async _playExample() { - this.loadingExample = true; - const defaultVoice = this.cloudStatus!.prefs.tts_default_voice; - // Our example sentence is English. If user uses English voice, use that - // for example. - let language; - let gender; - if (defaultVoice[0].split("-")[0] === "en") { - language = defaultVoice[0]; - gender = defaultVoice[1]; - } else { - language = "en-US"; - gender = "female"; - } - - let url; - try { - const result = await convertTextToSpeech(this.hass, { - platform: "cloud", - message: `Hello ${ - this.hass.user!.name - }, you can play any text on any supported media player!`, - language, - options: { gender }, - }); - url = result.url; - } catch (err) { - this.loadingExample = false; - // eslint-disable-next-line no-console - console.error(err); - showAlertDialog(this, { - text: `Unable to load example. ${err}`, - warning: true, - }); - return; - } - const audio = new Audio(url); - audio.play(); - audio.addEventListener("playing", () => { - this.loadingExample = false; + private _openTryDialog() { + showTryTtsDialog(this, { + defaultVoice: this.cloudStatus!.prefs.tts_default_voice, }); } diff --git a/src/panels/config/cloud/account/dialog-cloud-tts-try.ts b/src/panels/config/cloud/account/dialog-cloud-tts-try.ts new file mode 100644 index 0000000000..0fb9891aeb --- /dev/null +++ b/src/panels/config/cloud/account/dialog-cloud-tts-try.ts @@ -0,0 +1,201 @@ +import "@material/mwc-button"; +import { + css, + CSSResult, + customElement, + html, + internalProperty, + LitElement, + property, + query, + TemplateResult, +} from "lit-element"; +import { HomeAssistant } from "../../../../types"; +import { TryTtsDialogParams } from "./show-dialog-cloud-tts-try"; +import { haStyleDialog } from "../../../../resources/styles"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { convertTextToSpeech } from "../../../../data/tts"; +import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box"; +import "@polymer/paper-input/paper-textarea"; +import "../../../../components/ha-paper-dropdown-menu"; +import { computeStateDomain } from "../../../../common/entity/compute_state_domain"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; +import { supportsFeature } from "../../../../common/entity/supports-feature"; +import { SUPPORT_PLAY_MEDIA } from "../../../../data/media-player"; +import { createCloseHeading } from "../../../../components/ha-dialog"; +import { mdiPlayCircleOutline } from "@mdi/js"; +import type { PaperListboxElement } from "@polymer/paper-listbox/paper-listbox"; +import type { PaperTextareaElement } from "@polymer/paper-input/paper-textarea"; +import { LocalStorage } from "../../../../common/decorators/local-storage"; + +@customElement("dialog-cloud-try-tts") +export class DialogTryTts extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @internalProperty() private _loadingExample = false; + + @internalProperty() private _params?: TryTtsDialogParams; + + @query("#target") private _targetInput?: PaperListboxElement; + + @query("#message") private _messageInput?: PaperTextareaElement; + + @LocalStorage("cloudTtsTryMessage") private _message?: string; + + @LocalStorage("cloudTtsTryTarget") private _target?: string; + + public showDialog(params: TryTtsDialogParams) { + this._params = params; + } + + public closeDialog() { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render(): TemplateResult { + if (!this._params) { + return html``; + } + return html` + +
+ + + + + + + ${this.hass.localize( + "ui.panel.config.cloud.account.tts.dialog.target_browser" + )} + + ${Object.values(this.hass.states) + .filter( + (entity) => + computeStateDomain(entity) === "media_player" && + supportsFeature(entity, SUPPORT_PLAY_MEDIA) + ) + .map( + (entity) => html` + + ${computeStateName(entity)} + + ` + )} + + +
+ + +  ${this.hass.localize( + "ui.panel.config.cloud.account.tts.dialog.play" + )} + +
+ `; + } + + private async _playExample() { + const target = String(this._targetInput?.selected); + const message = this._messageInput?.value; + + if (!message || !target) { + return; + } + + this._message = message; + this._target = target; + + if (target === "browser") { + this._playBrowser(message); + } else { + this.hass.callService("tts", "cloud_say", { + entity_id: target, + message, + }); + } + } + + private async _playBrowser(message: string) { + this._loadingExample = true; + + const language = this._params!.defaultVoice[0]; + const gender = this._params!.defaultVoice[1]; + + let url; + try { + const result = await convertTextToSpeech(this.hass, { + platform: "cloud", + message, + language, + options: { gender }, + }); + url = result.url; + } catch (err) { + this._loadingExample = false; + showAlertDialog(this, { + text: `Unable to load example. ${err.error || err.body || err}`, + warning: true, + }); + return; + } + const audio = new Audio(url); + audio.addEventListener("canplaythrough", () => { + audio.play(); + }); + audio.addEventListener("playing", () => { + this._loadingExample = false; + }); + } + + static get styles(): CSSResult[] { + return [ + haStyleDialog, + css` + ha-dialog { + --mdc-dialog-max-width: 500px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-cloud-try-tts": DialogTryTts; + } +} diff --git a/src/panels/config/cloud/account/show-dialog-cloud-tts-try.ts b/src/panels/config/cloud/account/show-dialog-cloud-tts-try.ts new file mode 100644 index 0000000000..e1afa19d3d --- /dev/null +++ b/src/panels/config/cloud/account/show-dialog-cloud-tts-try.ts @@ -0,0 +1,18 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; + +export interface TryTtsDialogParams { + defaultVoice: [string, string]; +} + +export const loadTryTtsDialog = () => import("./dialog-cloud-tts-try"); + +export const showTryTtsDialog = ( + element: HTMLElement, + dialogParams: TryTtsDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-cloud-try-tts", + dialogImport: loadTryTtsDialog, + dialogParams, + }); +}; diff --git a/src/translations/en.json b/src/translations/en.json index d969ca9c94..3255368b8c 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1682,7 +1682,15 @@ "info": "Bring personality to your home by having it speak to you by using our Text-to-Speech services. You can use this in automations and scripts by using the {service} service.", "default_language": "Default language to use", "male": "Male", - "female": "Female" + "female": "Female", + "try": "Try", + "dialog": { + "header": "Try Text to Speech", + "example_message": "Hello {name}, you can play any text on any supported media player!", + "target": "Target", + "target_browser": "Browser", + "play": "Play" + } }, "remote": { "title": "Remote Control",