From 5292119e6e31d35bb8b7e9cb64281237af9e236e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 1 Sep 2020 10:23:59 +0200 Subject: [PATCH] Allow exposing domains in cloud (#6696) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow exposing domains in cloud https://github.com/home-assistant/core/pull/39216 * Update styles * Lint * Apply suggestions from code review Co-authored-by: Joakim Sørensen * Comments * Add translations * Apply suggestions from code review Co-authored-by: Joakim Sørensen Co-authored-by: Joakim Sørensen --- src/data/cloud.ts | 8 +- .../domain-toggler/dialog-domain-toggler.ts | 73 ++-- .../show-dialog-domain-toggler.ts | 2 + src/panels/config/cloud/alexa/cloud-alexa.ts | 352 ++++++++++++------ .../cloud-google-assistant.ts | 340 ++++++++++++----- src/translations/en.json | 19 +- 6 files changed, 551 insertions(+), 243 deletions(-) diff --git a/src/data/cloud.ts b/src/data/cloud.ts index c642841584..e261805f50 100644 --- a/src/data/cloud.ts +++ b/src/data/cloud.ts @@ -9,14 +9,14 @@ interface CloudStatusBase { } export interface GoogleEntityConfig { - should_expose?: boolean; + should_expose?: boolean | null; override_name?: string; aliases?: string[]; disable_2fa?: boolean; } export interface AlexaEntityConfig { - should_expose?: boolean; + should_expose?: boolean | null; } export interface CertificateInformation { @@ -31,9 +31,11 @@ export interface CloudPreferences { remote_enabled: boolean; google_secure_devices_pin: string | undefined; cloudhooks: { [webhookId: string]: CloudWebhook }; + google_default_expose: string[] | null; google_entity_configs: { [entityId: string]: GoogleEntityConfig; }; + alexa_default_expose: string[] | null; alexa_entity_configs: { [entityId: string]: AlexaEntityConfig; }; @@ -106,8 +108,10 @@ export const updateCloudPref = ( prefs: { google_enabled?: CloudPreferences["google_enabled"]; alexa_enabled?: CloudPreferences["alexa_enabled"]; + alexa_default_expose?: CloudPreferences["alexa_default_expose"]; alexa_report_state?: CloudPreferences["alexa_report_state"]; google_report_state?: CloudPreferences["google_report_state"]; + google_default_expose?: CloudPreferences["google_default_expose"]; google_secure_devices_pin?: CloudPreferences["google_secure_devices_pin"]; } ) => diff --git a/src/dialogs/domain-toggler/dialog-domain-toggler.ts b/src/dialogs/domain-toggler/dialog-domain-toggler.ts index 326fb40c01..4b61bac3ab 100644 --- a/src/dialogs/domain-toggler/dialog-domain-toggler.ts +++ b/src/dialogs/domain-toggler/dialog-domain-toggler.ts @@ -4,27 +4,35 @@ import { CSSResultArray, customElement, html, - LitElement, internalProperty, + LitElement, TemplateResult, } from "lit-element"; -import "../../components/dialog/ha-paper-dialog"; +import { fireEvent } from "../../common/dom/fire_event"; +import { createCloseHeading } from "../../components/ha-dialog"; +import "../../components/ha-switch"; +import "../../components/ha-formfield"; import { domainToName } from "../../data/integration"; -import { PolymerChangedEvent } from "../../polymer-types"; import { haStyleDialog } from "../../resources/styles"; import { HomeAssistant } from "../../types"; +import { HassDialog } from "../make-dialog-manager"; import { HaDomainTogglerDialogParams } from "./show-dialog-domain-toggler"; @customElement("dialog-domain-toggler") -class DomainTogglerDialog extends LitElement { +class DomainTogglerDialog extends LitElement implements HassDialog { public hass!: HomeAssistant; @internalProperty() private _params?: HaDomainTogglerDialogParams; - public async showDialog(params: HaDomainTogglerDialogParams): Promise { + public showDialog(params: HaDomainTogglerDialogParams): void { this._params = params; } + public closeDialog() { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + protected render(): TemplateResult { if (!this._params) { return html``; @@ -35,46 +43,47 @@ class DomainTogglerDialog extends LitElement { .sort(); return html` - -

- ${this.hass.localize("ui.dialogs.domain_toggler.title")} -

${domains.map( (domain) => html` -
${domain[0]}
- - ${this.hass.localize("state.default.off")} - - - ${this.hass.localize("state.default.on")} + + + + + + ${this.hass.localize("ui.dialogs.domain_toggler.reset_entities")} ` )}
-
+ `; } - private _openedChanged(ev: PolymerChangedEvent): void { - // Closed dialog by clicking on the overlay - if (!ev.detail.value) { - this._params = undefined; - } - } - - private _handleOff(ev) { - this._params!.toggleDomain(ev.currentTarget.domain, false); + private _handleSwitch(ev) { + this._params!.toggleDomain(ev.currentTarget.domain, ev.target.checked); ev.currentTarget.blur(); } - private _handleOn(ev) { - this._params!.toggleDomain(ev.currentTarget.domain, true); + private _handleReset(ev) { + this._params!.resetDomain(ev.currentTarget.domain); ev.currentTarget.blur(); } @@ -82,8 +91,8 @@ class DomainTogglerDialog extends LitElement { return [ haStyleDialog, css` - ha-paper-dialog { - max-width: 500px; + ha-dialog { + --mdc-dialog-max-width: 500px; } div { display: grid; diff --git a/src/dialogs/domain-toggler/show-dialog-domain-toggler.ts b/src/dialogs/domain-toggler/show-dialog-domain-toggler.ts index 509075c4d5..e5ee6bad5b 100644 --- a/src/dialogs/domain-toggler/show-dialog-domain-toggler.ts +++ b/src/dialogs/domain-toggler/show-dialog-domain-toggler.ts @@ -2,7 +2,9 @@ import { fireEvent } from "../../common/dom/fire_event"; export interface HaDomainTogglerDialogParams { domains: string[]; + exposedDomains: string[] | null; toggleDomain: (domain: string, turnOn: boolean) => void; + resetDomain: (domain: string) => void; } export const loadDomainTogglerDialog = () => diff --git a/src/panels/config/cloud/alexa/cloud-alexa.ts b/src/panels/config/cloud/alexa/cloud-alexa.ts index 3dd625cc6c..5614318886 100644 --- a/src/panels/config/cloud/alexa/cloud-alexa.ts +++ b/src/panels/config/cloud/alexa/cloud-alexa.ts @@ -1,14 +1,22 @@ -import "../../../../components/ha-icon-button"; +import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; +import "@material/mwc-list/mwc-list-item"; +import { + mdiCheckboxMarked, + mdiCheckboxMultipleMarked, + mdiCloseBox, + mdiCloseBoxMultiple, +} from "@mdi/js"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import { computeDomain } from "../../../../common/entity/compute_domain"; @@ -20,31 +28,28 @@ import { } from "../../../../common/entity/entity_filter"; import { compare } from "../../../../common/string/compare"; import "../../../../components/entity/state-info"; +import "../../../../components/ha-button-menu"; import "../../../../components/ha-card"; +import "../../../../components/ha-formfield"; +import "../../../../components/ha-icon-button"; import "../../../../components/ha-switch"; -import type { HaSwitch } from "../../../../components/ha-switch"; import { AlexaEntity, fetchCloudAlexaEntities } from "../../../../data/alexa"; import { AlexaEntityConfig, CloudPreferences, CloudStatusLoggedIn, updateCloudAlexaEntityConfig, + updateCloudPref, } from "../../../../data/cloud"; import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler"; import "../../../../layouts/hass-loading-screen"; import "../../../../layouts/hass-subpage"; +import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; -import "../../../../components/ha-formfield"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; const DEFAULT_CONFIG_EXPOSE = true; const IGNORE_INTERFACES = ["Alexa.EndpointHealth"]; -const configIsExposed = (config: AlexaEntityConfig) => - config.should_expose === undefined - ? DEFAULT_CONFIG_EXPOSE - : config.should_expose; - @customElement("cloud-alexa") class CloudAlexa extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -100,7 +105,10 @@ class CloudAlexa extends LitElement { const stateObj = this.hass.states[entity.entity_id]; const config = this._entityConfigs[entity.entity_id] || {}; const isExposed = emptyFilter - ? configIsExposed(config) + ? this._configIsExposed(entity.entity_id, config) + : filterFunc(entity.entity_id); + const isDomainExposed = emptyFilter + ? this._configIsDomainExposed(entity.entity_id) : filterFunc(entity.entity_id); if (isExposed) { selected++; @@ -117,33 +125,80 @@ class CloudAlexa extends LitElement { target.push(html`
- - ${entity.interfaces - .filter((ifc) => !IGNORE_INTERFACES.includes(ifc)) - .map((ifc) => - ifc.replace("Alexa.", "").replace("Controller", "") - ) - .join(", ")} - - - + - - + ${entity.interfaces + .filter((ifc) => !IGNORE_INTERFACES.includes(ifc)) + .map((ifc) => ifc.replace(/(Alexa.|Controller)/g, "")) + .join(", ")} + + + + + + + ${this.hass!.localize( + "ui.panel.config.cloud.alexa.expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.alexa.dont_expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.alexa.follow_domain" + )} + + + +
`); @@ -157,17 +212,16 @@ class CloudAlexa extends LitElement { - - ${selected}${!this.narrow ? html` selected ` : ""} - ${ emptyFilter ? html` - + >${this.hass!.localize( + "ui.panel.config.cloud.alexa.manage_domains" + )} ` : "" } @@ -183,11 +237,20 @@ class CloudAlexa extends LitElement { ${ exposedCards.length > 0 ? html` -

- ${this.hass!.localize( - "ui.panel.config.cloud.alexa.exposed_entities" - )} -

+
+

+ ${this.hass!.localize( + "ui.panel.config.cloud.alexa.exposed_entities" + )} +

+ ${!this.narrow + ? this.hass!.localize( + "ui.panel.config.cloud.alexa.exposed", + "selected", + selected + ) + : selected} +
${exposedCards}
` : "" @@ -195,11 +258,20 @@ class CloudAlexa extends LitElement { ${ notExposedCards.length > 0 ? html` -

- ${this.hass!.localize( - "ui.panel.config.cloud.alexa.not_exposed_entities" - )} -

+
+

+ ${this.hass!.localize( + "ui.panel.config.cloud.alexa.not_exposed_entities" + )} +

+ ${!this.narrow + ? this.hass!.localize( + "ui.panel.config.cloud.alexa.not_exposed", + "selected", + this._entities.length - selected + ) + : this._entities.length - selected} +
${notExposedCards}
` : "" @@ -239,17 +311,37 @@ class CloudAlexa extends LitElement { fireEvent(this, "hass-more-info", { entityId }); } - private async _exposeChanged(ev: Event) { - const entityId = (ev.currentTarget as any).entityId; - const newExposed = (ev.target as HaSwitch).checked; - await this._updateExposed(entityId, newExposed); + private _configIsDomainExposed(entityId: string) { + const domain = computeDomain(entityId); + return this.cloudStatus.prefs.alexa_default_expose + ? this.cloudStatus.prefs.alexa_default_expose.includes(domain) + : DEFAULT_CONFIG_EXPOSE; } - private async _updateExposed(entityId: string, newExposed: boolean) { - const curExposed = configIsExposed(this._entityConfigs[entityId] || {}); - if (newExposed === curExposed) { - return; + private _configIsExposed(entityId: string, config: AlexaEntityConfig) { + return config.should_expose === null + ? this._configIsDomainExposed(entityId) + : config.should_expose; + } + + private async _exposeChanged(ev: CustomEvent) { + const entityId = (ev.currentTarget as any).entityId; + let newVal: boolean | null = null; + switch (ev.detail.index) { + case 0: + newVal = true; + break; + case 1: + newVal = false; + break; + case 2: + newVal = null; + break; } + await this._updateExposed(entityId, newVal); + } + + private async _updateExposed(entityId: string, newExposed: boolean | null) { await this._updateConfig(entityId, { should_expose: newExposed, }); @@ -274,16 +366,46 @@ class CloudAlexa extends LitElement { domains: this._entities!.map((entity) => computeDomain(entity.entity_id) ).filter((value, idx, self) => self.indexOf(value) === idx), - toggleDomain: (domain, turnOn) => { + exposedDomains: this.cloudStatus.prefs.alexa_default_expose, + toggleDomain: (domain, expose) => { + this._updateDomainExposed(domain, expose); + }, + resetDomain: (domain) => { this._entities!.forEach((entity) => { if (computeDomain(entity.entity_id) === domain) { - this._updateExposed(entity.entity_id, turnOn); + this._updateExposed(entity.entity_id, null); } }); }, }); } + private async _updateDomainExposed(domain: string, expose: boolean) { + const defaultExpose = + this.cloudStatus.prefs.alexa_default_expose || + this._entities!.map((entity) => computeDomain(entity.entity_id)).filter( + (value, idx, self) => self.indexOf(value) === idx + ); + + if ( + (expose && defaultExpose.includes(domain)) || + (!expose && !defaultExpose.includes(domain)) + ) { + return; + } + + if (expose) { + defaultExpose.push(domain); + } else { + defaultExpose.splice(defaultExpose.indexOf(domain), 1); + } + + await updateCloudPref(this.hass!, { + alexa_default_expose: defaultExpose, + }); + fireEvent(this, "ha-refresh-cloud-status"); + } + private _ensureStatusReload() { if (this._popstateReloadStatusAttached) { return; @@ -306,61 +428,75 @@ class CloudAlexa extends LitElement { this._popstateSyncAttached = true; // Cache parent because by the time popstate happens, // this element is detached - // const parent = this.parentElement!; window.addEventListener( "popstate", () => { // We don't have anything yet. - // showToast(parent, { message: "Synchronizing changes to Google." }); - // cloudSyncGoogleAssistant(this.hass); }, { once: true } ); } - static get styles(): CSSResult { - return css` - .banner { - color: var(--primary-text-color); - background-color: var( - --ha-card-background, - var(--card-background-color, white) - ); - padding: 16px 8px; - text-align: center; - } - h1 { - color: var(--primary-text-color); - font-size: 24px; - letter-spacing: -0.012em; - margin-bottom: 0; - padding: 0 8px; - } - .content { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - grid-gap: 8px 8px; - padding: 8px; - } - ha-switch { - clear: both; - } - .card-content { - padding-bottom: 12px; - } - state-info { - cursor: pointer; - } - ha-switch { - padding: 8px 0; - } - - @media all and (max-width: 450px) { - ha-card { - max-width: 100%; + static get styles(): CSSResult[] { + return [ + haStyle, + css` + mwc-list-item > [slot="meta"] { + margin-left: 4px; } - } - `; + .banner { + color: var(--primary-text-color); + background-color: var( + --ha-card-background, + var(--card-background-color, white) + ); + padding: 16px 8px; + text-align: center; + } + .content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + grid-gap: 8px 8px; + padding: 8px; + } + .card-content { + padding-bottom: 12px; + } + state-info { + cursor: pointer; + } + ha-switch { + padding: 8px 0; + } + .top-line { + display: flex; + align-items: center; + justify-content: space-between; + } + .header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 16px; + border-bottom: 1px solid var(--divider-color); + background: var(--app-header-background-color); + } + .header.second { + border-top: 1px solid var(--divider-color); + } + .exposed { + color: var(--success-color); + } + .not-exposed { + color: var(--error-color); + } + @media all and (max-width: 450px) { + ha-card { + max-width: 100%; + } + } + `, + ]; } } diff --git a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts index bdf23ec5f5..c29e22eaa0 100644 --- a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts +++ b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts @@ -1,14 +1,22 @@ -import "../../../../components/ha-icon-button"; +import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; +import "@material/mwc-list/mwc-list-item"; +import { + mdiCheckboxMarked, + mdiCheckboxMultipleMarked, + mdiCloseBox, + mdiCloseBoxMultiple, +} from "@mdi/js"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import { computeDomain } from "../../../../common/entity/compute_domain"; @@ -19,8 +27,12 @@ import { isEmptyFilter, } from "../../../../common/entity/entity_filter"; import { compare } from "../../../../common/string/compare"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; import "../../../../components/entity/state-info"; +import "../../../../components/ha-button-menu"; import "../../../../components/ha-card"; +import "../../../../components/ha-formfield"; +import "../../../../components/ha-icon-button"; import "../../../../components/ha-switch"; import type { HaSwitch } from "../../../../components/ha-switch"; import { @@ -29,6 +41,7 @@ import { cloudSyncGoogleAssistant, GoogleEntityConfig, updateCloudGoogleEntityConfig, + updateCloudPref, } from "../../../../data/cloud"; import { fetchCloudGoogleEntities, @@ -37,18 +50,12 @@ import { import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler"; import "../../../../layouts/hass-loading-screen"; import "../../../../layouts/hass-subpage"; +import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import { showToast } from "../../../../util/toast"; -import "../../../../components/ha-formfield"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; const DEFAULT_CONFIG_EXPOSE = true; -const configIsExposed = (config: GoogleEntityConfig) => - config.should_expose === undefined - ? DEFAULT_CONFIG_EXPOSE - : config.should_expose; - @customElement("cloud-google-assistant") class CloudGoogleAssistant extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -104,7 +111,10 @@ class CloudGoogleAssistant extends LitElement { const stateObj = this.hass.states[entity.entity_id]; const config = this._entityConfigs[entity.entity_id] || {}; const isExposed = emptyFilter - ? configIsExposed(config) + ? this._configIsExposed(entity.entity_id, config) + : filterFunc(entity.entity_id); + const isDomainExposed = emptyFilter + ? this._configIsDomainExposed(entity.entity_id) : filterFunc(entity.entity_id); if (isExposed) { selected++; @@ -121,31 +131,78 @@ class CloudGoogleAssistant extends LitElement { target.push(html`
- - ${entity.traits - .map((trait) => trait.substr(trait.lastIndexOf(".") + 1)) - .join(", ")} - -
- + - trait.substr(trait.lastIndexOf(".") + 1)) + .join(", ")} + + + - - + + + + ${this.hass!.localize( + "ui.panel.config.cloud.google.expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.google.dont_expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.google.follow_domain" + )} + + +
${entity.might_2fa ? html` @@ -178,17 +235,16 @@ class CloudGoogleAssistant extends LitElement { - - ${selected}${!this.narrow ? html` selected ` : ""} - ${ emptyFilter ? html` - + >${this.hass!.localize( + "ui.panel.config.cloud.google.manage_domains" + )} ` : "" } @@ -204,11 +260,20 @@ class CloudGoogleAssistant extends LitElement { ${ exposedCards.length > 0 ? html` -

- ${this.hass!.localize( - "ui.panel.config.cloud.google.exposed_entities" - )} -

+
+

+ ${this.hass!.localize( + "ui.panel.config.cloud.google.exposed_entities" + )} +

+ ${!this.narrow + ? this.hass!.localize( + "ui.panel.config.cloud.alexa.exposed", + "selected", + selected + ) + : selected} +
${exposedCards}
` : "" @@ -216,11 +281,20 @@ class CloudGoogleAssistant extends LitElement { ${ notExposedCards.length > 0 ? html` -

- ${this.hass!.localize( - "ui.panel.config.cloud.google.not_exposed_entities" - )} -

+
+

+ ${this.hass!.localize( + "ui.panel.config.cloud.google.not_exposed_entities" + )} +

+ ${!this.narrow + ? this.hass!.localize( + "ui.panel.config.cloud.alexa.not_exposed", + "selected", + this._entities.length - selected + ) + : this._entities.length - selected} +
${notExposedCards}
` : "" @@ -242,6 +316,19 @@ class CloudGoogleAssistant extends LitElement { } } + private _configIsDomainExposed(entityId: string) { + const domain = computeDomain(entityId); + return this.cloudStatus.prefs.google_default_expose + ? this.cloudStatus.prefs.google_default_expose.includes(domain) + : DEFAULT_CONFIG_EXPOSE; + } + + private _configIsExposed(entityId: string, config: GoogleEntityConfig) { + return config.should_expose === null + ? this._configIsDomainExposed(entityId) + : config.should_expose; + } + private async _fetchData() { const entities = await fetchCloudGoogleEntities(this.hass); entities.sort((a, b) => { @@ -260,17 +347,24 @@ class CloudGoogleAssistant extends LitElement { fireEvent(this, "hass-more-info", { entityId }); } - private async _exposeChanged(ev: Event) { + private async _exposeChanged(ev: CustomEvent) { const entityId = (ev.currentTarget as any).entityId; - const newExposed = (ev.target as HaSwitch).checked; - await this._updateExposed(entityId, newExposed); + let newVal: boolean | null = null; + switch (ev.detail.index) { + case 0: + newVal = true; + break; + case 1: + newVal = false; + break; + case 2: + newVal = null; + break; + } + await this._updateExposed(entityId, newVal); } - private async _updateExposed(entityId: string, newExposed: boolean) { - const curExposed = configIsExposed(this._entityConfigs[entityId] || {}); - if (newExposed === curExposed) { - return; - } + private async _updateExposed(entityId: string, newExposed: boolean | null) { await this._updateConfig(entityId, { should_expose: newExposed, }); @@ -309,16 +403,46 @@ class CloudGoogleAssistant extends LitElement { domains: this._entities!.map((entity) => computeDomain(entity.entity_id) ).filter((value, idx, self) => self.indexOf(value) === idx), - toggleDomain: (domain, turnOn) => { + exposedDomains: this.cloudStatus.prefs.google_default_expose, + toggleDomain: (domain, expose) => { + this._updateDomainExposed(domain, expose); + }, + resetDomain: (domain) => { this._entities!.forEach((entity) => { if (computeDomain(entity.entity_id) === domain) { - this._updateExposed(entity.entity_id, turnOn); + this._updateExposed(entity.entity_id, null); } }); }, }); } + private async _updateDomainExposed(domain: string, expose: boolean) { + const defaultExpose = + this.cloudStatus.prefs.google_default_expose || + this._entities!.map((entity) => computeDomain(entity.entity_id)).filter( + (value, idx, self) => self.indexOf(value) === idx + ); + + if ( + (expose && defaultExpose.includes(domain)) || + (!expose && !defaultExpose.includes(domain)) + ) { + return; + } + + if (expose) { + defaultExpose.push(domain); + } else { + defaultExpose.splice(defaultExpose.indexOf(domain), 1); + } + + await updateCloudPref(this.hass!, { + google_default_expose: defaultExpose, + }); + fireEvent(this, "ha-refresh-cloud-status"); + } + private _ensureStatusReload() { if (this._popstateReloadStatusAttached) { return; @@ -356,46 +480,66 @@ class CloudGoogleAssistant extends LitElement { ); } - static get styles(): CSSResult { - return css` - .banner { - color: var(--primary-text-color); - background-color: var( - --ha-card-background, - var(--card-background-color, white) - ); - padding: 16px 8px; - text-align: center; - } - h1 { - color: var(--primary-text-color); - font-size: 24px; - letter-spacing: -0.012em; - margin-bottom: 0; - padding: 0 8px; - } - .content { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - grid-gap: 8px 8px; - padding: 8px; - } - .card-content { - padding-bottom: 12px; - } - state-info { - cursor: pointer; - } - ha-switch { - padding: 8px 0; - } - - @media all and (max-width: 450px) { - ha-card { - max-width: 100%; + static get styles(): CSSResult[] { + return [ + haStyle, + css` + mwc-list-item > [slot="meta"] { + margin-left: 4px; } - } - `; + .banner { + color: var(--primary-text-color); + background-color: var( + --ha-card-background, + var(--card-background-color, white) + ); + padding: 16px 8px; + text-align: center; + } + .content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + grid-gap: 8px 8px; + padding: 8px; + } + .card-content { + padding-bottom: 12px; + } + state-info { + cursor: pointer; + } + ha-switch { + padding: 8px 0; + } + .top-line { + display: flex; + align-items: center; + justify-content: space-between; + } + .header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 16px; + border-bottom: 1px solid var(--divider-color); + background: var(--app-header-background-color); + } + .header.second { + border-top: 1px solid var(--divider-color); + } + .exposed { + color: var(--success-color); + } + .not-exposed { + color: var(--error-color); + } + @media all and (max-width: 450px) { + ha-card { + max-width: 100%; + } + } + `, + ]; } } diff --git a/src/translations/en.json b/src/translations/en.json index d922b5f919..bb6ec74e35 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -526,7 +526,8 @@ } }, "domain_toggler": { - "title": "Toggle Domains" + "title": "Toggle Domains", + "reset_entities": "Reset Entities" }, "mqtt_device_debug_info": { "title": "{device} debug info", @@ -1383,7 +1384,13 @@ "title": "Alexa", "banner": "Editing which entities are exposed via this UI is disabled because you have configured entity filters in configuration.yaml.", "exposed_entities": "Exposed entities", - "not_exposed_entities": "Not Exposed entities", + "not_exposed_entities": "Not exposed entities", + "manage_domains": "Manage domains", + "expose_entity": "Expose entity", + "dont_expose_entity": "Don't expose entity", + "follow_domain": "Follow domain", + "exposed": "{selected} exposed", + "not_exposed": "{selected} not exposed", "expose": "Expose to Alexa" }, "dialog_certificate": { @@ -1399,7 +1406,13 @@ "disable_2FA": "Disable two factor authentication", "banner": "Editing which entities are exposed via this UI is disabled because you have configured entity filters in configuration.yaml.", "exposed_entities": "Exposed entities", - "not_exposed_entities": "Not Exposed entities", + "not_exposed_entities": "Not exposed entities", + "manage_domains": "Manage domains", + "expose_entity": "Expose entity", + "dont_expose_entity": "Don't expose entity", + "follow_domain": "Follow domain", + "exposed": "{selected} exposed", + "not_exposed": "{selected} not exposed", "sync_to_google": "Synchronizing changes to Google." }, "dialog_cloudhook": {