Move exclude entities config to area card (#25909)

This commit is contained in:
Paul Bottein 2025-06-25 16:18:47 +02:00 committed by GitHub
parent 3ab6a02994
commit af149dcfab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 77 additions and 38 deletions

View File

@ -18,6 +18,7 @@ import type { AreaRegistryEntry } from "../../../data/area_registry";
import { forwardHaptic } from "../../../data/haptics";
import { computeCssVariable } from "../../../resources/css-variables";
import type { HomeAssistant } from "../../../types";
import type { AreaCardFeatureContext } from "../cards/hui-area-card";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles";
import type {
@ -86,8 +87,8 @@ export const supportsAreaControlsCardFeature = (
export const getAreaControlEntities = (
controls: AreaControl[],
areaId: string,
hass: HomeAssistant,
excludeEntities: string[] = []
excludeEntities: string[] | undefined,
hass: HomeAssistant
): Record<AreaControl, string[]> =>
controls.reduce(
(acc, control) => {
@ -99,7 +100,7 @@ export const getAreaControlEntities = (
});
acc[control] = Object.keys(hass.entities).filter(
(entityId) => filter(entityId) && !excludeEntities.includes(entityId)
(entityId) => filter(entityId) && !excludeEntities?.includes(entityId)
);
return acc;
},
@ -115,7 +116,7 @@ class HuiAreaControlsCardFeature
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@property({ attribute: false }) public context?: AreaCardFeatureContext;
@property({ attribute: false })
public position?: LovelaceCardFeaturePosition;
@ -168,7 +169,7 @@ class HuiAreaControlsCardFeature
const controlEntities = this._controlEntities(
this._controls,
this.context.area_id,
this._config.exclude_entities,
this.context.exclude_entities,
this.hass!.entities,
this.hass!.devices,
this.hass!.areas
@ -192,7 +193,7 @@ class HuiAreaControlsCardFeature
_entities: HomeAssistant["entities"],
_devices: HomeAssistant["devices"],
_areas: HomeAssistant["areas"]
) => getAreaControlEntities(controls, areaId, this.hass!, excludeEntities)
) => getAreaControlEntities(controls, areaId, excludeEntities, this.hass!)
);
protected render() {
@ -209,7 +210,7 @@ class HuiAreaControlsCardFeature
const controlEntities = this._controlEntities(
this._controls,
this.context.area_id!,
this._config.exclude_entities,
this.context.exclude_entities,
this.hass!.entities,
this.hass!.devices,
this.hass!.areas

View File

@ -179,7 +179,6 @@ export type AreaControl = (typeof AREA_CONTROLS)[number];
export interface AreaControlsCardFeatureConfig {
type: "area-controls";
controls?: AreaControl[];
exclude_entities?: string[];
}
export type LovelaceCardFeaturePosition = "bottom" | "inline";

View File

@ -53,6 +53,10 @@ export const DEVICE_CLASSES = {
binary_sensor: ["motion", "moisture"],
};
export interface AreaCardFeatureContext extends LovelaceCardFeatureContext {
exclude_entities?: string[];
}
@customElement("hui-area-card")
export class HuiAreaCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass!: HomeAssistant;
@ -61,7 +65,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
@state() private _config?: AreaCardConfig;
@state() private _featureContext: LovelaceCardFeatureContext = {};
@state() private _featureContext: AreaCardFeatureContext = {};
private _ratio: {
w: number;
@ -87,6 +91,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
this._featureContext = {
area_id: config.area,
exclude_entities: config.exclude_entities,
};
}
@ -166,7 +171,8 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
(
entities: HomeAssistant["entities"],
areaId: string,
sensorClasses: string[]
sensorClasses: string[],
excludeEntities?: string[]
): Map<string, string[]> => {
const sensorFilter = generateEntityFilter(this.hass, {
area: areaId,
@ -174,7 +180,10 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
domain: "sensor",
device_class: sensorClasses,
});
const entityIds = Object.keys(entities).filter(sensorFilter);
const entityIds = Object.keys(entities).filter(
(id) => sensorFilter(id) && !excludeEntities?.includes(id)
);
return this._groupEntitiesByDeviceClass(entityIds);
}
);
@ -183,7 +192,8 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
(
entities: HomeAssistant["entities"],
areaId: string,
binarySensorClasses: string[]
binarySensorClasses: string[],
excludeEntities?: string[]
): Map<string, string[]> => {
const binarySensorFilter = generateEntityFilter(this.hass, {
area: areaId,
@ -191,7 +201,11 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
domain: "binary_sensor",
device_class: binarySensorClasses,
});
const entityIds = Object.keys(entities).filter(binarySensorFilter);
const entityIds = Object.keys(entities).filter(
(id) => binarySensorFilter(id) && !excludeEntities?.includes(id)
);
return this._groupEntitiesByDeviceClass(entityIds);
}
);
@ -215,13 +229,15 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
const areaId = this._config?.area;
const area = areaId ? this.hass.areas[areaId] : undefined;
const alertClasses = this._config?.alert_classes;
const excludeEntities = this._config?.exclude_entities;
if (!area || !alertClasses) {
return [];
}
const groupedEntities = this._groupedBinarySensorEntityIds(
this.hass.entities,
area.area_id,
alertClasses
alertClasses,
excludeEntities
);
return (
@ -286,6 +302,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
const areaId = this._config?.area;
const area = areaId ? this.hass.areas[areaId] : undefined;
const sensorClasses = this._config?.sensor_classes;
const excludeEntities = this._config?.exclude_entities;
if (!area || !sensorClasses) {
return undefined;
}
@ -293,7 +310,8 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
const groupedEntities = this._groupedSensorEntityIds(
this.hass.entities,
area.area_id,
sensorClasses
sensorClasses,
excludeEntities
);
const sensorStates = sensorClasses

View File

@ -117,6 +117,7 @@ export interface AreaCardConfig extends LovelaceCardConfig {
alert_classes?: string[];
features?: LovelaceCardFeatureConfig[];
features_position?: LovelaceCardFeaturePosition;
exclude_entities?: string[];
}
export interface ButtonCardConfig extends LovelaceCardConfig {

View File

@ -32,7 +32,10 @@ import type {
LovelaceCardFeatureConfig,
LovelaceCardFeatureContext,
} from "../../card-features/types";
import { DEVICE_CLASSES } from "../../cards/hui-area-card";
import {
DEVICE_CLASSES,
type AreaCardFeatureContext,
} from "../../cards/hui-area-card";
import type { AreaCardConfig } from "../../cards/types";
import type { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
@ -55,6 +58,7 @@ const cardConfigStruct = assign(
features: optional(array(any())),
features_position: optional(enums(["bottom", "inline"])),
aspect_ratio: optional(string()),
exclude_entities: optional(array(string())),
})
);
@ -69,11 +73,7 @@ export class HuiAreaCardEditor
@state() private _numericDeviceClasses?: string[];
private _featureContext = memoizeOne(
(areaId?: string): LovelaceCardFeatureContext => ({
area_id: areaId,
})
);
@state() private _featureContext: AreaCardFeatureContext = {};
private _schema = memoizeOne(
(
@ -174,7 +174,10 @@ export class HuiAreaCardEditor
);
private _binaryClassesForArea = memoizeOne(
(area: string | undefined): string[] => {
(
area: string | undefined,
excludeEntities: string[] | undefined
): string[] => {
if (!area) {
return [];
}
@ -186,7 +189,9 @@ export class HuiAreaCardEditor
});
const classes = Object.keys(this.hass!.entities)
.filter(binarySensorFilter)
.filter(
(id) => binarySensorFilter(id) && !excludeEntities?.includes(id)
)
.map((id) => this.hass!.states[id]?.attributes.device_class)
.filter((c): c is string => Boolean(c));
@ -195,7 +200,11 @@ export class HuiAreaCardEditor
);
private _sensorClassesForArea = memoizeOne(
(area: string | undefined, numericDeviceClasses?: string[]): string[] => {
(
area: string | undefined,
excludeEntities: string[] | undefined,
numericDeviceClasses: string[] | undefined
): string[] => {
if (!area) {
return [];
}
@ -208,7 +217,7 @@ export class HuiAreaCardEditor
});
const classes = Object.keys(this.hass!.entities)
.filter(sensorFilter)
.filter((id) => sensorFilter(id) && !excludeEntities?.includes(id))
.map((id) => this.hass!.states[id]?.attributes.device_class)
.filter((c): c is string => Boolean(c));
@ -257,6 +266,11 @@ export class HuiAreaCardEditor
display_type: displayType,
};
delete this._config.show_camera;
this._featureContext = {
area_id: config.area,
exclude_entities: config.exclude_entities,
};
}
protected async updated() {
@ -306,11 +320,13 @@ export class HuiAreaCardEditor
return nothing;
}
const areaId = this._config!.area;
const possibleBinaryClasses = this._binaryClassesForArea(this._config.area);
const possibleBinaryClasses = this._binaryClassesForArea(
this._config.area,
this._config.exclude_entities
);
const possibleSensorClasses = this._sensorClassesForArea(
this._config.area,
this._config.exclude_entities,
this._numericDeviceClasses
);
const binarySelectOptions = this._buildBinaryOptions(
@ -347,8 +363,9 @@ export class HuiAreaCardEditor
...this._config,
};
const featureContext = this._featureContext(areaId);
const hasCompatibleFeatures = this._hasCompatibleFeatures(featureContext);
const hasCompatibleFeatures = this._hasCompatibleFeatures(
this._featureContext
);
return html`
<ha-form
@ -381,7 +398,7 @@ export class HuiAreaCardEditor
: nothing}
<hui-card-features-editor
.hass=${this.hass}
.context=${featureContext}
.context=${this._featureContext}
.features=${this._config!.features ?? []}
@features-changed=${this._featuresChanged}
@edit-detail-element=${this._editDetailElement}
@ -428,12 +445,11 @@ export class HuiAreaCardEditor
private _editDetailElement(ev: HASSDomEvent<EditDetailElementEvent>): void {
const index = ev.detail.subElementConfig.index;
const config = this._config!.features![index!];
const featureContext = this._featureContext(this._config!.area);
fireEvent(this, "edit-sub-element", {
config: config,
saveConfig: (newConfig) => this._updateFeature(index!, newConfig),
context: featureContext,
context: this._featureContext,
type: "feature",
} as EditSubElementEvent<
LovelaceCardFeatureConfig,

View File

@ -17,8 +17,8 @@ import {
AREA_CONTROLS,
type AreaControl,
type AreaControlsCardFeatureConfig,
type LovelaceCardFeatureContext,
} from "../../card-features/types";
import type { AreaCardFeatureContext } from "../../cards/hui-area-card";
import type { LovelaceCardFeatureEditor } from "../../types";
type AreaControlsCardFeatureData = AreaControlsCardFeatureConfig & {
@ -32,7 +32,7 @@ export class HuiAreaControlsCardFeatureEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@property({ attribute: false }) public context?: AreaCardFeatureContext;
@state() private _config?: AreaControlsCardFeatureConfig;
@ -78,6 +78,7 @@ export class HuiAreaControlsCardFeatureEditor
private _supportedControls = memoizeOne(
(
areaId: string,
excludeEntities: string[] | undefined,
// needed to update memoized function when entities, devices or areas change
_entities: HomeAssistant["entities"],
_devices: HomeAssistant["devices"],
@ -89,6 +90,7 @@ export class HuiAreaControlsCardFeatureEditor
const controlEntities = getAreaControlEntities(
AREA_CONTROLS as unknown as AreaControl[],
areaId,
excludeEntities,
this.hass!
);
return (
@ -104,6 +106,7 @@ export class HuiAreaControlsCardFeatureEditor
const supportedControls = this._supportedControls(
this.context.area_id,
this.context.exclude_entities,
this.hass.entities,
this.hass.devices,
this.hass.areas
@ -148,6 +151,7 @@ export class HuiAreaControlsCardFeatureEditor
if (customize_controls && !config.controls) {
config.controls = this._supportedControls(
this.context!.area_id!,
this.context!.exclude_entities,
this.hass!.entities,
this.hass!.devices,
this.hass!.areas

View File

@ -83,8 +83,8 @@ export class AreasOverviewViewStrategy extends ReactiveElement {
const controlEntities = getAreaControlEntities(
controls,
area.area_id,
hass,
hiddenEntities
hiddenEntities,
hass
);
const filteredControls = controls.filter(
@ -105,12 +105,12 @@ export class AreasOverviewViewStrategy extends ReactiveElement {
"occupancy",
"presence",
],
exclude_entities: hiddenEntities,
features: filteredControls.length
? [
{
type: "area-controls",
controls: filteredControls,
exclude_entities: hiddenEntities,
},
]
: [],