diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts index ceb789d02c..441684a477 100644 --- a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -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 => 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 diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index 4b74481431..9ded542fe3 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -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"; diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index dc2844e731..1f545e3a78 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -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 => { 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 => { 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 diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 38c58a6bed..0902cf853e 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -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 { diff --git a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts index e256b21956..d0c20791e2 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts @@ -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` ): 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, diff --git a/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts index 819ce72dfb..18089a26b7 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts @@ -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 diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 3ff37a0b7c..99feb34b5a 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -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, }, ] : [],