mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Allow exposing domains in cloud (#6696)
* 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 <joasoe@gmail.com> * Comments * Add translations * Apply suggestions from code review Co-authored-by: Joakim Sørensen <joasoe@gmail.com> Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
This commit is contained in:
parent
994a397231
commit
5292119e6e
@ -9,14 +9,14 @@ interface CloudStatusBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GoogleEntityConfig {
|
export interface GoogleEntityConfig {
|
||||||
should_expose?: boolean;
|
should_expose?: boolean | null;
|
||||||
override_name?: string;
|
override_name?: string;
|
||||||
aliases?: string[];
|
aliases?: string[];
|
||||||
disable_2fa?: boolean;
|
disable_2fa?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlexaEntityConfig {
|
export interface AlexaEntityConfig {
|
||||||
should_expose?: boolean;
|
should_expose?: boolean | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CertificateInformation {
|
export interface CertificateInformation {
|
||||||
@ -31,9 +31,11 @@ export interface CloudPreferences {
|
|||||||
remote_enabled: boolean;
|
remote_enabled: boolean;
|
||||||
google_secure_devices_pin: string | undefined;
|
google_secure_devices_pin: string | undefined;
|
||||||
cloudhooks: { [webhookId: string]: CloudWebhook };
|
cloudhooks: { [webhookId: string]: CloudWebhook };
|
||||||
|
google_default_expose: string[] | null;
|
||||||
google_entity_configs: {
|
google_entity_configs: {
|
||||||
[entityId: string]: GoogleEntityConfig;
|
[entityId: string]: GoogleEntityConfig;
|
||||||
};
|
};
|
||||||
|
alexa_default_expose: string[] | null;
|
||||||
alexa_entity_configs: {
|
alexa_entity_configs: {
|
||||||
[entityId: string]: AlexaEntityConfig;
|
[entityId: string]: AlexaEntityConfig;
|
||||||
};
|
};
|
||||||
@ -106,8 +108,10 @@ export const updateCloudPref = (
|
|||||||
prefs: {
|
prefs: {
|
||||||
google_enabled?: CloudPreferences["google_enabled"];
|
google_enabled?: CloudPreferences["google_enabled"];
|
||||||
alexa_enabled?: CloudPreferences["alexa_enabled"];
|
alexa_enabled?: CloudPreferences["alexa_enabled"];
|
||||||
|
alexa_default_expose?: CloudPreferences["alexa_default_expose"];
|
||||||
alexa_report_state?: CloudPreferences["alexa_report_state"];
|
alexa_report_state?: CloudPreferences["alexa_report_state"];
|
||||||
google_report_state?: CloudPreferences["google_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"];
|
google_secure_devices_pin?: CloudPreferences["google_secure_devices_pin"];
|
||||||
}
|
}
|
||||||
) =>
|
) =>
|
||||||
|
@ -4,27 +4,35 @@ import {
|
|||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
LitElement,
|
|
||||||
internalProperty,
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} 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 { domainToName } from "../../data/integration";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { HassDialog } from "../make-dialog-manager";
|
||||||
import { HaDomainTogglerDialogParams } from "./show-dialog-domain-toggler";
|
import { HaDomainTogglerDialogParams } from "./show-dialog-domain-toggler";
|
||||||
|
|
||||||
@customElement("dialog-domain-toggler")
|
@customElement("dialog-domain-toggler")
|
||||||
class DomainTogglerDialog extends LitElement {
|
class DomainTogglerDialog extends LitElement implements HassDialog {
|
||||||
public hass!: HomeAssistant;
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
@internalProperty() private _params?: HaDomainTogglerDialogParams;
|
@internalProperty() private _params?: HaDomainTogglerDialogParams;
|
||||||
|
|
||||||
public async showDialog(params: HaDomainTogglerDialogParams): Promise<void> {
|
public showDialog(params: HaDomainTogglerDialogParams): void {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public closeDialog() {
|
||||||
|
this._params = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return html``;
|
return html``;
|
||||||
@ -35,46 +43,47 @@ class DomainTogglerDialog extends LitElement {
|
|||||||
.sort();
|
.sort();
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-paper-dialog
|
<ha-dialog
|
||||||
with-backdrop
|
open
|
||||||
opened
|
@closed=${this.closeDialog}
|
||||||
@opened-changed=${this._openedChanged}
|
scrimClickAction
|
||||||
|
escapeKeyAction
|
||||||
|
hideActions
|
||||||
|
.heading=${createCloseHeading(
|
||||||
|
this.hass,
|
||||||
|
this.hass.localize("ui.dialogs.domain_toggler.title")
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<h2>
|
|
||||||
${this.hass.localize("ui.dialogs.domain_toggler.title")}
|
|
||||||
</h2>
|
|
||||||
<div>
|
<div>
|
||||||
${domains.map(
|
${domains.map(
|
||||||
(domain) =>
|
(domain) =>
|
||||||
html`
|
html`
|
||||||
<div>${domain[0]}</div>
|
<ha-formfield .label=${domain[0]}>
|
||||||
<mwc-button .domain=${domain[1]} @click=${this._handleOff}>
|
<ha-switch
|
||||||
${this.hass.localize("state.default.off")}
|
.domain=${domain[1]}
|
||||||
</mwc-button>
|
.checked=${!this._params!.exposedDomains ||
|
||||||
<mwc-button .domain=${domain[1]} @click=${this._handleOn}>
|
this._params!.exposedDomains.includes(domain[1])}
|
||||||
${this.hass.localize("state.default.on")}
|
@change=${this._handleSwitch}
|
||||||
|
>
|
||||||
|
</ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
<mwc-button .domain=${domain[1]} @click=${this._handleReset}>
|
||||||
|
${this.hass.localize("ui.dialogs.domain_toggler.reset_entities")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-paper-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
private _handleSwitch(ev) {
|
||||||
// Closed dialog by clicking on the overlay
|
this._params!.toggleDomain(ev.currentTarget.domain, ev.target.checked);
|
||||||
if (!ev.detail.value) {
|
|
||||||
this._params = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleOff(ev) {
|
|
||||||
this._params!.toggleDomain(ev.currentTarget.domain, false);
|
|
||||||
ev.currentTarget.blur();
|
ev.currentTarget.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleOn(ev) {
|
private _handleReset(ev) {
|
||||||
this._params!.toggleDomain(ev.currentTarget.domain, true);
|
this._params!.resetDomain(ev.currentTarget.domain);
|
||||||
ev.currentTarget.blur();
|
ev.currentTarget.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +91,8 @@ class DomainTogglerDialog extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-paper-dialog {
|
ha-dialog {
|
||||||
max-width: 500px;
|
--mdc-dialog-max-width: 500px;
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -2,7 +2,9 @@ import { fireEvent } from "../../common/dom/fire_event";
|
|||||||
|
|
||||||
export interface HaDomainTogglerDialogParams {
|
export interface HaDomainTogglerDialogParams {
|
||||||
domains: string[];
|
domains: string[];
|
||||||
|
exposedDomains: string[] | null;
|
||||||
toggleDomain: (domain: string, turnOn: boolean) => void;
|
toggleDomain: (domain: string, turnOn: boolean) => void;
|
||||||
|
resetDomain: (domain: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadDomainTogglerDialog = () =>
|
export const loadDomainTogglerDialog = () =>
|
||||||
|
@ -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 {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
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 { computeDomain } from "../../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
@ -20,31 +28,28 @@ import {
|
|||||||
} from "../../../../common/entity/entity_filter";
|
} from "../../../../common/entity/entity_filter";
|
||||||
import { compare } from "../../../../common/string/compare";
|
import { compare } from "../../../../common/string/compare";
|
||||||
import "../../../../components/entity/state-info";
|
import "../../../../components/entity/state-info";
|
||||||
|
import "../../../../components/ha-button-menu";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
|
import "../../../../components/ha-formfield";
|
||||||
|
import "../../../../components/ha-icon-button";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../components/ha-switch";
|
||||||
import type { HaSwitch } from "../../../../components/ha-switch";
|
|
||||||
import { AlexaEntity, fetchCloudAlexaEntities } from "../../../../data/alexa";
|
import { AlexaEntity, fetchCloudAlexaEntities } from "../../../../data/alexa";
|
||||||
import {
|
import {
|
||||||
AlexaEntityConfig,
|
AlexaEntityConfig,
|
||||||
CloudPreferences,
|
CloudPreferences,
|
||||||
CloudStatusLoggedIn,
|
CloudStatusLoggedIn,
|
||||||
updateCloudAlexaEntityConfig,
|
updateCloudAlexaEntityConfig,
|
||||||
|
updateCloudPref,
|
||||||
} from "../../../../data/cloud";
|
} from "../../../../data/cloud";
|
||||||
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
||||||
import "../../../../layouts/hass-loading-screen";
|
import "../../../../layouts/hass-loading-screen";
|
||||||
import "../../../../layouts/hass-subpage";
|
import "../../../../layouts/hass-subpage";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import "../../../../components/ha-formfield";
|
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
|
||||||
|
|
||||||
const DEFAULT_CONFIG_EXPOSE = true;
|
const DEFAULT_CONFIG_EXPOSE = true;
|
||||||
const IGNORE_INTERFACES = ["Alexa.EndpointHealth"];
|
const IGNORE_INTERFACES = ["Alexa.EndpointHealth"];
|
||||||
|
|
||||||
const configIsExposed = (config: AlexaEntityConfig) =>
|
|
||||||
config.should_expose === undefined
|
|
||||||
? DEFAULT_CONFIG_EXPOSE
|
|
||||||
: config.should_expose;
|
|
||||||
|
|
||||||
@customElement("cloud-alexa")
|
@customElement("cloud-alexa")
|
||||||
class CloudAlexa extends LitElement {
|
class CloudAlexa extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -100,7 +105,10 @@ class CloudAlexa extends LitElement {
|
|||||||
const stateObj = this.hass.states[entity.entity_id];
|
const stateObj = this.hass.states[entity.entity_id];
|
||||||
const config = this._entityConfigs[entity.entity_id] || {};
|
const config = this._entityConfigs[entity.entity_id] || {};
|
||||||
const isExposed = emptyFilter
|
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);
|
: filterFunc(entity.entity_id);
|
||||||
if (isExposed) {
|
if (isExposed) {
|
||||||
selected++;
|
selected++;
|
||||||
@ -117,33 +125,80 @@ class CloudAlexa extends LitElement {
|
|||||||
target.push(html`
|
target.push(html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<state-info
|
<div class="top-line">
|
||||||
.hass=${this.hass}
|
<state-info
|
||||||
.stateObj=${stateObj}
|
.hass=${this.hass}
|
||||||
secondary-line
|
.stateObj=${stateObj}
|
||||||
@click=${this._showMoreInfo}
|
secondary-line
|
||||||
>
|
@click=${this._showMoreInfo}
|
||||||
${entity.interfaces
|
|
||||||
.filter((ifc) => !IGNORE_INTERFACES.includes(ifc))
|
|
||||||
.map((ifc) =>
|
|
||||||
ifc.replace("Alexa.", "").replace("Controller", "")
|
|
||||||
)
|
|
||||||
.join(", ")}
|
|
||||||
</state-info>
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this.hass!.localize(
|
|
||||||
"ui.panel.config.cloud.alexa.expose"
|
|
||||||
)}
|
|
||||||
.dir=${computeRTLDirection(this.hass!)}
|
|
||||||
>
|
|
||||||
<ha-switch
|
|
||||||
.entityId=${entity.entity_id}
|
|
||||||
.disabled=${!emptyFilter}
|
|
||||||
.checked=${isExposed}
|
|
||||||
@change=${this._exposeChanged}
|
|
||||||
>
|
>
|
||||||
</ha-switch>
|
${entity.interfaces
|
||||||
</ha-formfield>
|
.filter((ifc) => !IGNORE_INTERFACES.includes(ifc))
|
||||||
|
.map((ifc) => ifc.replace(/(Alexa.|Controller)/g, ""))
|
||||||
|
.join(", ")}
|
||||||
|
</state-info>
|
||||||
|
<ha-button-menu
|
||||||
|
corner="BOTTOM_START"
|
||||||
|
.entityId=${stateObj.entity_id}
|
||||||
|
@action=${this._exposeChanged}
|
||||||
|
>
|
||||||
|
<mwc-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
class=${classMap({
|
||||||
|
exposed: isExposed!,
|
||||||
|
"not-exposed": !isExposed,
|
||||||
|
})}
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.expose"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${config.should_expose !== null
|
||||||
|
? isExposed
|
||||||
|
? mdiCheckboxMarked
|
||||||
|
: mdiCloseBox
|
||||||
|
: isDomainExposed
|
||||||
|
? mdiCheckboxMultipleMarked
|
||||||
|
: mdiCloseBoxMultiple}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-list-item hasMeta>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.expose_entity"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="exposed"
|
||||||
|
slot="meta"
|
||||||
|
.path=${mdiCheckboxMarked}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
<mwc-list-item hasMeta>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.dont_expose_entity"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="not-exposed"
|
||||||
|
slot="meta"
|
||||||
|
.path=${mdiCloseBox}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
<mwc-list-item hasMeta>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.follow_domain"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class=${classMap({
|
||||||
|
exposed: isDomainExposed,
|
||||||
|
"not-exposed": !isDomainExposed,
|
||||||
|
})}
|
||||||
|
slot="meta"
|
||||||
|
.path=${isDomainExposed
|
||||||
|
? mdiCheckboxMultipleMarked
|
||||||
|
: mdiCloseBoxMultiple}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`);
|
`);
|
||||||
@ -157,17 +212,16 @@ class CloudAlexa extends LitElement {
|
|||||||
<hass-subpage header="${this.hass!.localize(
|
<hass-subpage header="${this.hass!.localize(
|
||||||
"ui.panel.config.cloud.alexa.title"
|
"ui.panel.config.cloud.alexa.title"
|
||||||
)}">
|
)}">
|
||||||
<span slot="toolbar-icon">
|
|
||||||
${selected}${!this.narrow ? html` selected ` : ""}
|
|
||||||
</span>
|
|
||||||
${
|
${
|
||||||
emptyFilter
|
emptyFilter
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<mwc-button
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
icon="hass:tune"
|
|
||||||
@click=${this._openDomainToggler}
|
@click=${this._openDomainToggler}
|
||||||
></ha-icon-button>
|
>${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.manage_domains"
|
||||||
|
)}</mwc-button
|
||||||
|
>
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
@ -183,11 +237,20 @@ class CloudAlexa extends LitElement {
|
|||||||
${
|
${
|
||||||
exposedCards.length > 0
|
exposedCards.length > 0
|
||||||
? html`
|
? html`
|
||||||
<h1>
|
<div class="header">
|
||||||
${this.hass!.localize(
|
<h3>
|
||||||
"ui.panel.config.cloud.alexa.exposed_entities"
|
${this.hass!.localize(
|
||||||
)}
|
"ui.panel.config.cloud.alexa.exposed_entities"
|
||||||
</h1>
|
)}
|
||||||
|
</h3>
|
||||||
|
${!this.narrow
|
||||||
|
? this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.exposed",
|
||||||
|
"selected",
|
||||||
|
selected
|
||||||
|
)
|
||||||
|
: selected}
|
||||||
|
</div>
|
||||||
<div class="content">${exposedCards}</div>
|
<div class="content">${exposedCards}</div>
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
@ -195,11 +258,20 @@ class CloudAlexa extends LitElement {
|
|||||||
${
|
${
|
||||||
notExposedCards.length > 0
|
notExposedCards.length > 0
|
||||||
? html`
|
? html`
|
||||||
<h1>
|
<div class="header second">
|
||||||
${this.hass!.localize(
|
<h3>
|
||||||
"ui.panel.config.cloud.alexa.not_exposed_entities"
|
${this.hass!.localize(
|
||||||
)}
|
"ui.panel.config.cloud.alexa.not_exposed_entities"
|
||||||
</h1>
|
)}
|
||||||
|
</h3>
|
||||||
|
${!this.narrow
|
||||||
|
? this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.not_exposed",
|
||||||
|
"selected",
|
||||||
|
this._entities.length - selected
|
||||||
|
)
|
||||||
|
: this._entities.length - selected}
|
||||||
|
</div>
|
||||||
<div class="content">${notExposedCards}</div>
|
<div class="content">${notExposedCards}</div>
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
@ -239,17 +311,37 @@ class CloudAlexa extends LitElement {
|
|||||||
fireEvent(this, "hass-more-info", { entityId });
|
fireEvent(this, "hass-more-info", { entityId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _exposeChanged(ev: Event) {
|
private _configIsDomainExposed(entityId: string) {
|
||||||
const entityId = (ev.currentTarget as any).entityId;
|
const domain = computeDomain(entityId);
|
||||||
const newExposed = (ev.target as HaSwitch).checked;
|
return this.cloudStatus.prefs.alexa_default_expose
|
||||||
await this._updateExposed(entityId, newExposed);
|
? this.cloudStatus.prefs.alexa_default_expose.includes(domain)
|
||||||
|
: DEFAULT_CONFIG_EXPOSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateExposed(entityId: string, newExposed: boolean) {
|
private _configIsExposed(entityId: string, config: AlexaEntityConfig) {
|
||||||
const curExposed = configIsExposed(this._entityConfigs[entityId] || {});
|
return config.should_expose === null
|
||||||
if (newExposed === curExposed) {
|
? this._configIsDomainExposed(entityId)
|
||||||
return;
|
: config.should_expose;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _exposeChanged(ev: CustomEvent<ActionDetail>) {
|
||||||
|
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, {
|
await this._updateConfig(entityId, {
|
||||||
should_expose: newExposed,
|
should_expose: newExposed,
|
||||||
});
|
});
|
||||||
@ -274,16 +366,46 @@ class CloudAlexa extends LitElement {
|
|||||||
domains: this._entities!.map((entity) =>
|
domains: this._entities!.map((entity) =>
|
||||||
computeDomain(entity.entity_id)
|
computeDomain(entity.entity_id)
|
||||||
).filter((value, idx, self) => self.indexOf(value) === idx),
|
).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) => {
|
this._entities!.forEach((entity) => {
|
||||||
if (computeDomain(entity.entity_id) === domain) {
|
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() {
|
private _ensureStatusReload() {
|
||||||
if (this._popstateReloadStatusAttached) {
|
if (this._popstateReloadStatusAttached) {
|
||||||
return;
|
return;
|
||||||
@ -306,61 +428,75 @@ class CloudAlexa extends LitElement {
|
|||||||
this._popstateSyncAttached = true;
|
this._popstateSyncAttached = true;
|
||||||
// Cache parent because by the time popstate happens,
|
// Cache parent because by the time popstate happens,
|
||||||
// this element is detached
|
// this element is detached
|
||||||
// const parent = this.parentElement!;
|
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"popstate",
|
"popstate",
|
||||||
() => {
|
() => {
|
||||||
// We don't have anything yet.
|
// We don't have anything yet.
|
||||||
// showToast(parent, { message: "Synchronizing changes to Google." });
|
|
||||||
// cloudSyncGoogleAssistant(this.hass);
|
|
||||||
},
|
},
|
||||||
{ once: true }
|
{ once: true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult[] {
|
||||||
return css`
|
return [
|
||||||
.banner {
|
haStyle,
|
||||||
color: var(--primary-text-color);
|
css`
|
||||||
background-color: var(
|
mwc-list-item > [slot="meta"] {
|
||||||
--ha-card-background,
|
margin-left: 4px;
|
||||||
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%;
|
|
||||||
}
|
}
|
||||||
}
|
.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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
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 { computeDomain } from "../../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
@ -19,8 +27,12 @@ import {
|
|||||||
isEmptyFilter,
|
isEmptyFilter,
|
||||||
} from "../../../../common/entity/entity_filter";
|
} from "../../../../common/entity/entity_filter";
|
||||||
import { compare } from "../../../../common/string/compare";
|
import { compare } from "../../../../common/string/compare";
|
||||||
|
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||||
import "../../../../components/entity/state-info";
|
import "../../../../components/entity/state-info";
|
||||||
|
import "../../../../components/ha-button-menu";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
|
import "../../../../components/ha-formfield";
|
||||||
|
import "../../../../components/ha-icon-button";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../components/ha-switch";
|
||||||
import type { HaSwitch } from "../../../../components/ha-switch";
|
import type { HaSwitch } from "../../../../components/ha-switch";
|
||||||
import {
|
import {
|
||||||
@ -29,6 +41,7 @@ import {
|
|||||||
cloudSyncGoogleAssistant,
|
cloudSyncGoogleAssistant,
|
||||||
GoogleEntityConfig,
|
GoogleEntityConfig,
|
||||||
updateCloudGoogleEntityConfig,
|
updateCloudGoogleEntityConfig,
|
||||||
|
updateCloudPref,
|
||||||
} from "../../../../data/cloud";
|
} from "../../../../data/cloud";
|
||||||
import {
|
import {
|
||||||
fetchCloudGoogleEntities,
|
fetchCloudGoogleEntities,
|
||||||
@ -37,18 +50,12 @@ import {
|
|||||||
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
||||||
import "../../../../layouts/hass-loading-screen";
|
import "../../../../layouts/hass-loading-screen";
|
||||||
import "../../../../layouts/hass-subpage";
|
import "../../../../layouts/hass-subpage";
|
||||||
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { showToast } from "../../../../util/toast";
|
import { showToast } from "../../../../util/toast";
|
||||||
import "../../../../components/ha-formfield";
|
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
|
||||||
|
|
||||||
const DEFAULT_CONFIG_EXPOSE = true;
|
const DEFAULT_CONFIG_EXPOSE = true;
|
||||||
|
|
||||||
const configIsExposed = (config: GoogleEntityConfig) =>
|
|
||||||
config.should_expose === undefined
|
|
||||||
? DEFAULT_CONFIG_EXPOSE
|
|
||||||
: config.should_expose;
|
|
||||||
|
|
||||||
@customElement("cloud-google-assistant")
|
@customElement("cloud-google-assistant")
|
||||||
class CloudGoogleAssistant extends LitElement {
|
class CloudGoogleAssistant extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -104,7 +111,10 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
const stateObj = this.hass.states[entity.entity_id];
|
const stateObj = this.hass.states[entity.entity_id];
|
||||||
const config = this._entityConfigs[entity.entity_id] || {};
|
const config = this._entityConfigs[entity.entity_id] || {};
|
||||||
const isExposed = emptyFilter
|
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);
|
: filterFunc(entity.entity_id);
|
||||||
if (isExposed) {
|
if (isExposed) {
|
||||||
selected++;
|
selected++;
|
||||||
@ -121,31 +131,78 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
target.push(html`
|
target.push(html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<state-info
|
<div class="top-line">
|
||||||
.hass=${this.hass}
|
<state-info
|
||||||
.stateObj=${stateObj}
|
.hass=${this.hass}
|
||||||
secondary-line
|
.stateObj=${stateObj}
|
||||||
@click=${this._showMoreInfo}
|
secondary-line
|
||||||
>
|
@click=${this._showMoreInfo}
|
||||||
${entity.traits
|
|
||||||
.map((trait) => trait.substr(trait.lastIndexOf(".") + 1))
|
|
||||||
.join(", ")}
|
|
||||||
</state-info>
|
|
||||||
<div>
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this.hass!.localize(
|
|
||||||
"ui.panel.config.cloud.google.expose"
|
|
||||||
)}
|
|
||||||
.dir=${dir}
|
|
||||||
>
|
>
|
||||||
<ha-switch
|
${entity.traits
|
||||||
.entityId=${entity.entity_id}
|
.map((trait) => trait.substr(trait.lastIndexOf(".") + 1))
|
||||||
.disabled=${!emptyFilter}
|
.join(", ")}
|
||||||
.checked=${isExposed}
|
</state-info>
|
||||||
@change=${this._exposeChanged}
|
<ha-button-menu
|
||||||
|
corner="BOTTOM_START"
|
||||||
|
.entityId=${stateObj.entity_id}
|
||||||
|
@action=${this._exposeChanged}
|
||||||
|
>
|
||||||
|
<mwc-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
class=${classMap({
|
||||||
|
exposed: isExposed!,
|
||||||
|
"not-exposed": !isExposed,
|
||||||
|
})}
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.google.expose"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
</ha-switch>
|
<ha-svg-icon
|
||||||
</ha-formfield>
|
.path=${config.should_expose !== null
|
||||||
|
? isExposed
|
||||||
|
? mdiCheckboxMarked
|
||||||
|
: mdiCloseBox
|
||||||
|
: isDomainExposed
|
||||||
|
? mdiCheckboxMultipleMarked
|
||||||
|
: mdiCloseBoxMultiple}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-list-item hasMeta>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.google.expose_entity"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="exposed"
|
||||||
|
slot="meta"
|
||||||
|
.path=${mdiCheckboxMarked}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
<mwc-list-item hasMeta>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.google.dont_expose_entity"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class="not-exposed"
|
||||||
|
slot="meta"
|
||||||
|
.path=${mdiCloseBox}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
<mwc-list-item hasMeta>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.google.follow_domain"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
class=${classMap({
|
||||||
|
exposed: isDomainExposed,
|
||||||
|
"not-exposed": !isDomainExposed,
|
||||||
|
})}
|
||||||
|
slot="meta"
|
||||||
|
.path=${isDomainExposed
|
||||||
|
? mdiCheckboxMultipleMarked
|
||||||
|
: mdiCloseBoxMultiple}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
${entity.might_2fa
|
${entity.might_2fa
|
||||||
? html`
|
? html`
|
||||||
@ -178,17 +235,16 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
<hass-subpage header="${this.hass!.localize(
|
<hass-subpage header="${this.hass!.localize(
|
||||||
"ui.panel.config.cloud.google.title"
|
"ui.panel.config.cloud.google.title"
|
||||||
)}">
|
)}">
|
||||||
<span slot="toolbar-icon">
|
|
||||||
${selected}${!this.narrow ? html` selected ` : ""}
|
|
||||||
</span>
|
|
||||||
${
|
${
|
||||||
emptyFilter
|
emptyFilter
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<mwc-button
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
icon="hass:tune"
|
|
||||||
@click=${this._openDomainToggler}
|
@click=${this._openDomainToggler}
|
||||||
></ha-icon-button>
|
>${this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.google.manage_domains"
|
||||||
|
)}</mwc-button
|
||||||
|
>
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
@ -204,11 +260,20 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
${
|
${
|
||||||
exposedCards.length > 0
|
exposedCards.length > 0
|
||||||
? html`
|
? html`
|
||||||
<h1>
|
<div class="header">
|
||||||
${this.hass!.localize(
|
<h3>
|
||||||
"ui.panel.config.cloud.google.exposed_entities"
|
${this.hass!.localize(
|
||||||
)}
|
"ui.panel.config.cloud.google.exposed_entities"
|
||||||
</h1>
|
)}
|
||||||
|
</h3>
|
||||||
|
${!this.narrow
|
||||||
|
? this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.exposed",
|
||||||
|
"selected",
|
||||||
|
selected
|
||||||
|
)
|
||||||
|
: selected}
|
||||||
|
</div>
|
||||||
<div class="content">${exposedCards}</div>
|
<div class="content">${exposedCards}</div>
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
@ -216,11 +281,20 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
${
|
${
|
||||||
notExposedCards.length > 0
|
notExposedCards.length > 0
|
||||||
? html`
|
? html`
|
||||||
<h1>
|
<div class="header second">
|
||||||
${this.hass!.localize(
|
<h3>
|
||||||
"ui.panel.config.cloud.google.not_exposed_entities"
|
${this.hass!.localize(
|
||||||
)}
|
"ui.panel.config.cloud.google.not_exposed_entities"
|
||||||
</h1>
|
)}
|
||||||
|
</h3>
|
||||||
|
${!this.narrow
|
||||||
|
? this.hass!.localize(
|
||||||
|
"ui.panel.config.cloud.alexa.not_exposed",
|
||||||
|
"selected",
|
||||||
|
this._entities.length - selected
|
||||||
|
)
|
||||||
|
: this._entities.length - selected}
|
||||||
|
</div>
|
||||||
<div class="content">${notExposedCards}</div>
|
<div class="content">${notExposedCards}</div>
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
@ -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() {
|
private async _fetchData() {
|
||||||
const entities = await fetchCloudGoogleEntities(this.hass);
|
const entities = await fetchCloudGoogleEntities(this.hass);
|
||||||
entities.sort((a, b) => {
|
entities.sort((a, b) => {
|
||||||
@ -260,17 +347,24 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
fireEvent(this, "hass-more-info", { entityId });
|
fireEvent(this, "hass-more-info", { entityId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _exposeChanged(ev: Event) {
|
private async _exposeChanged(ev: CustomEvent<ActionDetail>) {
|
||||||
const entityId = (ev.currentTarget as any).entityId;
|
const entityId = (ev.currentTarget as any).entityId;
|
||||||
const newExposed = (ev.target as HaSwitch).checked;
|
let newVal: boolean | null = null;
|
||||||
await this._updateExposed(entityId, newExposed);
|
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) {
|
private async _updateExposed(entityId: string, newExposed: boolean | null) {
|
||||||
const curExposed = configIsExposed(this._entityConfigs[entityId] || {});
|
|
||||||
if (newExposed === curExposed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this._updateConfig(entityId, {
|
await this._updateConfig(entityId, {
|
||||||
should_expose: newExposed,
|
should_expose: newExposed,
|
||||||
});
|
});
|
||||||
@ -309,16 +403,46 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
domains: this._entities!.map((entity) =>
|
domains: this._entities!.map((entity) =>
|
||||||
computeDomain(entity.entity_id)
|
computeDomain(entity.entity_id)
|
||||||
).filter((value, idx, self) => self.indexOf(value) === idx),
|
).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) => {
|
this._entities!.forEach((entity) => {
|
||||||
if (computeDomain(entity.entity_id) === domain) {
|
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() {
|
private _ensureStatusReload() {
|
||||||
if (this._popstateReloadStatusAttached) {
|
if (this._popstateReloadStatusAttached) {
|
||||||
return;
|
return;
|
||||||
@ -356,46 +480,66 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult[] {
|
||||||
return css`
|
return [
|
||||||
.banner {
|
haStyle,
|
||||||
color: var(--primary-text-color);
|
css`
|
||||||
background-color: var(
|
mwc-list-item > [slot="meta"] {
|
||||||
--ha-card-background,
|
margin-left: 4px;
|
||||||
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%;
|
|
||||||
}
|
}
|
||||||
}
|
.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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +526,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain_toggler": {
|
"domain_toggler": {
|
||||||
"title": "Toggle Domains"
|
"title": "Toggle Domains",
|
||||||
|
"reset_entities": "Reset Entities"
|
||||||
},
|
},
|
||||||
"mqtt_device_debug_info": {
|
"mqtt_device_debug_info": {
|
||||||
"title": "{device} debug info",
|
"title": "{device} debug info",
|
||||||
@ -1383,7 +1384,13 @@
|
|||||||
"title": "Alexa",
|
"title": "Alexa",
|
||||||
"banner": "Editing which entities are exposed via this UI is disabled because you have configured entity filters in configuration.yaml.",
|
"banner": "Editing which entities are exposed via this UI is disabled because you have configured entity filters in configuration.yaml.",
|
||||||
"exposed_entities": "Exposed entities",
|
"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"
|
"expose": "Expose to Alexa"
|
||||||
},
|
},
|
||||||
"dialog_certificate": {
|
"dialog_certificate": {
|
||||||
@ -1399,7 +1406,13 @@
|
|||||||
"disable_2FA": "Disable two factor authentication",
|
"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.",
|
"banner": "Editing which entities are exposed via this UI is disabled because you have configured entity filters in configuration.yaml.",
|
||||||
"exposed_entities": "Exposed entities",
|
"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."
|
"sync_to_google": "Synchronizing changes to Google."
|
||||||
},
|
},
|
||||||
"dialog_cloudhook": {
|
"dialog_cloudhook": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user