mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 11:16:35 +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;
|
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
|
||||||
|
|
||||||
|
interface StateOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_COMBINE_MODE: "union" | "intersection" = "union";
|
||||||
|
|
||||||
@customElement("ha-entity-state-picker")
|
@customElement("ha-entity-state-picker")
|
||||||
class HaEntityStatePicker extends LitElement {
|
class HaEntityStatePicker extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -34,6 +41,9 @@ class HaEntityStatePicker extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public hideStates?: string[];
|
public hideStates?: string[];
|
||||||
|
|
||||||
|
@property({ attribute: "combine-mode" })
|
||||||
|
public combineMode?: "union" | "intersection";
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@property() public value?: string;
|
@property() public value?: string;
|
||||||
@ -56,29 +66,55 @@ class HaEntityStatePicker extends LitElement {
|
|||||||
changedProps.has("extraOptions")
|
changedProps.has("extraOptions")
|
||||||
) {
|
) {
|
||||||
const entityIds = this.entityId ? ensureArray(this.entityId) : [];
|
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];
|
const stateObj = this.hass.states[entityId];
|
||||||
|
if (!stateObj) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const states = getStates(this.hass, stateObj, this.attribute).filter(
|
const states = getStates(this.hass, stateObj, this.attribute).filter(
|
||||||
(s) => !this.hideStates || !this.hideStates.includes(s)
|
(s) => !this.hideStates || !this.hideStates.includes(s)
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const s of states) {
|
return states.map((s) => ({
|
||||||
if (!statesSet.has(s)) {
|
value: s,
|
||||||
statesSet.add(s);
|
label: !this.attribute
|
||||||
options.push({
|
? this.hass.formatEntityState(stateObj, s)
|
||||||
value: s,
|
: this.hass.formatEntityAttributeValue(stateObj, this.attribute, s),
|
||||||
label: !this.attribute
|
}));
|
||||||
? this.hass.formatEntityState(stateObj, s)
|
});
|
||||||
: this.hass.formatEntityAttributeValue(
|
|
||||||
stateObj,
|
const mode: "union" | "intersection" =
|
||||||
this.attribute,
|
this.combineMode || DEFAULT_COMBINE_MODE;
|
||||||
s
|
|
||||||
),
|
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}
|
.required=${this.required}
|
||||||
allow-custom-value
|
allow-custom-value
|
||||||
.hideStates=${this.selector.state?.hide_states}
|
.hideStates=${this.selector.state?.hide_states}
|
||||||
|
.combineMode=${this.selector.state?.combine_mode}
|
||||||
></ha-entity-state-picker>
|
></ha-entity-state-picker>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,20 @@ const showOptionalToggle = (field) =>
|
|||||||
!field.required &&
|
!field.required &&
|
||||||
!("boolean" in field.selector && field.default);
|
!("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"> {
|
interface Field extends Omit<HassService["fields"][string], "selector"> {
|
||||||
key: string;
|
key: string;
|
||||||
selector?: Selector;
|
selector?: Selector;
|
||||||
@ -244,7 +258,9 @@ export class HaServiceControl extends LitElement {
|
|||||||
).map(([key, value]) => ({
|
).map(([key, value]) => ({
|
||||||
key,
|
key,
|
||||||
...value,
|
...value,
|
||||||
selector: value.selector as Selector | undefined,
|
selector: (value.selector
|
||||||
|
? enrichSelector(value.selector)
|
||||||
|
: undefined) as Selector | undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const flatFields: Field[] = [];
|
const flatFields: Field[] = [];
|
||||||
@ -314,7 +330,12 @@ export class HaServiceControl extends LitElement {
|
|||||||
targetSelector
|
targetSelector
|
||||||
);
|
);
|
||||||
targetDevices.push(...expanded.devices);
|
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);
|
targetAreas.push(...expanded.areas);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -338,20 +359,29 @@ export class HaServiceControl extends LitElement {
|
|||||||
this.hass.entities,
|
this.hass.entities,
|
||||||
targetSelector
|
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);
|
targetDevices.push(...expanded.devices);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (targetDevices.length) {
|
if (targetDevices.length) {
|
||||||
targetDevices.forEach((deviceId) => {
|
targetDevices.forEach((deviceId) => {
|
||||||
targetEntities.push(
|
const expanded = expandDeviceTarget(
|
||||||
...expandDeviceTarget(
|
this.hass,
|
||||||
this.hass,
|
deviceId,
|
||||||
deviceId,
|
this.hass.entities,
|
||||||
this.hass.entities,
|
targetSelector
|
||||||
targetSelector
|
|
||||||
).entities
|
|
||||||
);
|
);
|
||||||
|
const primaryEntities = expanded.entities.filter(
|
||||||
|
(entityId) =>
|
||||||
|
!this.hass.entities[entityId]?.entity_category &&
|
||||||
|
!this.hass.entities[entityId]?.hidden
|
||||||
|
);
|
||||||
|
targetEntities.push(...primaryEntities);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return targetEntities;
|
return targetEntities;
|
||||||
|
@ -719,7 +719,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _entityRegMeetsFilter(entity: EntityRegistryDisplayEntry): boolean {
|
private _entityRegMeetsFilter(entity: EntityRegistryDisplayEntry): boolean {
|
||||||
if (entity.entity_category) {
|
if (entity.hidden || entity.entity_category) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,6 +396,7 @@ export interface StateSelector {
|
|||||||
entity_id?: string | string[];
|
entity_id?: string | string[];
|
||||||
attribute?: string;
|
attribute?: string;
|
||||||
hide_states?: string[];
|
hide_states?: string[];
|
||||||
|
combine_mode?: "union" | "intersection";
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user