diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index 54327ae35e..3175d393ba 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -92,7 +92,7 @@ export class HaDialog extends DialogBase { padding: 24px 24px 0 24px; } .mdc-dialog__actions { - padding: 0 24px 24px 24px; + padding: 12px 24px 12px 24px; } .mdc-dialog__title::before { display: block; diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index d0e2afb2cd..b9f145c6e8 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -90,6 +90,9 @@ export interface EntityRegistryOptions { number?: NumberEntityOptions; sensor?: SensorEntityOptions; weather?: WeatherEntityOptions; + conversation?: Record; + "cloud.alexa"?: Record; + "cloud.google_assistant"?: Record; } export interface EntityRegistryEntryUpdateParams { diff --git a/src/panels/config/voice-assistants/assist-pref.ts b/src/panels/config/voice-assistants/assist-pref.ts index 221bc6dfa4..80034e3bd1 100644 --- a/src/panels/config/voice-assistants/assist-pref.ts +++ b/src/panels/config/voice-assistants/assist-pref.ts @@ -2,36 +2,40 @@ import "@material/mwc-list/mwc-list"; import { mdiHelpCircle, mdiPlus, mdiStar } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; 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-button"; import "../../../components/ha-card"; import "../../../components/ha-icon-next"; -import "../../../components/ha-svg-icon"; import "../../../components/ha-list-item"; +import "../../../components/ha-svg-icon"; import "../../../components/ha-switch"; -import "../../../components/ha-button"; import { + AssistPipeline, createAssistPipeline, deleteAssistPipeline, listAssistPipelines, - updateAssistPipeline, - AssistPipeline, setAssistPipelinePreferred, + updateAssistPipeline, } from "../../../data/assist_pipeline"; +import { CloudStatus } from "../../../data/cloud"; +import { ExtEntityRegistryEntry } from "../../../data/entity_registry"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../types"; -import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assistant-pipeline-detail"; import { brandsUrl } from "../../../util/brands-url"; -import { formatLanguageCode } from "../../../common/language/format_language"; -import { CloudStatusLoggedIn } from "../../../data/cloud"; +import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assistant-pipeline-detail"; export class AssistPref extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property() private extEntities?: Record; + @state() private _pipelines: AssistPipeline[] = []; @state() private _preferred: string | null = null; - @property() public cloudStatus?: CloudStatusLoggedIn; + @property() public cloudStatus?: CloudStatus; protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); @@ -42,6 +46,13 @@ export class AssistPref extends LitElement { }); } + private _exposedEntities = memoizeOne( + (extEntities: Record) => + Object.values(extEntities).filter( + (entity) => entity.options?.conversation?.should_expose + ).length + ); + protected render() { return html` @@ -106,7 +117,12 @@ export class AssistPref extends LitElement { > ${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, + } )} @@ -128,7 +144,8 @@ export class AssistPref extends LitElement { private async _openDialog(pipeline?: AssistPipeline): Promise { showVoiceAssistantPipelineDetailDialog(this, { - cloudActiveSubscription: this.cloudStatus?.active_subscription, + cloudActiveSubscription: + this.cloudStatus?.logged_in && this.cloudStatus.active_subscription, pipeline, preferred: pipeline?.id === this._preferred, createPipeline: async (values) => { diff --git a/src/panels/config/voice-assistants/cloud-alexa-pref.ts b/src/panels/config/voice-assistants/cloud-alexa-pref.ts index 5f6bce0416..b4106304bf 100644 --- a/src/panels/config/voice-assistants/cloud-alexa-pref.ts +++ b/src/panels/config/voice-assistants/cloud-alexa-pref.ts @@ -2,6 +2,7 @@ import "@material/mwc-button"; import { mdiHelpCircle } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; import { isEmptyFilter } from "../../../common/entity/entity_filter"; import "../../../components/ha-alert"; @@ -10,6 +11,7 @@ import "../../../components/ha-settings-row"; import "../../../components/ha-switch"; import type { HaSwitch } from "../../../components/ha-switch"; import { CloudStatusLoggedIn, updateCloudPref } from "../../../data/cloud"; +import { ExtEntityRegistryEntry } from "../../../data/entity_registry"; import { getExposeNewEntities, setExposeNewEntities, @@ -20,10 +22,19 @@ import { brandsUrl } from "../../../util/brands-url"; export class CloudAlexaPref extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property() private extEntities?: Record; + @property() public cloudStatus?: CloudStatusLoggedIn; @state() private _exposeNew?: boolean; + private _exposedEntities = memoizeOne( + (extEntities: Record) => + Object.values(extEntities).filter( + (entity) => entity.options?.["cloud.alexa"]?.should_expose + ).length + ); + protected willUpdate() { if (!this.hasUpdated) { getExposeNewEntities(this.hass, "cloud.alexa").then((value) => { @@ -159,17 +170,28 @@ export class CloudAlexaPref extends LitElement { ` : ""}`} - + ${alexa_enabled + ? html`` + : nothing} `; } diff --git a/src/panels/config/voice-assistants/cloud-google-pref.ts b/src/panels/config/voice-assistants/cloud-google-pref.ts index 531e05dd0b..3c28d1b636 100644 --- a/src/panels/config/voice-assistants/cloud-google-pref.ts +++ b/src/panels/config/voice-assistants/cloud-google-pref.ts @@ -2,6 +2,7 @@ import "@material/mwc-button"; import { mdiHelpCircle } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-alert"; import "../../../components/ha-card"; @@ -18,10 +19,13 @@ import { getExposeNewEntities, setExposeNewEntities, } from "../../../data/voice"; +import { ExtEntityRegistryEntry } from "../../../data/entity_registry"; export class CloudGooglePref extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property() private extEntities?: Record; + @property({ attribute: false }) public cloudStatus?: CloudStatusLoggedIn; @state() private _exposeNew?: boolean; @@ -36,6 +40,13 @@ export class CloudGooglePref extends LitElement { } } + private _exposedEntities = memoizeOne( + (extEntities: Record) => + Object.values(extEntities).filter( + (entity) => entity.options?.["cloud.google_assistant"]?.should_expose + ).length + ); + protected render() { if (!this.cloudStatus) { return nothing; @@ -215,17 +226,28 @@ export class CloudGooglePref extends LitElement { ` : ""}`} - + ${google_enabled + ? html`` + : nothing} `; } diff --git a/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts b/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts index 141990ae79..137f57a76a 100644 --- a/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts +++ b/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts @@ -1,17 +1,23 @@ +import { consume } from "@lit-labs/context"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item-body"; -import { css, html, LitElement, nothing } from "lit"; -import { customElement, property } from "lit/decorators"; +import { css, html, LitElement, nothing, PropertyValues } from "lit"; +import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { CloudStatus } from "../../../data/cloud"; +import { entitiesContext } from "../../../data/context"; +import { + ExtEntityRegistryEntry, + getExtendedEntityRegistryEntries, +} from "../../../data/entity_registry"; import "../../../layouts/hass-tabs-subpage"; import { HomeAssistant, Route } from "../../../types"; import "./assist-pref"; import "./cloud-alexa-pref"; +import "./cloud-discover"; import "./cloud-google-pref"; import { voiceAssistantTabs } from "./ha-config-voice-assistants"; -import "./cloud-discover"; @customElement("ha-config-voice-assistants-assistants") export class HaConfigVoiceAssistantsAssistants extends LitElement { @@ -25,6 +31,12 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement { @property() public route!: Route; + @state() + @consume({ context: entitiesContext, subscribe: true }) + _entities!: HomeAssistant["entities"]; + + @state() private _extEntities?: Record; + protected render() { if (!this.hass) { return html``; @@ -40,20 +52,25 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement { >
${isComponentLoaded(this.hass, "assist_pipeline") - ? html`` + ? html` + + ` : nothing} ${this.cloudStatus?.logged_in ? html` @@ -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` .content { padding: 28px 20px 0; diff --git a/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts b/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts index c838c8a291..8199907934 100644 --- a/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts +++ b/src/panels/config/voice-assistants/ha-config-voice-assistants-expose.ts @@ -44,7 +44,6 @@ import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box import "../../../layouts/hass-loading-screen"; import "../../../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 { HomeAssistant, Route } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; @@ -53,7 +52,7 @@ import { showExposeEntityDialog } from "./show-dialog-expose-entity"; import { showVoiceSettingsDialog } from "./show-dialog-voice-settings"; @customElement("ha-config-voice-assistants-expose") -export class VoiceAssistantsExpose extends SubscribeMixin(LitElement) { +export class VoiceAssistantsExpose extends LitElement { @property() public hass!: HomeAssistant; @property({ attribute: false }) public cloudStatus?: CloudStatus; diff --git a/src/translations/en.json b/src/translations/en.json index 36a61b8f81..7dbb6e8665 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2030,7 +2030,7 @@ "caption": "Assistants", "pipeline": { "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": { "confirm_title": "Delete {name}?", "confirm_text": "{name} will be permanently deleted." @@ -2845,7 +2845,8 @@ "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.", "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%]", "enable": "enable", "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.", "devices_pin": "Security Devices PIN", "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.", "enter_pin_error": "Unable to store PIN:", "not_configured_title": "Continue setting up Google Assistant",