Use area card in area strategy (#25879)

* Bring area card to area strategy

* Add device classes

* Use subview
This commit is contained in:
Paul Bottein 2025-06-24 13:46:12 +02:00 committed by GitHub
parent 876e36b4e0
commit 641a2eb77c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 92 additions and 64 deletions

View File

@ -30,6 +30,22 @@ export const floorDefaultIconPath = (
return mdiHome; return mdiHome;
}; };
export const floorDefaultIcon = (floor: Pick<FloorRegistryEntry, "level">) => {
switch (floor.level) {
case 0:
return "mdi:home-floor-0";
case 1:
return "mdi:home-floor-1";
case 2:
return "mdi:home-floor-2";
case 3:
return "mdi:home-floor-3";
case -1:
return "mdi:home-floor-negative-1";
}
return "mdi:home";
};
@customElement("ha-floor-icon") @customElement("ha-floor-icon")
export class HaFloorIcon extends LitElement { export class HaFloorIcon extends LitElement {
@property({ attribute: false }) public floor!: Pick< @property({ attribute: false }) public floor!: Pick<

View File

@ -191,12 +191,6 @@ export class AreaViewStrategy extends ReactiveElement {
type: "sections", type: "sections",
header: { header: {
badges_position: "bottom", badges_position: "bottom",
layout: "responsive",
card: {
type: "markdown",
text_only: true,
content: `## ${area.name}`,
},
}, },
max_columns: maxColumns, max_columns: maxColumns,
sections: sections, sections: sections,

View File

@ -66,6 +66,7 @@ export class AreasDashboardStrategy extends ReactiveElement {
return { return {
title: area.name, title: area.name,
path: path, path: path,
subview: true,
strategy: { strategy: {
type: "area", type: "area",
area: area.area_id, area: area.area_id,

View File

@ -1,15 +1,15 @@
import { ReactiveElement } from "lit"; import { ReactiveElement } from "lit";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import { stringCompare } from "../../../../common/string/compare";
import { floorDefaultIcon } from "../../../../components/ha-floor-icon";
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { getAreaControlEntities } from "../../card-features/hui-area-controls-card-feature";
import type { AreaControl } from "../../card-features/types";
import type { AreaCardConfig, HeadingCardConfig } from "../../cards/types";
import type { EntitiesDisplay } from "./area-view-strategy"; import type { EntitiesDisplay } from "./area-view-strategy";
import { import { computeAreaPath, getAreas } from "./helpers/areas-strategy-helper";
computeAreaPath,
computeAreaTileCardConfig,
getAreaGroupedEntities,
getAreas,
} from "./helpers/areas-strategy-helper";
interface AreaOptions { interface AreaOptions {
groups_options?: Record<string, EntitiesDisplay>; groups_options?: Record<string, EntitiesDisplay>;
@ -36,71 +36,88 @@ export class AreasOverviewViewStrategy extends ReactiveElement {
config.areas_display?.order config.areas_display?.order
); );
const areaSections = areas const floors = Object.values(hass.floors);
.map<LovelaceSectionConfig | undefined>((area) => { floors.sort((floorA, floorB) => {
if (floorA.level !== floorB.level) {
return (floorA.level ?? 0) - (floorB.level ?? 0);
}
return stringCompare(floorA.name, floorB.name);
});
const floorSections = [
...floors,
{ floor_id: "default", name: "Default", level: null, icon: null },
]
.map<LovelaceSectionConfig | undefined>((floor) => {
const areasInFloors = areas.filter(
(area) =>
area.floor_id === floor.floor_id ||
(!area.floor_id && floor.floor_id === "default")
);
if (areasInFloors.length === 0) {
return undefined;
}
const areasCards = areasInFloors.map<AreaCardConfig>((area) => {
const path = computeAreaPath(area.area_id); const path = computeAreaPath(area.area_id);
const areaConfig = config.areas_options?.[area.area_id]; const controls: AreaControl[] = ["light", "fan"];
const controlEntities = getAreaControlEntities(
const groups = getAreaGroupedEntities( controls,
area.area_id, area.area_id,
hass, hass
areaConfig?.groups_options
); );
const entities = [ const filteredControls = controls.filter(
...groups.lights, (control) => controlEntities[control].length > 0
...groups.covers, );
...groups.climate,
...groups.media_players,
...groups.security,
...groups.actions,
...groups.others,
];
const computeTileCard = computeAreaTileCardConfig(hass, area.name);
return { return {
type: "grid", type: "area",
cards: [ area: area.area_id,
{ display_type: "compact",
type: "heading", sensor_classes: ["temperature", "humidity"],
heading: area.name, alert_classes: [
icon: area.icon || undefined, "water_leak",
badges: [ "smoke",
...(area.temperature_entity_id "gas",
? [{ entity: area.temperature_entity_id }] "co",
: []), "motion",
...(area.humidity_entity_id "occupancy",
? [{ entity: area.humidity_entity_id }] "presence",
: []),
], ],
tap_action: { features: filteredControls.length
action: "navigate", ? [
{
type: "area-controls",
controls: filteredControls,
},
]
: [],
navigation_path: path, navigation_path: path,
}, };
}, });
...(entities.length
? entities.map(computeTileCard) const headingCard: HeadingCardConfig = {
: [ type: "heading",
{ heading_style: "title",
type: "markdown", heading: floor.name,
content: hass.localize( icon: floor.icon || floorDefaultIcon(floor),
"ui.panel.lovelace.strategy.areas.no_entities" };
),
}, return {
]), max_columns: 3,
], type: "grid",
cards: [headingCard, ...areasCards],
}; };
}) })
.filter( ?.filter((section) => section !== undefined);
(section): section is LovelaceSectionConfig => section !== undefined
);
return { return {
type: "sections", type: "sections",
max_columns: 3, max_columns: 3,
sections: areaSections, sections: floorSections || [],
}; };
} }
} }