Add simple try tts dialog (#8245)

This commit is contained in:
Bram Kragten 2021-01-27 15:58:58 +01:00 committed by GitHub
parent 4f0bb9f6c3
commit b22455d2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 234 additions and 49 deletions

View File

@ -23,12 +23,12 @@ import {
updateCloudPref, updateCloudPref,
} from "../../../../data/cloud"; } from "../../../../data/cloud";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { convertTextToSpeech } from "../../../../data/tts";
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
import { translationMetadata } from "../../../../resources/translations-metadata"; import { translationMetadata } from "../../../../resources/translations-metadata";
import { caseInsensitiveCompare } from "../../../../common/string/compare"; import { caseInsensitiveCompare } from "../../../../common/string/compare";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { showTryTtsDialog } from "./show-dialog-cloud-tts-try";
@customElement("cloud-tts-pref") @customElement("cloud-tts-pref")
export class CloudTTSPref extends LitElement { export class CloudTTSPref extends LitElement {
@ -36,8 +36,6 @@ export class CloudTTSPref extends LitElement {
@property() public cloudStatus?: CloudStatusLoggedIn; @property() public cloudStatus?: CloudStatusLoggedIn;
@internalProperty() private loadingExample = false;
@internalProperty() private savingPreferences = false; @internalProperty() private savingPreferences = false;
@internalProperty() private ttsInfo?: CloudTTSInfo; @internalProperty() private ttsInfo?: CloudTTSInfo;
@ -62,12 +60,9 @@ export class CloudTTSPref extends LitElement {
header=${this.hass.localize("ui.panel.config.cloud.account.tts.title")} header=${this.hass.localize("ui.panel.config.cloud.account.tts.title")}
> >
<div class="example"> <div class="example">
<mwc-button <mwc-button @click=${this._openTryDialog}>
@click=${this._playExample}
.disabled=${this.loadingExample}
>
<ha-svg-icon .path=${mdiPlayCircleOutline}></ha-svg-icon> <ha-svg-icon .path=${mdiPlayCircleOutline}></ha-svg-icon>
&nbsp;Example &nbsp;${this.hass.localize("ui.panel.config.cloud.account.tts.try")}
</mwc-button> </mwc-button>
</div> </div>
<div class="card-content"> <div class="card-content">
@ -191,46 +186,9 @@ export class CloudTTSPref extends LitElement {
} }
); );
async _playExample() { private _openTryDialog() {
this.loadingExample = true; showTryTtsDialog(this, {
const defaultVoice = this.cloudStatus!.prefs.tts_default_voice; 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;
}); });
} }

View File

@ -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`
<ha-dialog
open
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.cloud.account.tts.dialog.header")
)}
>
<div>
<paper-textarea
id="message"
label="Message"
.value=${this._message ||
this.hass.localize(
"ui.panel.config.cloud.account.tts.dialog.example_message",
"name",
this.hass.user!.name
)}
>
</paper-textarea>
<ha-paper-dropdown-menu
.label=${this.hass.localize(
"ui.panel.config.cloud.account.tts.dialog.target"
)}
>
<paper-listbox
id="target"
slot="dropdown-content"
attr-for-selected="item-value"
.selected=${this._target || "browser"}
>
<paper-item item-value="browser">
${this.hass.localize(
"ui.panel.config.cloud.account.tts.dialog.target_browser"
)}
</paper-item>
${Object.values(this.hass.states)
.filter(
(entity) =>
computeStateDomain(entity) === "media_player" &&
supportsFeature(entity, SUPPORT_PLAY_MEDIA)
)
.map(
(entity) => html`
<paper-item .itemValue=${entity.entity_id}>
${computeStateName(entity)}
</paper-item>
`
)}
</paper-listbox>
</ha-paper-dropdown-menu>
</div>
<mwc-button
slot="primaryAction"
@click=${this._playExample}
.disabled=${this._loadingExample}
>
<ha-svg-icon .path=${mdiPlayCircleOutline}></ha-svg-icon>
&nbsp;${this.hass.localize(
"ui.panel.config.cloud.account.tts.dialog.play"
)}
</mwc-button>
</ha-dialog>
`;
}
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;
}
}

View File

@ -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,
});
};

View File

@ -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.", "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", "default_language": "Default language to use",
"male": "Male", "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": { "remote": {
"title": "Remote Control", "title": "Remote Control",