mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 10:16:46 +00:00
Add combine mode and fix hidden and entity category for service control
This commit is contained in:
parent
be71c0b4fa
commit
ea5c014552
@ -11,6 +11,13 @@ import type { HaComboBox } from "../ha-combo-box";
|
||||
|
||||
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
|
||||
|
||||
interface StateOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const DEFAULT_COMBINE_MODE: "union" | "intersection" = "union";
|
||||
|
||||
@customElement("ha-entity-state-picker")
|
||||
class HaEntityStatePicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -34,6 +41,9 @@ class HaEntityStatePicker extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public hideStates?: string[];
|
||||
|
||||
@property({ attribute: "combine-mode" })
|
||||
public combineMode?: "union" | "intersection";
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public value?: string;
|
||||
@ -56,29 +66,55 @@ class HaEntityStatePicker extends LitElement {
|
||||
changedProps.has("extraOptions")
|
||||
) {
|
||||
const entityIds = this.entityId ? ensureArray(this.entityId) : [];
|
||||
const options: { value: string; label: string }[] = [];
|
||||
const statesSet = new Set<string>();
|
||||
|
||||
for (const entityId of entityIds) {
|
||||
const entitiesOptions = entityIds.map<StateOption[]>((entityId) => {
|
||||
const stateObj = this.hass.states[entityId];
|
||||
if (!stateObj) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const states = getStates(this.hass, stateObj, this.attribute).filter(
|
||||
(s) => !this.hideStates || !this.hideStates.includes(s)
|
||||
);
|
||||
|
||||
for (const s of states) {
|
||||
if (!statesSet.has(s)) {
|
||||
statesSet.add(s);
|
||||
options.push({
|
||||
value: s,
|
||||
label: !this.attribute
|
||||
? this.hass.formatEntityState(stateObj, s)
|
||||
: this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
this.attribute,
|
||||
s
|
||||
),
|
||||
});
|
||||
}
|
||||
return states.map((s) => ({
|
||||
value: s,
|
||||
label: !this.attribute
|
||||
? this.hass.formatEntityState(stateObj, s)
|
||||
: this.hass.formatEntityAttributeValue(stateObj, this.attribute, s),
|
||||
}));
|
||||
});
|
||||
|
||||
const mode: "union" | "intersection" =
|
||||
this.combineMode || DEFAULT_COMBINE_MODE;
|
||||
|
||||
let options: StateOption[] = [];
|
||||
|
||||
if (mode === "union") {
|
||||
// Union: combine all unique states from all entities
|
||||
options = entitiesOptions.reduce(
|
||||
(acc, curr) => [
|
||||
...acc,
|
||||
...curr.filter(
|
||||
(item) => !acc.some((existing) => existing.value === item.value)
|
||||
),
|
||||
],
|
||||
[] as StateOption[]
|
||||
);
|
||||
} else if (mode === "intersection") {
|
||||
// Intersection: only states that exist in ALL entities
|
||||
if (entitiesOptions.length === 0) {
|
||||
options = [];
|
||||
} else if (entitiesOptions.length === 1) {
|
||||
options = entitiesOptions[0];
|
||||
} else {
|
||||
options = entitiesOptions[0].filter((item) =>
|
||||
entitiesOptions
|
||||
.slice(1)
|
||||
.every((entityOptions) =>
|
||||
entityOptions.some((option) => option.value === item.value)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ export class HaSelectorState extends SubscribeMixin(LitElement) {
|
||||
.required=${this.required}
|
||||
allow-custom-value
|
||||
.hideStates=${this.selector.state?.hide_states}
|
||||
.combineMode=${this.selector.state?.combine_mode}
|
||||
></ha-entity-state-picker>
|
||||
`;
|
||||
}
|
||||
|
@ -55,6 +55,20 @@ const showOptionalToggle = (field) =>
|
||||
!field.required &&
|
||||
!("boolean" in field.selector && field.default);
|
||||
|
||||
const enrichSelector = (selector: Selector): Selector => {
|
||||
// Default combine_mode to intersection for state selectors
|
||||
if ("state" in selector) {
|
||||
return {
|
||||
...selector,
|
||||
state: {
|
||||
...selector.state,
|
||||
combine_mode: selector.state?.combine_mode || "intersection",
|
||||
},
|
||||
};
|
||||
}
|
||||
return selector;
|
||||
};
|
||||
|
||||
interface Field extends Omit<HassService["fields"][string], "selector"> {
|
||||
key: string;
|
||||
selector?: Selector;
|
||||
@ -244,7 +258,9 @@ export class HaServiceControl extends LitElement {
|
||||
).map(([key, value]) => ({
|
||||
key,
|
||||
...value,
|
||||
selector: value.selector as Selector | undefined,
|
||||
selector: (value.selector
|
||||
? enrichSelector(value.selector)
|
||||
: undefined) as Selector | undefined,
|
||||
}));
|
||||
|
||||
const flatFields: Field[] = [];
|
||||
@ -314,7 +330,12 @@ export class HaServiceControl extends LitElement {
|
||||
targetSelector
|
||||
);
|
||||
targetDevices.push(...expanded.devices);
|
||||
targetEntities.push(...expanded.entities);
|
||||
const primaryEntities = expanded.entities.filter(
|
||||
(entityId) =>
|
||||
!this.hass.entities[entityId]?.entity_category &&
|
||||
!this.hass.entities[entityId]?.hidden
|
||||
);
|
||||
targetEntities.push(primaryEntities);
|
||||
targetAreas.push(...expanded.areas);
|
||||
});
|
||||
}
|
||||
@ -338,20 +359,29 @@ export class HaServiceControl extends LitElement {
|
||||
this.hass.entities,
|
||||
targetSelector
|
||||
);
|
||||
targetEntities.push(...expanded.entities);
|
||||
const primaryEntities = expanded.entities.filter(
|
||||
(entityId) =>
|
||||
!this.hass.entities[entityId]?.entity_category &&
|
||||
!this.hass.entities[entityId]?.hidden
|
||||
);
|
||||
targetEntities.push(...primaryEntities);
|
||||
targetDevices.push(...expanded.devices);
|
||||
});
|
||||
}
|
||||
if (targetDevices.length) {
|
||||
targetDevices.forEach((deviceId) => {
|
||||
targetEntities.push(
|
||||
...expandDeviceTarget(
|
||||
this.hass,
|
||||
deviceId,
|
||||
this.hass.entities,
|
||||
targetSelector
|
||||
).entities
|
||||
const expanded = expandDeviceTarget(
|
||||
this.hass,
|
||||
deviceId,
|
||||
this.hass.entities,
|
||||
targetSelector
|
||||
);
|
||||
const primaryEntities = expanded.entities.filter(
|
||||
(entityId) =>
|
||||
!this.hass.entities[entityId]?.entity_category &&
|
||||
!this.hass.entities[entityId]?.hidden
|
||||
);
|
||||
targetEntities.push(...primaryEntities);
|
||||
});
|
||||
}
|
||||
return targetEntities;
|
||||
|
@ -719,7 +719,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _entityRegMeetsFilter(entity: EntityRegistryDisplayEntry): boolean {
|
||||
if (entity.entity_category) {
|
||||
if (entity.hidden || entity.entity_category) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -396,6 +396,7 @@ export interface StateSelector {
|
||||
entity_id?: string | string[];
|
||||
attribute?: string;
|
||||
hide_states?: string[];
|
||||
combine_mode?: "union" | "intersection";
|
||||
} | null;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user