diff --git a/src/data/condition.ts b/src/data/condition.ts index 44a0e21860..d8c7af44fe 100644 --- a/src/data/condition.ts +++ b/src/data/condition.ts @@ -46,7 +46,6 @@ export const CONDITION_GROUPS: AutomationElementGroup = { icon: mdiDotsHorizontal, members: { template: {}, - trigger: {}, }, }, } as const; diff --git a/src/panels/config/automation/add-automation-element-dialog.ts b/src/panels/config/automation/add-automation-element-dialog.ts index b96283a61d..31760dca1b 100644 --- a/src/panels/config/automation/add-automation-element-dialog.ts +++ b/src/panels/config/automation/add-automation-element-dialog.ts @@ -1,7 +1,14 @@ import "@material/mwc-list/mwc-list"; import { mdiClose, mdiContentPaste, mdiPlus } from "@mdi/js"; import Fuse, { IFuseOptions } from "fuse.js"; -import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; +import { + CSSResultGroup, + LitElement, + PropertyValues, + css, + html, + nothing, +} from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import { repeat } from "lit/directives/repeat"; @@ -42,6 +49,8 @@ import { AddAutomationElementDialogParams, PASTE_VALUE, } from "./show-add-automation-element-dialog"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { deepEqual } from "../../../common/util/deep-equal"; const TYPES = { trigger: { groups: TRIGGER_GROUPS, icons: TRIGGER_ICONS }, @@ -93,13 +102,15 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { @state() private _manifests?: DomainManifestLookup; + @state() private _domains?: Set; + @query("ha-dialog") private _dialog?: HaDialog; private _fullScreen = false; - private _width?: number; + @state() private _width?: number; - private _height?: number; + @state() private _height?: number; public showDialog(params): void { this._params = params; @@ -124,6 +135,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { this._prev = undefined; this._filter = ""; this._manifests = undefined; + this._domains = undefined; } private _convertToItem = ( @@ -152,6 +164,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { private _getFilteredItems = memoizeOne( ( type: AddAutomationElementDialogParams["type"], + root: AddAutomationElementDialogParams["root"], group: string | undefined, filter: string, localize: LocalizeFunc, @@ -164,6 +177,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { : TYPES[type].groups[group].members! : TYPES[type].groups; + if (type === "condition" && group === "other" && !root) { + groups.trigger = {}; + } + const flattenGroups = (grp: AutomationElementGroup) => Object.entries(grp).map(([key, options]) => options.members @@ -191,7 +208,9 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { private _getGroupItems = memoizeOne( ( type: AddAutomationElementDialogParams["type"], + root: AddAutomationElementDialogParams["root"], group: string | undefined, + domains: Set | undefined, localize: LocalizeFunc, services: HomeAssistant["services"], manifests?: DomainManifestLookup @@ -208,6 +227,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { ? TYPES[type].groups[group].members! : TYPES[type].groups; + if (type === "condition" && group === "other" && !root) { + groups.trigger = {}; + } + const result = Object.entries(groups).map(([key, options]) => this._convertToItem(key, options, type, localize) ); @@ -215,15 +238,33 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { if (type === "action") { if (!this._group) { result.unshift( - ...this._serviceGroups(localize, services, manifests, undefined) + ...this._serviceGroups( + localize, + services, + manifests, + domains, + undefined + ) ); } else if (this._group === "helpers") { result.unshift( - ...this._serviceGroups(localize, services, manifests, "helper") + ...this._serviceGroups( + localize, + services, + manifests, + domains, + "helper" + ) ); } else if (this._group === "other") { result.unshift( - ...this._serviceGroups(localize, services, manifests, "other") + ...this._serviceGroups( + localize, + services, + manifests, + domains, + "other" + ) ); } } @@ -243,42 +284,44 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { } ); - private _serviceGroups = memoizeOne( - ( - localize: LocalizeFunc, - services: HomeAssistant["services"], - manifests: DomainManifestLookup | undefined, - type: "helper" | "other" | undefined - ): ListItem[] => { - if (!services || !manifests) { - return []; - } - const result: ListItem[] = []; - Object.keys(services).forEach((domain) => { - const manifest = manifests[domain]; - if ( - (type === undefined && - manifest?.integration_type === "entity" && - !ENTITY_DOMAINS_OTHER.has(domain)) || - (type === "helper" && manifest?.integration_type === "helper") || - (type === "other" && - (ENTITY_DOMAINS_OTHER.has(domain) || - !["helper", "entity"].includes(manifest?.integration_type || ""))) - ) { - result.push({ - group: true, - icon: domainIcon(domain), - key: `${SERVICE_PREFIX}${domain}`, - name: domainToName(localize, domain, manifest), - description: "", - }); - } - }); - return result.sort((a, b) => - stringCompare(a.name, b.name, this.hass.locale.language) - ); + private _serviceGroups = ( + localize: LocalizeFunc, + services: HomeAssistant["services"], + manifests: DomainManifestLookup | undefined, + domains: Set | undefined, + type: "helper" | "other" | undefined + ): ListItem[] => { + if (!services || !manifests) { + return []; } - ); + const result: ListItem[] = []; + Object.keys(services).forEach((domain) => { + const manifest = manifests[domain]; + const domainUsed = !domains ? true : domains.has(domain); + if ( + (type === undefined && + manifest?.integration_type === "entity" && + domainUsed && + !ENTITY_DOMAINS_OTHER.has(domain)) || + (type === "helper" && manifest?.integration_type === "helper") || + (type === "other" && + (ENTITY_DOMAINS_OTHER.has(domain) || + (!domainUsed && manifest?.integration_type === "entity") || + !["helper", "entity"].includes(manifest?.integration_type || ""))) + ) { + result.push({ + group: true, + icon: domainIcon(domain), + key: `${SERVICE_PREFIX}${domain}`, + name: domainToName(localize, domain, manifest), + description: "", + }); + } + }); + return result.sort((a, b) => + stringCompare(a.name, b.name, this.hass.locale.language) + ); + }; private _services = memoizeOne( ( @@ -368,6 +411,19 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { this._height = boundingRect?.height; } + protected willUpdate(changedProperties: PropertyValues): void { + if ( + this._params?.type === "action" && + changedProperties.has("hass") && + changedProperties.get("hass")?.states !== this.hass.states + ) { + const domains = new Set(Object.keys(this.hass.states).map(computeDomain)); + if (!deepEqual(domains, this._domains)) { + this._domains = domains; + } + } + } + protected render() { if (!this._params) { return nothing; @@ -376,6 +432,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { const items = this._filter ? this._getFilteredItems( this._params.type, + this._params.root, this._group, this._filter, this.hass.localize, @@ -384,7 +441,9 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { ) : this._getGroupItems( this._params.type, + this._params.root, this._group, + this._domains, this.hass.localize, this.hass.services, this._manifests @@ -562,6 +621,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { ha-icon-next { width: 24px; } + mwc-list { + max-height: 468px; + max-width: 100vw; + } search-input { display: block; margin: 0 16px; diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts index 27364b92a3..ecf4f88237 100644 --- a/src/panels/config/automation/condition/ha-automation-condition.ts +++ b/src/panels/config/automation/condition/ha-automation-condition.ts @@ -203,6 +203,7 @@ export default class HaAutomationCondition extends LitElement { showAddAutomationElementDialog(this, { type: "condition", add: this._addCondition, + root: !this.nested, clipboardItem: this._clipboard?.condition?.condition, }); } diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-device.ts b/src/panels/config/automation/condition/types/ha-automation-condition-device.ts index 71568fe02c..4593a5f701 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-device.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-device.ts @@ -168,6 +168,7 @@ export class HaDeviceCondition extends LitElement { } ha-form { + display: block; margin-top: 24px; } `; diff --git a/src/panels/config/automation/show-add-automation-element-dialog.ts b/src/panels/config/automation/show-add-automation-element-dialog.ts index c497ca85c6..5545350512 100644 --- a/src/panels/config/automation/show-add-automation-element-dialog.ts +++ b/src/panels/config/automation/show-add-automation-element-dialog.ts @@ -6,6 +6,7 @@ export interface AddAutomationElementDialogParams { type: "trigger" | "condition" | "action"; add: (key: string) => void; clipboardItem: string | undefined; + root?: boolean; group?: string; } const loadDialog = () => import("./add-automation-element-dialog"); diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts index 8b3a635752..1b94ff6138 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-device.ts @@ -174,6 +174,7 @@ export class HaDeviceTrigger extends LitElement { } ha-form { + display: block; margin-top: 24px; } `;