mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Add entity filter section strategy
This commit is contained in:
parent
91e9836423
commit
99cd67c857
@ -5,6 +5,7 @@ import type { LovelaceStrategyConfig } from "./strategy";
|
|||||||
export interface LovelaceBaseSectionConfig {
|
export interface LovelaceBaseSectionConfig {
|
||||||
visibility?: Condition[];
|
visibility?: Condition[];
|
||||||
column_span?: number;
|
column_span?: number;
|
||||||
|
hidden?: boolean;
|
||||||
row_span?: number;
|
row_span?: number;
|
||||||
/**
|
/**
|
||||||
* @deprecated Use heading card instead.
|
* @deprecated Use heading card instead.
|
||||||
|
@ -80,7 +80,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
|
|||||||
|
|
||||||
const cardsConfig = this._config?.cards ?? [];
|
const cardsConfig = this._config?.cards ?? [];
|
||||||
|
|
||||||
const editMode = Boolean(this.lovelace?.editMode && !this.isStrategy);
|
const editMode = Boolean(this.lovelace?.editMode);
|
||||||
|
|
||||||
const sortableOptions = this.importOnly
|
const sortableOptions = this.importOnly
|
||||||
? IMPORT_MODE_CARD_SORTABLE_OPTIONS
|
? IMPORT_MODE_CARD_SORTABLE_OPTIONS
|
||||||
@ -88,7 +88,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-sortable
|
<ha-sortable
|
||||||
.disabled=${!editMode}
|
.disabled=${!(editMode && !this.isStrategy)}
|
||||||
@drag-start=${this._dragStart}
|
@drag-start=${this._dragStart}
|
||||||
@drag-end=${this._dragEnd}
|
@drag-end=${this._dragEnd}
|
||||||
draggable-selector=".card"
|
draggable-selector=".card"
|
||||||
@ -103,6 +103,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
|
|||||||
class="container ${classMap({
|
class="container ${classMap({
|
||||||
"edit-mode": editMode,
|
"edit-mode": editMode,
|
||||||
"import-only": this.importOnly,
|
"import-only": this.importOnly,
|
||||||
|
"is-strategy": this.isStrategy,
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
${repeat(
|
${repeat(
|
||||||
@ -133,7 +134,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
|
|||||||
})}"
|
})}"
|
||||||
.sortableData=${cardPath}
|
.sortableData=${cardPath}
|
||||||
>
|
>
|
||||||
${editMode
|
${editMode && !this.isStrategy
|
||||||
? html`
|
? html`
|
||||||
<hui-card-edit-mode
|
<hui-card-edit-mode
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -151,7 +152,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
${editMode && !this.importOnly
|
${editMode && !this.importOnly && !this.isStrategy
|
||||||
? html`
|
? html`
|
||||||
<button
|
<button
|
||||||
class="add"
|
class="add"
|
||||||
@ -246,6 +247,10 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
|
|||||||
min-height: var(--row-height);
|
min-height: var(--row-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container.edit-mode.is-strategy {
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
.container.import-only {
|
.container.import-only {
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
@ -55,6 +55,8 @@ export class HuiSection extends ReactiveElement {
|
|||||||
|
|
||||||
@state() private _cards: HuiCard[] = [];
|
@state() private _cards: HuiCard[] = [];
|
||||||
|
|
||||||
|
@state() private _config?: LovelaceSectionConfig;
|
||||||
|
|
||||||
private _layoutElementType?: string;
|
private _layoutElementType?: string;
|
||||||
|
|
||||||
private _layoutElement?: LovelaceSectionElement;
|
private _layoutElement?: LovelaceSectionElement;
|
||||||
@ -195,6 +197,8 @@ export class HuiSection extends ReactiveElement {
|
|||||||
type: sectionConfig.type || DEFAULT_SECTION_LAYOUT,
|
type: sectionConfig.type || DEFAULT_SECTION_LAYOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._config = sectionConfig;
|
||||||
|
|
||||||
// Create a new layout element if necessary.
|
// Create a new layout element if necessary.
|
||||||
let addLayoutElement = false;
|
let addLayoutElement = false;
|
||||||
|
|
||||||
@ -223,14 +227,16 @@ export class HuiSection extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _updateElement(forceVisible?: boolean) {
|
private _updateElement(forceVisible?: boolean) {
|
||||||
if (!this._layoutElement) {
|
if (!this._layoutElement || !this._config) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const visible =
|
const visible =
|
||||||
forceVisible ||
|
forceVisible ||
|
||||||
this.preview ||
|
this.preview ||
|
||||||
!this.config.visibility ||
|
(!this._config.hidden &&
|
||||||
checkConditionsMet(this.config.visibility, this.hass);
|
(!this.config.visibility ||
|
||||||
|
checkConditionsMet(this.config.visibility, this.hass)));
|
||||||
|
|
||||||
if (this.hidden !== !visible) {
|
if (this.hidden !== !visible) {
|
||||||
this.style.setProperty("display", visible ? "" : "none");
|
this.style.setProperty("display", visible ? "" : "none");
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
import { ReactiveElement } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import {
|
||||||
|
generateEntityFilter,
|
||||||
|
type EntityFilter,
|
||||||
|
} from "../../../../common/entity/entity_filter";
|
||||||
|
import type { TileCardConfig, HeadingCardConfig } from "../../cards/types";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
|
||||||
|
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
|
|
||||||
|
interface EntityFilterConfig {
|
||||||
|
title?: string;
|
||||||
|
icon?: string;
|
||||||
|
filter?: EntityFilter | EntityFilter[];
|
||||||
|
include_entities?: string[];
|
||||||
|
exclude_entities?: string[];
|
||||||
|
order?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EntitiesFilterSectionStrategyConfig = EntityFilterConfig & {
|
||||||
|
type: "entities-filter";
|
||||||
|
groups?: EntityFilterConfig[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEntities = (hass: HomeAssistant, config: EntityFilterConfig) => {
|
||||||
|
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<LovelaceSectionConfig> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,10 @@ const STRATEGIES: Record<LovelaceStrategyConfigType, Record<string, any>> = {
|
|||||||
area: () => import("./area/area-view-strategy"),
|
area: () => import("./area/area-view-strategy"),
|
||||||
areas: () => import("./areas/areas-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";
|
export type LovelaceStrategyConfigType = "dashboard" | "view" | "section";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user