Show number of exposed entities (#16321)

This commit is contained in:
Bram Kragten 2023-04-26 16:23:23 +02:00 committed by GitHub
parent ff4c01e15c
commit 8cf8c41698
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 140 additions and 45 deletions

View File

@ -92,7 +92,7 @@ export class HaDialog extends DialogBase {
padding: 24px 24px 0 24px; padding: 24px 24px 0 24px;
} }
.mdc-dialog__actions { .mdc-dialog__actions {
padding: 0 24px 24px 24px; padding: 12px 24px 12px 24px;
} }
.mdc-dialog__title::before { .mdc-dialog__title::before {
display: block; display: block;

View File

@ -90,6 +90,9 @@ export interface EntityRegistryOptions {
number?: NumberEntityOptions; number?: NumberEntityOptions;
sensor?: SensorEntityOptions; sensor?: SensorEntityOptions;
weather?: WeatherEntityOptions; weather?: WeatherEntityOptions;
conversation?: Record<string, unknown>;
"cloud.alexa"?: Record<string, unknown>;
"cloud.google_assistant"?: Record<string, unknown>;
} }
export interface EntityRegistryEntryUpdateParams { export interface EntityRegistryEntryUpdateParams {

View File

@ -2,36 +2,40 @@ import "@material/mwc-list/mwc-list";
import { mdiHelpCircle, mdiPlus, mdiStar } from "@mdi/js"; import { mdiHelpCircle, mdiPlus, mdiStar } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators"; import { property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { formatLanguageCode } from "../../../common/language/format_language";
import "../../../components/ha-alert"; import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-icon-next"; import "../../../components/ha-icon-next";
import "../../../components/ha-svg-icon";
import "../../../components/ha-list-item"; import "../../../components/ha-list-item";
import "../../../components/ha-svg-icon";
import "../../../components/ha-switch"; import "../../../components/ha-switch";
import "../../../components/ha-button";
import { import {
AssistPipeline,
createAssistPipeline, createAssistPipeline,
deleteAssistPipeline, deleteAssistPipeline,
listAssistPipelines, listAssistPipelines,
updateAssistPipeline,
AssistPipeline,
setAssistPipelinePreferred, setAssistPipelinePreferred,
updateAssistPipeline,
} from "../../../data/assist_pipeline"; } from "../../../data/assist_pipeline";
import { CloudStatus } from "../../../data/cloud";
import { ExtEntityRegistryEntry } from "../../../data/entity_registry";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assistant-pipeline-detail";
import { brandsUrl } from "../../../util/brands-url"; import { brandsUrl } from "../../../util/brands-url";
import { formatLanguageCode } from "../../../common/language/format_language"; import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assistant-pipeline-detail";
import { CloudStatusLoggedIn } from "../../../data/cloud";
export class AssistPref extends LitElement { export class AssistPref extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() private extEntities?: Record<string, ExtEntityRegistryEntry>;
@state() private _pipelines: AssistPipeline[] = []; @state() private _pipelines: AssistPipeline[] = [];
@state() private _preferred: string | null = null; @state() private _preferred: string | null = null;
@property() public cloudStatus?: CloudStatusLoggedIn; @property() public cloudStatus?: CloudStatus;
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
@ -42,6 +46,13 @@ export class AssistPref extends LitElement {
}); });
} }
private _exposedEntities = memoizeOne(
(extEntities: Record<string, ExtEntityRegistryEntry>) =>
Object.values(extEntities).filter(
(entity) => entity.options?.conversation?.should_expose
).length
);
protected render() { protected render() {
return html` return html`
<ha-card outlined> <ha-card outlined>
@ -106,7 +117,12 @@ export class AssistPref extends LitElement {
> >
<ha-button> <ha-button>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.voice_assistants.assistants.pipeline.manage_entities" "ui.panel.config.voice_assistants.assistants.pipeline.exposed_entities",
{
number: this.extEntities
? this._exposedEntities(this.extEntities)
: 0,
}
)} )}
</ha-button> </ha-button>
</a> </a>
@ -128,7 +144,8 @@ export class AssistPref extends LitElement {
private async _openDialog(pipeline?: AssistPipeline): Promise<void> { private async _openDialog(pipeline?: AssistPipeline): Promise<void> {
showVoiceAssistantPipelineDetailDialog(this, { showVoiceAssistantPipelineDetailDialog(this, {
cloudActiveSubscription: this.cloudStatus?.active_subscription, cloudActiveSubscription:
this.cloudStatus?.logged_in && this.cloudStatus.active_subscription,
pipeline, pipeline,
preferred: pipeline?.id === this._preferred, preferred: pipeline?.id === this._preferred,
createPipeline: async (values) => { createPipeline: async (values) => {

View File

@ -2,6 +2,7 @@ import "@material/mwc-button";
import { mdiHelpCircle } from "@mdi/js"; import { mdiHelpCircle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators"; import { property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { isEmptyFilter } from "../../../common/entity/entity_filter"; import { isEmptyFilter } from "../../../common/entity/entity_filter";
import "../../../components/ha-alert"; import "../../../components/ha-alert";
@ -10,6 +11,7 @@ import "../../../components/ha-settings-row";
import "../../../components/ha-switch"; import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch"; import type { HaSwitch } from "../../../components/ha-switch";
import { CloudStatusLoggedIn, updateCloudPref } from "../../../data/cloud"; import { CloudStatusLoggedIn, updateCloudPref } from "../../../data/cloud";
import { ExtEntityRegistryEntry } from "../../../data/entity_registry";
import { import {
getExposeNewEntities, getExposeNewEntities,
setExposeNewEntities, setExposeNewEntities,
@ -20,10 +22,19 @@ import { brandsUrl } from "../../../util/brands-url";
export class CloudAlexaPref extends LitElement { export class CloudAlexaPref extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() private extEntities?: Record<string, ExtEntityRegistryEntry>;
@property() public cloudStatus?: CloudStatusLoggedIn; @property() public cloudStatus?: CloudStatusLoggedIn;
@state() private _exposeNew?: boolean; @state() private _exposeNew?: boolean;
private _exposedEntities = memoizeOne(
(extEntities: Record<string, ExtEntityRegistryEntry>) =>
Object.values(extEntities).filter(
(entity) => entity.options?.["cloud.alexa"]?.should_expose
).length
);
protected willUpdate() { protected willUpdate() {
if (!this.hasUpdated) { if (!this.hasUpdated) {
getExposeNewEntities(this.hass, "cloud.alexa").then((value) => { getExposeNewEntities(this.hass, "cloud.alexa").then((value) => {
@ -159,17 +170,28 @@ export class CloudAlexaPref extends LitElement {
` `
: ""}`} : ""}`}
</div> </div>
<div class="card-actions"> ${alexa_enabled
? html`<div class="card-actions">
<a <a
href="/config/voice-assistants/expose?assistants=cloud.alexa&historyBack" href="/config/voice-assistants/expose?assistants=cloud.alexa&historyBack"
> >
<mwc-button <mwc-button>
>${this.hass!.localize( ${manualConfig
"ui.panel.config.cloud.account.alexa.manage_entities" ? this.hass!.localize(
)}</mwc-button "ui.panel.config.cloud.account.alexa.show_entities"
> )
: this.hass.localize(
"ui.panel.config.cloud.account.alexa.exposed_entities",
{
number: this.extEntities
? this._exposedEntities(this.extEntities)
: 0,
}
)}
</mwc-button>
</a> </a>
</div> </div>`
: nothing}
</ha-card> </ha-card>
`; `;
} }

View File

@ -2,6 +2,7 @@ import "@material/mwc-button";
import { mdiHelpCircle } from "@mdi/js"; import { mdiHelpCircle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators"; import { property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-alert"; import "../../../components/ha-alert";
import "../../../components/ha-card"; import "../../../components/ha-card";
@ -18,10 +19,13 @@ import {
getExposeNewEntities, getExposeNewEntities,
setExposeNewEntities, setExposeNewEntities,
} from "../../../data/voice"; } from "../../../data/voice";
import { ExtEntityRegistryEntry } from "../../../data/entity_registry";
export class CloudGooglePref extends LitElement { export class CloudGooglePref extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() private extEntities?: Record<string, ExtEntityRegistryEntry>;
@property({ attribute: false }) public cloudStatus?: CloudStatusLoggedIn; @property({ attribute: false }) public cloudStatus?: CloudStatusLoggedIn;
@state() private _exposeNew?: boolean; @state() private _exposeNew?: boolean;
@ -36,6 +40,13 @@ export class CloudGooglePref extends LitElement {
} }
} }
private _exposedEntities = memoizeOne(
(extEntities: Record<string, ExtEntityRegistryEntry>) =>
Object.values(extEntities).filter(
(entity) => entity.options?.["cloud.google_assistant"]?.should_expose
).length
);
protected render() { protected render() {
if (!this.cloudStatus) { if (!this.cloudStatus) {
return nothing; return nothing;
@ -215,17 +226,28 @@ export class CloudGooglePref extends LitElement {
` `
: ""}`} : ""}`}
</div> </div>
<div class="card-actions"> ${google_enabled
? html`<div class="card-actions">
<a <a
href="/config/voice-assistants/expose?assistants=cloud.google_assistant&historyBack" href="/config/voice-assistants/expose?assistants=cloud.google_assistant&historyBack"
> >
<mwc-button> <mwc-button>
${this.hass.localize( ${manualConfig
"ui.panel.config.cloud.account.google.manage_entities" ? this.hass!.localize(
"ui.panel.config.cloud.account.google.show_entities"
)
: this.hass.localize(
"ui.panel.config.cloud.account.google.exposed_entities",
{
number: this.extEntities
? this._exposedEntities(this.extEntities)
: 0,
}
)} )}
</mwc-button> </mwc-button>
</a> </a>
</div> </div>`
: nothing}
</ha-card> </ha-card>
`; `;
} }

View File

@ -1,17 +1,23 @@
import { consume } from "@lit-labs/context";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { CloudStatus } from "../../../data/cloud"; import { CloudStatus } from "../../../data/cloud";
import { entitiesContext } from "../../../data/context";
import {
ExtEntityRegistryEntry,
getExtendedEntityRegistryEntries,
} from "../../../data/entity_registry";
import "../../../layouts/hass-tabs-subpage"; import "../../../layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import "./assist-pref"; import "./assist-pref";
import "./cloud-alexa-pref"; import "./cloud-alexa-pref";
import "./cloud-discover";
import "./cloud-google-pref"; import "./cloud-google-pref";
import { voiceAssistantTabs } from "./ha-config-voice-assistants"; import { voiceAssistantTabs } from "./ha-config-voice-assistants";
import "./cloud-discover";
@customElement("ha-config-voice-assistants-assistants") @customElement("ha-config-voice-assistants-assistants")
export class HaConfigVoiceAssistantsAssistants extends LitElement { export class HaConfigVoiceAssistantsAssistants extends LitElement {
@ -25,6 +31,12 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement {
@property() public route!: Route; @property() public route!: Route;
@state()
@consume({ context: entitiesContext, subscribe: true })
_entities!: HomeAssistant["entities"];
@state() private _extEntities?: Record<string, ExtEntityRegistryEntry>;
protected render() { protected render() {
if (!this.hass) { if (!this.hass) {
return html`<hass-loading-screen></hass-loading-screen>`; return html`<hass-loading-screen></hass-loading-screen>`;
@ -40,20 +52,25 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement {
> >
<div class="content"> <div class="content">
${isComponentLoaded(this.hass, "assist_pipeline") ${isComponentLoaded(this.hass, "assist_pipeline")
? html`<assist-pref ? html`
<assist-pref
.hass=${this.hass} .hass=${this.hass}
.cloudStatus=${this.cloudStatus} .cloudStatus=${this.cloudStatus}
></assist-pref>` .extEntities=${this._extEntities}
></assist-pref>
`
: nothing} : nothing}
${this.cloudStatus?.logged_in ${this.cloudStatus?.logged_in
? html` ? html`
<cloud-alexa-pref <cloud-alexa-pref
.hass=${this.hass} .hass=${this.hass}
.extEntities=${this._extEntities}
.cloudStatus=${this.cloudStatus} .cloudStatus=${this.cloudStatus}
dir=${computeRTLDirection(this.hass)} dir=${computeRTLDirection(this.hass)}
></cloud-alexa-pref> ></cloud-alexa-pref>
<cloud-google-pref <cloud-google-pref
.hass=${this.hass} .hass=${this.hass}
.extEntities=${this._extEntities}
.cloudStatus=${this.cloudStatus} .cloudStatus=${this.cloudStatus}
dir=${computeRTLDirection(this.hass)} dir=${computeRTLDirection(this.hass)}
></cloud-google-pref> ></cloud-google-pref>
@ -64,6 +81,19 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement {
`; `;
} }
public willUpdate(changedProperties: PropertyValues): void {
if (changedProperties.has("_entities")) {
this._fetchExtendedEntities();
}
}
private async _fetchExtendedEntities() {
this._extEntities = await getExtendedEntityRegistryEntries(
this.hass,
Object.keys(this._entities)
);
}
static styles = css` static styles = css`
.content { .content {
padding: 28px 20px 0; padding: 28px 20px 0;

View File

@ -44,7 +44,6 @@ import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box
import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-loading-screen";
import "../../../layouts/hass-tabs-subpage-data-table"; import "../../../layouts/hass-tabs-subpage-data-table";
import type { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table"; import type { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { brandsUrl } from "../../../util/brands-url"; import { brandsUrl } from "../../../util/brands-url";
@ -53,7 +52,7 @@ import { showExposeEntityDialog } from "./show-dialog-expose-entity";
import { showVoiceSettingsDialog } from "./show-dialog-voice-settings"; import { showVoiceSettingsDialog } from "./show-dialog-voice-settings";
@customElement("ha-config-voice-assistants-expose") @customElement("ha-config-voice-assistants-expose")
export class VoiceAssistantsExpose extends SubscribeMixin(LitElement) { export class VoiceAssistantsExpose extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property({ attribute: false }) public cloudStatus?: CloudStatus; @property({ attribute: false }) public cloudStatus?: CloudStatus;

View File

@ -2030,7 +2030,7 @@
"caption": "Assistants", "caption": "Assistants",
"pipeline": { "pipeline": {
"add_assistant": "Add assistant", "add_assistant": "Add assistant",
"manage_entities": "[%key:ui::panel::config::cloud::account::google::manage_entities%]", "exposed_entities": "[%key:ui::panel::config::cloud::account::google::exposed_entities%]",
"delete": { "delete": {
"confirm_title": "Delete {name}?", "confirm_title": "Delete {name}?",
"confirm_text": "{name} will be permanently deleted." "confirm_text": "{name} will be permanently deleted."
@ -2845,7 +2845,8 @@
"enable_state_reporting": "Enable State Reporting", "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 Amazon. This allows you to always see the latest states in the Alexa app and use the state changes to create routines.", "info_state_reporting": "If you enable state reporting, Home Assistant will send all state changes of exposed entities to Amazon. This allows you to always see the latest states in the Alexa app and use the state changes to create routines.",
"state_reporting_error": "Unable to {enable_disable} report state.", "state_reporting_error": "Unable to {enable_disable} report state.",
"manage_entities": "[%key:ui::panel::config::cloud::account::google::manage_entities%]", "show_entities": "[%key:ui::panel::config::cloud::account::google::show_entities%]",
"exposed_entities": "[%key:ui::panel::config::cloud::account::google::exposed_entities%]",
"manual_config": "[%key:ui::panel::config::cloud::account::google::manual_config%]", "manual_config": "[%key:ui::panel::config::cloud::account::google::manual_config%]",
"enable": "enable", "enable": "enable",
"disable": "disable", "disable": "disable",
@ -2868,7 +2869,8 @@
"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.", "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", "devices_pin": "Security Devices PIN",
"enter_pin_hint": "Enter a PIN to use security devices", "enter_pin_hint": "Enter a PIN to use security devices",
"manage_entities": "Manage Entities", "show_entities": "Show Entities",
"exposed_entities": "{number} {number, plural,\n one {entity}\n other {entities}\n} exposed",
"manual_config": "Editing which entities are exposed via the UI is disabled because you have configured entity filters in configuration.yaml.", "manual_config": "Editing which entities are exposed via the UI is disabled because you have configured entity filters in configuration.yaml.",
"enter_pin_error": "Unable to store PIN:", "enter_pin_error": "Unable to store PIN:",
"not_configured_title": "Continue setting up Google Assistant", "not_configured_title": "Continue setting up Google Assistant",