mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-16 05:46:35 +00:00
Improve cloud dashboard (#11422)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
68bee4dd58
commit
f398692e75
@ -51,11 +51,13 @@ export interface CloudStatusLoggedIn {
|
||||
google_registered: boolean;
|
||||
google_entities: EntityFilter;
|
||||
google_domains: string[];
|
||||
alexa_registered: boolean;
|
||||
alexa_entities: EntityFilter;
|
||||
prefs: CloudPreferences;
|
||||
remote_domain: string | undefined;
|
||||
remote_connected: boolean;
|
||||
remote_certificate: undefined | CertificateInformation;
|
||||
http_use_ssl: boolean;
|
||||
}
|
||||
|
||||
export type CloudStatus = CloudStatusNotLoggedIn | CloudStatusLoggedIn;
|
||||
|
@ -8,3 +8,6 @@ export interface GoogleEntity {
|
||||
|
||||
export const fetchCloudGoogleEntities = (hass: HomeAssistant) =>
|
||||
hass.callWS<GoogleEntity[]>({ type: "cloud/google_assistant/entities" });
|
||||
|
||||
export const syncCloudGoogleEntities = (hass: HomeAssistant) =>
|
||||
hass.callApi("POST", "cloud/google_actions/sync");
|
||||
|
@ -1,5 +1,8 @@
|
||||
import "@material/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import type { ActionDetail } from "@material/mwc-list";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import { LitElement, css, html, PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatDateTime } from "../../../../common/datetime/format_date_time";
|
||||
@ -7,6 +10,8 @@ import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||
import "../../../../components/buttons/ha-call-api-button";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import {
|
||||
cloudLogout,
|
||||
CloudStatusLoggedIn,
|
||||
@ -21,9 +26,10 @@ import "./cloud-google-pref";
|
||||
import "./cloud-remote-pref";
|
||||
import "./cloud-tts-pref";
|
||||
import "./cloud-webhooks";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
|
||||
@customElement("cloud-account")
|
||||
export class CloudAccount extends LitElement {
|
||||
export class CloudAccount extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public isWide = false;
|
||||
@ -43,6 +49,23 @@ export class CloudAccount extends LitElement {
|
||||
.narrow=${this.narrow}
|
||||
header="Home Assistant Cloud"
|
||||
>
|
||||
<ha-button-menu
|
||||
slot="toolbar-icon"
|
||||
corner="BOTTOM_START"
|
||||
@action=${this._handleMenuAction}
|
||||
activatable
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
|
||||
<mwc-list-item>
|
||||
${this.hass.localize("ui.panel.config.cloud.account.sign_out")}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
|
||||
<div class="content">
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<span slot="header">Home Assistant Cloud</span>
|
||||
@ -115,11 +138,6 @@ export class CloudAccount extends LitElement {
|
||||
)}
|
||||
</mwc-button>
|
||||
</a>
|
||||
<mwc-button @click=${this._handleLogout}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.sign_out"
|
||||
)}</mwc-button
|
||||
>
|
||||
</div>
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
@ -200,6 +218,33 @@ export class CloudAccount extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
protected override hassSubscribe() {
|
||||
const googleCheck = () => {
|
||||
if (!this.cloudStatus?.google_registered) {
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
}
|
||||
};
|
||||
return [
|
||||
this.hass.connection.subscribeEvents(() => {
|
||||
if (!this.cloudStatus?.alexa_registered) {
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
}
|
||||
}, "alexa_smart_home"),
|
||||
this.hass.connection.subscribeEvents(
|
||||
googleCheck,
|
||||
"google_assistant_command"
|
||||
),
|
||||
this.hass.connection.subscribeEvents(
|
||||
googleCheck,
|
||||
"google_assistant_query"
|
||||
),
|
||||
this.hass.connection.subscribeEvents(
|
||||
googleCheck,
|
||||
"google_assistant_sync"
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private async _fetchSubscriptionInfo() {
|
||||
this._subscription = await fetchCloudSubscriptionInfo(this.hass);
|
||||
if (
|
||||
@ -211,9 +256,12 @@ export class CloudAccount extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleLogout() {
|
||||
await cloudLogout(this.hass);
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
await cloudLogout(this.hass);
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
}
|
||||
}
|
||||
|
||||
_computeRTLDirection(hass) {
|
||||
@ -237,7 +285,7 @@ export class CloudAccount extends LitElement {
|
||||
}
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.card-actions a {
|
||||
text-decoration: none;
|
||||
|
@ -10,7 +10,7 @@ import { CloudStatusLoggedIn, updateCloudPref } from "../../../../data/cloud";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
||||
export class CloudAlexaPref extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public cloudStatus?: CloudStatusLoggedIn;
|
||||
|
||||
@ -21,6 +21,7 @@ export class CloudAlexaPref extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const alexa_registered = this.cloudStatus.alexa_registered;
|
||||
const { alexa_enabled, alexa_report_state } = this.cloudStatus!.prefs;
|
||||
|
||||
return html`
|
||||
@ -36,33 +37,49 @@ export class CloudAlexaPref extends LitElement {
|
||||
></ha-switch>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
${this.hass!.localize("ui.panel.config.cloud.account.alexa.info")}
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://skills-store.amazon.com/deeplink/dp/B0772J1QKB?deviceType=app"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.enable_ha_skill"
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.nabucasa.com/config/amazon_alexa/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.config_documentation"
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
${alexa_enabled
|
||||
<p>
|
||||
${this.hass!.localize("ui.panel.config.cloud.account.alexa.info")}
|
||||
</p>
|
||||
${!alexa_enabled
|
||||
? ""
|
||||
: !alexa_registered
|
||||
? html`
|
||||
<ha-alert
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.alexa.not_configured_title"
|
||||
)}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.alexa.not_configured_text"
|
||||
)}
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://skills-store.amazon.com/deeplink/dp/B0772J1QKB?deviceType=app"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.enable_ha_skill"
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.nabucasa.com/config/amazon_alexa/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.config_documentation"
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</ha-alert>
|
||||
`
|
||||
: html`
|
||||
<div class="state-reporting">
|
||||
<h3>
|
||||
${this.hass!.localize(
|
||||
@ -81,18 +98,21 @@ export class CloudAlexaPref extends LitElement {
|
||||
"ui.panel.config.cloud.account.alexa.info_state_reporting"
|
||||
)}
|
||||
</p>
|
||||
`
|
||||
: ""}
|
||||
`}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button
|
||||
@click=${this._handleSync}
|
||||
.disabled=${!alexa_enabled || this._syncing}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.sync_entities"
|
||||
)}
|
||||
</mwc-button>
|
||||
${alexa_registered
|
||||
? html`
|
||||
<mwc-button
|
||||
@click=${this._handleSync}
|
||||
.disabled=${!alexa_enabled || this._syncing}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.sync_entities"
|
||||
)}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
<div class="spacer"></div>
|
||||
<a href="/config/cloud/alexa">
|
||||
<mwc-button
|
||||
|
@ -1,14 +1,14 @@
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||
import "@material/mwc-textfield/mwc-textfield";
|
||||
import type { TextField } from "@material/mwc-textfield/mwc-textfield";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { property } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/buttons/ha-call-api-button";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-card";
|
||||
import type { HaSwitch } from "../../../../components/ha-switch";
|
||||
import { CloudStatusLoggedIn, updateCloudPref } from "../../../../data/cloud";
|
||||
import { syncCloudGoogleEntities } from "../../../../data/google_assistant";
|
||||
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
|
||||
@ -16,13 +16,16 @@ import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
|
||||
export class CloudGooglePref extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public cloudStatus?: CloudStatusLoggedIn;
|
||||
@property({ attribute: false }) public cloudStatus?: CloudStatusLoggedIn;
|
||||
|
||||
@state() private _syncing = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.cloudStatus) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const google_registered = this.cloudStatus.google_registered;
|
||||
const { google_enabled, google_report_state, google_secure_devices_pin } =
|
||||
this.cloudStatus.prefs;
|
||||
|
||||
@ -43,7 +46,9 @@ export class CloudGooglePref extends LitElement {
|
||||
<p>
|
||||
${this.hass.localize("ui.panel.config.cloud.account.google.info")}
|
||||
</p>
|
||||
${google_enabled && !this.cloudStatus.google_registered
|
||||
${!google_enabled
|
||||
? ""
|
||||
: !google_registered
|
||||
? html`
|
||||
<ha-alert
|
||||
.title=${this.hass.localize(
|
||||
@ -80,9 +85,30 @@ export class CloudGooglePref extends LitElement {
|
||||
</ul>
|
||||
</ha-alert>
|
||||
`
|
||||
: ""}
|
||||
${google_enabled
|
||||
? html`
|
||||
: html`
|
||||
${this.cloudStatus.http_use_ssl
|
||||
? html`
|
||||
<ha-alert
|
||||
alert-type="warning"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.http_use_ssl_warning_title"
|
||||
)}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.http_use_ssl_warning_text"
|
||||
)}
|
||||
<a
|
||||
href="https://www.nabucasa.com/config/google_assistant/#local-communication"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.common.learn_more"
|
||||
)}</a
|
||||
>
|
||||
</ha-alert>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<div class="state-reporting">
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
@ -110,32 +136,33 @@ export class CloudGooglePref extends LitElement {
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.enter_pin_info"
|
||||
)}
|
||||
<paper-input
|
||||
label=${this.hass.localize(
|
||||
<mwc-textfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.devices_pin"
|
||||
)}
|
||||
id="google_secure_devices_pin"
|
||||
placeholder=${this.hass.localize(
|
||||
.placeholder=${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.enter_pin_hint"
|
||||
)}
|
||||
.value=${google_secure_devices_pin || ""}
|
||||
@change=${this._pinChanged}
|
||||
></paper-input>
|
||||
></mwc-textfield>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
`}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-api-button
|
||||
.hass=${this.hass}
|
||||
.disabled=${!google_enabled}
|
||||
@hass-api-called=${this._syncEntitiesCalled}
|
||||
path="cloud/google_actions/sync"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.sync_entities"
|
||||
)}
|
||||
</ha-call-api-button>
|
||||
${google_registered
|
||||
? html`
|
||||
<mwc-button
|
||||
@click=${this._handleSync}
|
||||
.disabled=${!google_enabled || this._syncing}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.sync_entities"
|
||||
)}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
<div class="spacer"></div>
|
||||
<a href="/config/cloud/google-assistant">
|
||||
<mwc-button>
|
||||
${this.hass.localize(
|
||||
@ -148,24 +175,31 @@ export class CloudGooglePref extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _syncEntitiesCalled(ev: CustomEvent) {
|
||||
if (!ev.detail.success && ev.detail.response.status_code === 404) {
|
||||
this._syncFailed();
|
||||
private async _handleSync() {
|
||||
this._syncing = true;
|
||||
try {
|
||||
await syncCloudGoogleEntities(this.hass!);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
`ui.panel.config.cloud.account.google.${
|
||||
err.status_code === 404
|
||||
? "not_configured_title"
|
||||
: "sync_failed_title"
|
||||
}`
|
||||
),
|
||||
text: this.hass.localize(
|
||||
`ui.panel.config.cloud.account.google.${
|
||||
err.status_code === 404 ? "not_configured_text" : "sync_failed_text"
|
||||
}`
|
||||
),
|
||||
});
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
} finally {
|
||||
this._syncing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async _syncFailed() {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.not_configured_title"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.cloud.account.google.not_configured_text"
|
||||
),
|
||||
});
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
}
|
||||
|
||||
private async _enableToggleChanged(ev) {
|
||||
const toggle = ev.target as HaSwitch;
|
||||
try {
|
||||
@ -194,7 +228,7 @@ export class CloudGooglePref extends LitElement {
|
||||
}
|
||||
|
||||
private async _pinChanged(ev) {
|
||||
const input = ev.target as PaperInputElement;
|
||||
const input = ev.target as TextField;
|
||||
try {
|
||||
await updateCloudPref(this.hass, {
|
||||
[input.id]: input.value || null,
|
||||
@ -207,7 +241,7 @@ export class CloudGooglePref extends LitElement {
|
||||
"ui.panel.config.cloud.account.google.enter_pin_error"
|
||||
)} ${err.message}`
|
||||
);
|
||||
input.value = this.cloudStatus!.prefs.google_secure_devices_pin;
|
||||
input.value = this.cloudStatus!.prefs.google_secure_devices_pin || "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,16 +259,13 @@ export class CloudGooglePref extends LitElement {
|
||||
right: auto;
|
||||
left: 24px;
|
||||
}
|
||||
ha-call-api-button {
|
||||
color: var(--primary-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
paper-input {
|
||||
mwc-textfield {
|
||||
width: 250px;
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card-actions a {
|
||||
text-decoration: none;
|
||||
@ -245,6 +276,10 @@ export class CloudGooglePref extends LitElement {
|
||||
.secure_devices {
|
||||
padding-top: 8px;
|
||||
}
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.state-reporting {
|
||||
display: flex;
|
||||
margin-top: 1.5em;
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
mdiCloseBox,
|
||||
mdiCloseBoxMultiple,
|
||||
} from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
@ -33,9 +34,14 @@ import {
|
||||
updateCloudAlexaEntityConfig,
|
||||
updateCloudPref,
|
||||
} from "../../../../data/cloud";
|
||||
import {
|
||||
EntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../../data/entity_registry";
|
||||
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
||||
import "../../../../layouts/hass-loading-screen";
|
||||
import "../../../../layouts/hass-subpage";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
||||
@ -43,7 +49,7 @@ const DEFAULT_CONFIG_EXPOSE = true;
|
||||
const IGNORE_INTERFACES = ["Alexa.EndpointHealth"];
|
||||
|
||||
@customElement("cloud-alexa")
|
||||
class CloudAlexa extends LitElement {
|
||||
class CloudAlexa extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property()
|
||||
@ -53,9 +59,15 @@ class CloudAlexa extends LitElement {
|
||||
|
||||
@state() private _entities?: AlexaEntity[];
|
||||
|
||||
@property()
|
||||
@state()
|
||||
private _entityConfigs: CloudPreferences["alexa_entity_configs"] = {};
|
||||
|
||||
@state()
|
||||
private _entityCategories?: Record<
|
||||
string,
|
||||
EntityRegistryEntry["entity_category"]
|
||||
>;
|
||||
|
||||
private _popstateSyncAttached = false;
|
||||
|
||||
private _popstateReloadStatusAttached = false;
|
||||
@ -72,7 +84,7 @@ class CloudAlexa extends LitElement {
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._entities === undefined) {
|
||||
if (this._entities === undefined || this._entityCategories === undefined) {
|
||||
return html` <hass-loading-screen></hass-loading-screen> `;
|
||||
}
|
||||
const emptyFilter = isEmptyFilter(this.cloudStatus.alexa_entities);
|
||||
@ -99,10 +111,17 @@ class CloudAlexa extends LitElement {
|
||||
should_expose: null,
|
||||
};
|
||||
const isExposed = emptyFilter
|
||||
? this._configIsExposed(entity.entity_id, config)
|
||||
? this._configIsExposed(
|
||||
entity.entity_id,
|
||||
config,
|
||||
this._entityCategories![entity.entity_id]
|
||||
)
|
||||
: filterFunc(entity.entity_id);
|
||||
const isDomainExposed = emptyFilter
|
||||
? this._configIsDomainExposed(entity.entity_id)
|
||||
? this._configIsDomainExposed(
|
||||
entity.entity_id,
|
||||
this._entityCategories![entity.entity_id]
|
||||
)
|
||||
: filterFunc(entity.entity_id);
|
||||
if (isExposed) {
|
||||
selected++;
|
||||
@ -287,6 +306,23 @@ class CloudAlexa extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
protected override hassSubscribe(): (
|
||||
| UnsubscribeFunc
|
||||
| Promise<UnsubscribeFunc>
|
||||
)[] {
|
||||
return [
|
||||
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||
const categories = {};
|
||||
|
||||
for (const entry of entries) {
|
||||
categories[entry.entity_id] = entry.entity_category;
|
||||
}
|
||||
|
||||
this._entityCategories = categories;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
private async _fetchData() {
|
||||
const entities = await fetchCloudAlexaEntities(this.hass);
|
||||
entities.sort((a, b) => {
|
||||
@ -305,15 +341,26 @@ class CloudAlexa extends LitElement {
|
||||
fireEvent(this, "hass-more-info", { entityId });
|
||||
}
|
||||
|
||||
private _configIsDomainExposed(entityId: string) {
|
||||
private _configIsDomainExposed(
|
||||
entityId: string,
|
||||
entityCategory: EntityRegistryEntry["entity_category"] | undefined
|
||||
) {
|
||||
const domain = computeDomain(entityId);
|
||||
return this.cloudStatus.prefs.alexa_default_expose
|
||||
? this.cloudStatus.prefs.alexa_default_expose.includes(domain)
|
||||
? !entityCategory &&
|
||||
this.cloudStatus.prefs.alexa_default_expose.includes(domain)
|
||||
: DEFAULT_CONFIG_EXPOSE;
|
||||
}
|
||||
|
||||
private _configIsExposed(entityId: string, config: AlexaEntityConfig) {
|
||||
return config.should_expose ?? this._configIsDomainExposed(entityId);
|
||||
private _configIsExposed(
|
||||
entityId: string,
|
||||
config: AlexaEntityConfig,
|
||||
entityCategory: EntityRegistryEntry["entity_category"] | undefined
|
||||
) {
|
||||
return (
|
||||
config.should_expose ??
|
||||
this._configIsDomainExposed(entityId, entityCategory)
|
||||
);
|
||||
}
|
||||
|
||||
private async _exposeChanged(ev: CustomEvent<ActionDetail>) {
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
mdiCloseBox,
|
||||
mdiCloseBoxMultiple,
|
||||
} from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
@ -35,6 +36,10 @@ import {
|
||||
updateCloudGoogleEntityConfig,
|
||||
updateCloudPref,
|
||||
} from "../../../../data/cloud";
|
||||
import {
|
||||
EntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../../data/entity_registry";
|
||||
import {
|
||||
fetchCloudGoogleEntities,
|
||||
GoogleEntity,
|
||||
@ -42,6 +47,7 @@ import {
|
||||
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
||||
import "../../../../layouts/hass-loading-screen";
|
||||
import "../../../../layouts/hass-subpage";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { showToast } from "../../../../util/toast";
|
||||
@ -49,7 +55,7 @@ import { showToast } from "../../../../util/toast";
|
||||
const DEFAULT_CONFIG_EXPOSE = true;
|
||||
|
||||
@customElement("cloud-google-assistant")
|
||||
class CloudGoogleAssistant extends LitElement {
|
||||
class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public cloudStatus!: CloudStatusLoggedIn;
|
||||
@ -58,9 +64,15 @@ class CloudGoogleAssistant extends LitElement {
|
||||
|
||||
@state() private _entities?: GoogleEntity[];
|
||||
|
||||
@property()
|
||||
@state()
|
||||
private _entityConfigs: CloudPreferences["google_entity_configs"] = {};
|
||||
|
||||
@state()
|
||||
private _entityCategories?: Record<
|
||||
string,
|
||||
EntityRegistryEntry["entity_category"]
|
||||
>;
|
||||
|
||||
private _popstateSyncAttached = false;
|
||||
|
||||
private _popstateReloadStatusAttached = false;
|
||||
@ -77,7 +89,7 @@ class CloudGoogleAssistant extends LitElement {
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._entities === undefined) {
|
||||
if (this._entities === undefined || this._entityCategories === undefined) {
|
||||
return html` <hass-loading-screen></hass-loading-screen> `;
|
||||
}
|
||||
const emptyFilter = isEmptyFilter(this.cloudStatus.google_entities);
|
||||
@ -105,10 +117,17 @@ class CloudGoogleAssistant extends LitElement {
|
||||
should_expose: null,
|
||||
};
|
||||
const isExposed = emptyFilter
|
||||
? this._configIsExposed(entity.entity_id, config)
|
||||
? this._configIsExposed(
|
||||
entity.entity_id,
|
||||
config,
|
||||
this._entityCategories![entity.entity_id]
|
||||
)
|
||||
: filterFunc(entity.entity_id);
|
||||
const isDomainExposed = emptyFilter
|
||||
? this._configIsDomainExposed(entity.entity_id)
|
||||
? this._configIsDomainExposed(
|
||||
entity.entity_id,
|
||||
this._entityCategories![entity.entity_id]
|
||||
)
|
||||
: filterFunc(entity.entity_id);
|
||||
if (isExposed) {
|
||||
selected++;
|
||||
@ -311,15 +330,43 @@ class CloudGoogleAssistant extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _configIsDomainExposed(entityId: string) {
|
||||
protected override hassSubscribe(): (
|
||||
| UnsubscribeFunc
|
||||
| Promise<UnsubscribeFunc>
|
||||
)[] {
|
||||
return [
|
||||
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||
const categories = {};
|
||||
|
||||
for (const entry of entries) {
|
||||
categories[entry.entity_id] = entry.entity_category;
|
||||
}
|
||||
|
||||
this._entityCategories = categories;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
private _configIsDomainExposed(
|
||||
entityId: string,
|
||||
entityCategory: EntityRegistryEntry["entity_category"] | undefined
|
||||
) {
|
||||
const domain = computeDomain(entityId);
|
||||
return this.cloudStatus.prefs.google_default_expose
|
||||
? this.cloudStatus.prefs.google_default_expose.includes(domain)
|
||||
? !entityCategory &&
|
||||
this.cloudStatus.prefs.google_default_expose.includes(domain)
|
||||
: DEFAULT_CONFIG_EXPOSE;
|
||||
}
|
||||
|
||||
private _configIsExposed(entityId: string, config: GoogleEntityConfig) {
|
||||
return config.should_expose ?? this._configIsDomainExposed(entityId);
|
||||
private _configIsExposed(
|
||||
entityId: string,
|
||||
config: GoogleEntityConfig,
|
||||
entityCategory: EntityRegistryEntry["entity_category"] | undefined
|
||||
) {
|
||||
return (
|
||||
config.should_expose ??
|
||||
this._configIsDomainExposed(entityId, entityCategory)
|
||||
);
|
||||
}
|
||||
|
||||
private async _fetchData() {
|
||||
|
@ -2115,25 +2115,30 @@
|
||||
"sync_entities_error": "Failed to sync entities:",
|
||||
"state_reporting_error": "Unable to {enable_disable} report state.",
|
||||
"enable": "enable",
|
||||
"disable": "disable"
|
||||
"disable": "disable",
|
||||
"not_configured_title": "Alexa is not activated",
|
||||
"not_configured_text": "Before you can use Alexa, you need to activate the Home Assistant skill for Alexa in the Alexa app."
|
||||
},
|
||||
"google": {
|
||||
"title": "Google Assistant",
|
||||
"info": "With the Google Assistant integration for Home Assistant Cloud you'll be able to control all your Home Assistant devices via any Google Assistant-enabled device.",
|
||||
"http_use_ssl_warning_title": "Local communication unavailable",
|
||||
"http_use_ssl_warning_text": "Google devices will not be able to talk locally with Home Assistant because you have configured an SSL certificate for your HTTP integration.",
|
||||
"enable_ha_skill": "Activate the Home Assistant Cloud skill for Google Assistant",
|
||||
"config_documentation": "Configuration documentation",
|
||||
"enable_state_reporting": "Enable State Reporting",
|
||||
"info_state_reporting": "If you enable state reporting, Home Assistant will send all state changes of exposed entities to Google. This allows you to always see the latest states in the Google app.",
|
||||
"info_state_reporting": "If you enable state reporting, Home Assistant will send all state changes of exposed entities to Google. This speeds up voice commands and allows you to always see the latest states in the Google app.",
|
||||
"security_devices": "Security Devices",
|
||||
"enter_pin_info": "Please enter a PIN to interact with security devices. Security devices are doors, garage doors and locks. You will be asked to say/enter this PIN when interacting with such devices via Google Assistant.",
|
||||
"devices_pin": "Security Devices PIN",
|
||||
"enter_pin_hint": "Enter a PIN to use security devices",
|
||||
"sync_entities": "Sync Entities to Google",
|
||||
"sync_entities_404_message": "Failed to sync your entities to Google, ask Google 'Hey Google, sync my devices' to sync your entities.",
|
||||
"manage_entities": "Manage Entities",
|
||||
"enter_pin_error": "Unable to store PIN:",
|
||||
"not_configured_title": "Google Assistant is not activated",
|
||||
"not_configured_text": "Before you can use Google Assistant, you need to activate the Home Assistant Cloud skill for Google Assistant in the Google Home app."
|
||||
"not_configured_text": "Before you can use Google Assistant, you need to activate the Home Assistant Cloud skill for Google Assistant in the Google Home app.",
|
||||
"sync_failed_title": "Syncing failed",
|
||||
"sync_failed_text": "Syncing your entities failed, try again or check the logs."
|
||||
},
|
||||
"webhooks": {
|
||||
"title": "Webhooks",
|
||||
|
Loading…
x
Reference in New Issue
Block a user