Add support for multiple entities and target for state selector

This commit is contained in:
Paul Bottein 2025-07-16 19:13:35 +02:00
parent 039ef18d8c
commit 77afb9ac1a
No known key found for this signature in database
5 changed files with 57 additions and 20 deletions

View File

@ -2,6 +2,7 @@ import type { HassEntity } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit"; import type { PropertyValues } from "lit";
import { LitElement, html, nothing } from "lit"; import { LitElement, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { ensureArray } from "../../common/array/ensure-array";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { getStates } from "../../common/entity/get_states"; import { getStates } from "../../common/entity/get_states";
import type { HomeAssistant, ValueChangedEvent } from "../../types"; import type { HomeAssistant, ValueChangedEvent } from "../../types";
@ -14,7 +15,7 @@ export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
class HaEntityStatePicker extends LitElement { class HaEntityStatePicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public entityId?: string; @property({ attribute: false }) public entityId?: string | string[];
@property() public attribute?: string; @property() public attribute?: string;
@ -30,6 +31,9 @@ class HaEntityStatePicker extends LitElement {
@property({ type: Boolean, attribute: "allow-custom-value" }) @property({ type: Boolean, attribute: "allow-custom-value" })
public allowCustomValue; public allowCustomValue;
@property({ attribute: false })
public excludeStates?: string[];
@property() public label?: string; @property() public label?: string;
@property() public value?: string; @property() public value?: string;
@ -51,23 +55,37 @@ class HaEntityStatePicker extends LitElement {
changedProps.has("attribute") || changedProps.has("attribute") ||
changedProps.has("extraOptions") changedProps.has("extraOptions")
) { ) {
const stateObj = this.entityId const entityIds = this.entityId ? ensureArray(this.entityId) : [];
? this.hass.states[this.entityId] const stateOptions: { value: string; label: string }[] = [];
: undefined; const statesSet = new Set<string>();
(this._comboBox as any).items = [
...(this.extraOptions ?? []), for (const entityId of entityIds) {
...(this.entityId && stateObj const stateObj = this.hass.states[entityId];
? getStates(this.hass, stateObj, this.attribute).map((key) => ({ const states = getStates(this.hass, stateObj, this.attribute).filter(
value: key, (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 label: !this.attribute
? this.hass.formatEntityState(stateObj, key) ? this.hass.formatEntityState(stateObj, s)
: this.hass.formatEntityAttributeValue( : this.hass.formatEntityAttributeValue(
stateObj, stateObj,
this.attribute, this.attribute,
key s
), ),
})) };
: []), stateOptions.push(options);
}
}
}
(this._comboBox as any).filteredItems = [
...(this.extraOptions ?? []),
...stateOptions,
]; ];
} }
} }

View File

@ -23,7 +23,7 @@ export class HaSelectorState extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public context?: { @property({ attribute: false }) public context?: {
filter_attribute?: string; filter_attribute?: string;
filter_entity?: string; filter_entity?: string | string[];
}; };
protected render() { protected render() {
@ -41,6 +41,7 @@ export class HaSelectorState extends SubscribeMixin(LitElement) {
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.required} .required=${this.required}
allow-custom-value allow-custom-value
.excludeStates=${this.selector.state?.exclude_states}
></ha-entity-state-picker> ></ha-entity-state-picker>
`; `;
} }

View File

@ -627,7 +627,7 @@ export class HaServiceControl extends LitElement {
const fieldDataHasTemplate = const fieldDataHasTemplate =
this._value?.data && hasTemplate(this._value.data[dataField.key]); this._value?.data && hasTemplate(this._value.data[dataField.key]);
const selector = let selector =
fieldDataHasTemplate && fieldDataHasTemplate &&
typeof this._value!.data![dataField.key] === "string" typeof this._value!.data![dataField.key] === "string"
? { template: null } ? { template: null }
@ -637,6 +637,16 @@ export class HaServiceControl extends LitElement {
: (this._stickySelector[dataField.key] ?? : (this._stickySelector[dataField.key] ??
dataField?.selector ?? { text: null }); dataField?.selector ?? { text: null });
if ("state" in selector) {
selector = {
...selector,
state: {
...selector.state,
entity_id: targetEntities || undefined,
},
};
}
if (fieldDataHasTemplate) { if (fieldDataHasTemplate) {
// Hold this selector type until the field is cleared // Hold this selector type until the field is cleared
this._stickySelector[dataField.key] = selector; this._stickySelector[dataField.key] = selector;

View File

@ -393,8 +393,9 @@ export interface SelectorSelector {
export interface StateSelector { export interface StateSelector {
state: { state: {
extra_options?: { label: string; value: any }[]; extra_options?: { label: string; value: any }[];
entity_id?: string; entity_id?: string | string[];
attribute?: string; attribute?: string;
exclude_states?: string[];
} | null; } | null;
} }

View File

@ -24,7 +24,10 @@ import { baseTriggerStruct, forDictStruct } from "../../structs";
import type { TriggerElement } from "../ha-automation-trigger-row"; import type { TriggerElement } from "../ha-automation-trigger-row";
import "../../../../../components/ha-form/ha-form"; import "../../../../../components/ha-form/ha-form";
import { createDurationData } from "../../../../../common/datetime/create_duration_data"; 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( const stateTriggerStruct = assign(
baseTriggerStruct, baseTriggerStruct,
@ -121,6 +124,9 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
}, },
{ {
name: "from", name: "from",
context: {
filter_entity: "entity_id",
},
selector: { selector: {
state: { state: {
extra_options: (attribute extra_options: (attribute
@ -133,13 +139,15 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
value: ANY_STATE_VALUE, value: ANY_STATE_VALUE,
}, },
]) as any, ]) as any,
entity_id: entityId ? entityId[0] : undefined,
attribute: attribute, attribute: attribute,
}, },
}, },
}, },
{ {
name: "to", name: "to",
context: {
filter_entity: "entity_id",
},
selector: { selector: {
state: { state: {
extra_options: (attribute extra_options: (attribute
@ -152,13 +160,12 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
value: ANY_STATE_VALUE, value: ANY_STATE_VALUE,
}, },
]) as any, ]) as any,
entity_id: entityId ? entityId[0] : undefined,
attribute: attribute, attribute: attribute,
}, },
}, },
}, },
{ name: "for", selector: { duration: {} } }, { name: "for", selector: { duration: {} } },
] as const ] as const satisfies HaFormSchema[]
); );
public shouldUpdate(changedProperties: PropertyValues) { public shouldUpdate(changedProperties: PropertyValues) {