diff --git a/src/components/entity/ha-entity-state-picker.ts b/src/components/entity/ha-entity-state-picker.ts index f54a8e5cd8..3b0cedeb0c 100644 --- a/src/components/entity/ha-entity-state-picker.ts +++ b/src/components/entity/ha-entity-state-picker.ts @@ -2,6 +2,7 @@ import type { HassEntity } from "home-assistant-js-websocket"; import type { PropertyValues } from "lit"; import { LitElement, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; +import { ensureArray } from "../../common/array/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; import { getStates } from "../../common/entity/get_states"; import type { HomeAssistant, ValueChangedEvent } from "../../types"; @@ -14,7 +15,7 @@ export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean; class HaEntityStatePicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public entityId?: string; + @property({ attribute: false }) public entityId?: string | string[]; @property() public attribute?: string; @@ -30,6 +31,9 @@ class HaEntityStatePicker extends LitElement { @property({ type: Boolean, attribute: "allow-custom-value" }) public allowCustomValue; + @property({ attribute: false }) + public excludeStates?: string[]; + @property() public label?: string; @property() public value?: string; @@ -51,23 +55,37 @@ class HaEntityStatePicker extends LitElement { changedProps.has("attribute") || changedProps.has("extraOptions") ) { - const stateObj = this.entityId - ? this.hass.states[this.entityId] - : undefined; - (this._comboBox as any).items = [ - ...(this.extraOptions ?? []), - ...(this.entityId && stateObj - ? getStates(this.hass, stateObj, this.attribute).map((key) => ({ - value: key, + const entityIds = this.entityId ? ensureArray(this.entityId) : []; + const stateOptions: { value: string; label: string }[] = []; + const statesSet = new Set(); + + for (const entityId of entityIds) { + const stateObj = this.hass.states[entityId]; + const states = getStates(this.hass, stateObj, this.attribute).filter( + (s) => !this.excludeStates || !this.excludeStates.includes(s) + ); + + for (const s of states) { + if (!statesSet.has(s)) { + statesSet.add(s); + const options = { + value: s, label: !this.attribute - ? this.hass.formatEntityState(stateObj, key) + ? this.hass.formatEntityState(stateObj, s) : this.hass.formatEntityAttributeValue( stateObj, this.attribute, - key + s ), - })) - : []), + }; + stateOptions.push(options); + } + } + } + + (this._comboBox as any).filteredItems = [ + ...(this.extraOptions ?? []), + ...stateOptions, ]; } } diff --git a/src/components/ha-selector/ha-selector-state.ts b/src/components/ha-selector/ha-selector-state.ts index 8350694a33..cb5dc0cd87 100644 --- a/src/components/ha-selector/ha-selector-state.ts +++ b/src/components/ha-selector/ha-selector-state.ts @@ -23,7 +23,7 @@ export class HaSelectorState extends SubscribeMixin(LitElement) { @property({ attribute: false }) public context?: { filter_attribute?: string; - filter_entity?: string; + filter_entity?: string | string[]; }; protected render() { @@ -41,6 +41,7 @@ export class HaSelectorState extends SubscribeMixin(LitElement) { .disabled=${this.disabled} .required=${this.required} allow-custom-value + .excludeStates=${this.selector.state?.exclude_states} > `; } diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts index 898959277d..81b56f58c5 100644 --- a/src/components/ha-service-control.ts +++ b/src/components/ha-service-control.ts @@ -627,7 +627,7 @@ export class HaServiceControl extends LitElement { const fieldDataHasTemplate = this._value?.data && hasTemplate(this._value.data[dataField.key]); - const selector = + let selector = fieldDataHasTemplate && typeof this._value!.data![dataField.key] === "string" ? { template: null } @@ -637,6 +637,16 @@ export class HaServiceControl extends LitElement { : (this._stickySelector[dataField.key] ?? dataField?.selector ?? { text: null }); + if ("state" in selector) { + selector = { + ...selector, + state: { + ...selector.state, + entity_id: targetEntities || undefined, + }, + }; + } + if (fieldDataHasTemplate) { // Hold this selector type until the field is cleared this._stickySelector[dataField.key] = selector; diff --git a/src/data/selector.ts b/src/data/selector.ts index dacd2c0c56..b064d34641 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -393,8 +393,9 @@ export interface SelectorSelector { export interface StateSelector { state: { extra_options?: { label: string; value: any }[]; - entity_id?: string; + entity_id?: string | string[]; attribute?: string; + exclude_states?: string[]; } | null; } diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts index 9f1f94cbdc..d95a39e36e 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts @@ -24,7 +24,10 @@ import { baseTriggerStruct, forDictStruct } from "../../structs"; import type { TriggerElement } from "../ha-automation-trigger-row"; import "../../../../../components/ha-form/ha-form"; import { createDurationData } from "../../../../../common/datetime/create_duration_data"; -import type { SchemaUnion } from "../../../../../components/ha-form/types"; +import type { + HaFormSchema, + SchemaUnion, +} from "../../../../../components/ha-form/types"; const stateTriggerStruct = assign( baseTriggerStruct, @@ -121,6 +124,9 @@ export class HaStateTrigger extends LitElement implements TriggerElement { }, { name: "from", + context: { + filter_entity: "entity_id", + }, selector: { state: { extra_options: (attribute @@ -133,13 +139,15 @@ export class HaStateTrigger extends LitElement implements TriggerElement { value: ANY_STATE_VALUE, }, ]) as any, - entity_id: entityId ? entityId[0] : undefined, attribute: attribute, }, }, }, { name: "to", + context: { + filter_entity: "entity_id", + }, selector: { state: { extra_options: (attribute @@ -152,13 +160,12 @@ export class HaStateTrigger extends LitElement implements TriggerElement { value: ANY_STATE_VALUE, }, ]) as any, - entity_id: entityId ? entityId[0] : undefined, attribute: attribute, }, }, }, { name: "for", selector: { duration: {} } }, - ] as const + ] as const satisfies HaFormSchema[] ); public shouldUpdate(changedProperties: PropertyValues) {