diff --git a/src/data/lovelace/config/section.ts b/src/data/lovelace/config/section.ts index ad2c66e2aa..91ccd17e10 100644 --- a/src/data/lovelace/config/section.ts +++ b/src/data/lovelace/config/section.ts @@ -5,6 +5,7 @@ import type { LovelaceStrategyConfig } from "./strategy"; export interface LovelaceBaseSectionConfig { visibility?: Condition[]; column_span?: number; + hidden?: boolean; row_span?: number; /** * @deprecated Use heading card instead. diff --git a/src/panels/lovelace/sections/hui-grid-section.ts b/src/panels/lovelace/sections/hui-grid-section.ts index c30b3484b2..8eda22b628 100644 --- a/src/panels/lovelace/sections/hui-grid-section.ts +++ b/src/panels/lovelace/sections/hui-grid-section.ts @@ -80,7 +80,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement { const cardsConfig = this._config?.cards ?? []; - const editMode = Boolean(this.lovelace?.editMode && !this.isStrategy); + const editMode = Boolean(this.lovelace?.editMode); const sortableOptions = this.importOnly ? IMPORT_MODE_CARD_SORTABLE_OPTIONS @@ -88,7 +88,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement { return html` ${repeat( @@ -133,7 +134,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement { })}" .sortableData=${cardPath} > - ${editMode + ${editMode && !this.isStrategy ? html` { + let entitiesIds = + config.filter || config.exclude_entities ? Object.keys(hass.states) : []; + + if (config.exclude_entities) { + entitiesIds = entitiesIds.filter( + (entityId) => !config.exclude_entities!.includes(entityId) + ); + } + + if (config.filter) { + const filters = ensureArray(config.filter); + const entityFilters = filters.map((filter) => + generateEntityFilter(hass, filter) + ); + + entitiesIds = entitiesIds.filter((entityId) => + entityFilters.some((filter) => filter(entityId)) + ); + } + + if (config.include_entities) { + entitiesIds.push(...config.include_entities); + } + + if (config.order) { + entitiesIds.sort((a, b) => { + const aIndex = config.order!.indexOf(a); + const bIndex = config.order!.indexOf(b); + if (aIndex === -1 && bIndex === -1) return 0; + if (aIndex === -1) return 1; + if (bIndex === -1) return -1; + return aIndex - bIndex; + }); + } + + return entitiesIds; +}; + +@customElement("entities-filter-section-strategy") +export class EntitiesFilterSectionStrategy extends ReactiveElement { + static async generate( + config: EntitiesFilterSectionStrategyConfig, + hass: HomeAssistant + ): Promise { + const cards: LovelaceCardConfig[] = []; + let isEmpty = true; + + if (config.title) { + const headingCard: HeadingCardConfig = { + type: "heading", + heading: config.title, + heading_style: "title", + icon: config.icon, + }; + cards.push(headingCard); + } + + const entities = getEntities(hass, config); + + if (entities.length > 0) { + isEmpty = false; + for (const entityId of entities) { + const tileCard: TileCardConfig = { + type: "tile", + entity: entityId, + }; + cards.push(tileCard); + } + } + + if (config.groups) { + for (const group of config.groups) { + const groupEntities = getEntities(hass, group); + + if (groupEntities.length > 0) { + isEmpty = false; + if (group.title) { + cards.push({ + type: "heading", + heading: group.title, + heading_style: "subtitle", + icon: group.icon, + }); + } + + for (const entityId of groupEntities) { + const tileCard: TileCardConfig = { + type: "tile", + entity: entityId, + }; + cards.push(tileCard); + } + } + } + } + + if (isEmpty) { + cards.push({ + type: "markdown", + content: "No entities found.", + }); + } + + return { + type: "grid", + cards: cards, + hidden: isEmpty, + }; + } +} + +declare global { + interface HTMLElementTagNameMap { + "entities-filter-section-strategy": EntitiesFilterSectionStrategy; + } +} diff --git a/src/panels/lovelace/strategies/get-strategy.ts b/src/panels/lovelace/strategies/get-strategy.ts index f39b2b9302..b1e9e23bab 100644 --- a/src/panels/lovelace/strategies/get-strategy.ts +++ b/src/panels/lovelace/strategies/get-strategy.ts @@ -35,7 +35,10 @@ const STRATEGIES: Record> = { area: () => import("./area/area-view-strategy"), areas: () => import("./areas/areas-view-strategy"), }, - section: {}, + section: { + "entities-filter": () => + import("./entities-filter/entities-filter-section-strategy"), + }, }; export type LovelaceStrategyConfigType = "dashboard" | "view" | "section";