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 { 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<string>();
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,
];
}
}

View File

@ -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}
></ha-entity-state-picker>
`;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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) {