Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/common/areas/areas-floor-hierarchy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { AreaRegistryEntry } from "../../data/area_registry";
import type { FloorRegistryEntry } from "../../data/floor_registry";

export interface AreasFloorHierarchy {
floors: {
id: string;
areas: string[];
}[];
areas: string[];
}

export const getAreasFloorHierarchy = (
floors: FloorRegistryEntry[],
areas: AreaRegistryEntry[]
): AreasFloorHierarchy => {
const floorAreas = new Map<string, string[]>();
const unassignedAreas: string[] = [];

for (const area of areas) {
if (area.floor_id) {
if (!floorAreas.has(area.floor_id)) {
floorAreas.set(area.floor_id, []);
}
floorAreas.get(area.floor_id)!.push(area.area_id);
} else {
unassignedAreas.push(area.area_id);
}
}

const hierarchy: AreasFloorHierarchy = {
floors: floors.map((floor) => ({
id: floor.floor_id,
areas: floorAreas.get(floor.floor_id) || [],
})),
areas: unassignedAreas,
};

return hierarchy;
};

export const getAreasOrder = (hierarchy: AreasFloorHierarchy): string[] => {
const order: string[] = [];

for (const floor of hierarchy.floors) {
order.push(...floor.areas);
}
order.push(...hierarchy.areas);

return order;
};

export const getFloorOrder = (hierarchy: AreasFloorHierarchy): string[] =>
hierarchy.floors.map((floor) => floor.id);
86 changes: 37 additions & 49 deletions src/data/area_floor.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getAreasFloorHierarchy } from "../common/areas/areas-floor-hierarchy";
import { computeAreaName } from "../common/entity/compute_area_name";
import { computeDomain } from "../common/entity/compute_domain";
import { computeFloorName } from "../common/entity/compute_floor_name";
Expand All @@ -12,11 +13,7 @@ import {
} from "./device_registry";
import type { HaEntityPickerEntityFilterFunc } from "./entity";
import type { EntityRegistryDisplayEntry } from "./entity_registry";
import {
floorCompare,
getFloorAreaLookup,
type FloorRegistryEntry,
} from "./floor_registry";
import type { FloorRegistryEntry } from "./floor_registry";

export interface FloorComboBoxItem extends PickerComboBoxItem {
type: "floor" | "area";
Expand Down Expand Up @@ -182,68 +179,59 @@ export const getAreasAndFloors = (
);
}

const floorAreaLookup = getFloorAreaLookup(outputAreas);
const unassignedAreas = Object.values(outputAreas).filter(
(area) => !area.floor_id || !floorAreaLookup[area.floor_id]
);

const compare = floorCompare(haFloors);

// @ts-ignore
const floorAreaEntries: [
FloorRegistryEntry | undefined,
AreaRegistryEntry[],
][] = Object.entries(floorAreaLookup)
.map(([floorId, floorAreas]) => {
const floor = floors.find((fl) => fl.floor_id === floorId)!;
return [floor, floorAreas] as const;
})
.sort(([floorA], [floorB]) => compare(floorA.floor_id, floorB.floor_id));
const hierarchy = getAreasFloorHierarchy(floors, outputAreas);

const items: FloorComboBoxItem[] = [];

floorAreaEntries.forEach(([floor, floorAreas]) => {
if (floor) {
const floorName = computeFloorName(floor);
hierarchy.floors.forEach((f) => {
const floor = haFloors[f.id];
const floorAreas = f.areas.map((areaId) => haAreas[areaId]);

const areaSearchLabels = floorAreas
.map((area) => {
const areaName = computeAreaName(area) || area.area_id;
return [area.area_id, areaName, ...area.aliases];
})
.flat();
const floorName = computeFloorName(floor);

const areaSearchLabels = floorAreas
.map((area) => {
const areaName = computeAreaName(area);
return [area.area_id, ...(areaName ? [areaName] : []), ...area.aliases];
})
.flat();

items.push({
id: formatId({ id: floor.floor_id, type: "floor" }),
type: "floor",
primary: floorName,
floor: floor,
icon: floor.icon || undefined,
search_labels: [
floor.floor_id,
floorName,
...floor.aliases,
...areaSearchLabels,
],
});

items.push({
id: formatId({ id: floor.floor_id, type: "floor" }),
type: "floor",
primary: floorName,
floor: floor,
icon: floor.icon || undefined,
search_labels: [
floor.floor_id,
floorName,
...floor.aliases,
...areaSearchLabels,
],
});
}
items.push(
...floorAreas.map((area) => {
const areaName = computeAreaName(area) || area.area_id;
const areaName = computeAreaName(area);
return {
id: formatId({ id: area.area_id, type: "area" }),
type: "area" as const,
primary: areaName,
primary: areaName || area.area_id,
area: area,
icon: area.icon || undefined,
search_labels: [area.area_id, areaName, ...area.aliases],
search_labels: [
area.area_id,
...(areaName ? [areaName] : []),
...area.aliases,
],
};
})
);
});

items.push(
...unassignedAreas.map((area) => {
...hierarchy.areas.map((areaId) => {
const area = haAreas[areaId];
const areaName = computeAreaName(area) || area.area_id;
return {
id: formatId({ id: area.area_id, type: "area" }),
Expand Down
9 changes: 9 additions & 0 deletions src/data/area_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ export const deleteAreaRegistryEntry = (hass: HomeAssistant, areaId: string) =>
area_id: areaId,
});

export const reorderAreaRegistryEntries = (
hass: HomeAssistant,
areaIds: string[]
) =>
hass.callWS({
type: "config/area_registry/reorder",
area_ids: areaIds,
});

export const getAreaEntityLookup = (
entities: EntityRegistryEntry[]
): AreaEntityLookup => {
Expand Down
9 changes: 9 additions & 0 deletions src/data/floor_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ export const deleteFloorRegistryEntry = (
floor_id: floorId,
});

export const reorderFloorRegistryEntries = (
hass: HomeAssistant,
floorIds: string[]
) =>
hass.callWS({
type: "config/floor_registry/reorder",
floor_ids: floorIds,
});

export const getFloorAreaLookup = (
areas: AreaRegistryEntry[]
): FloorAreaLookup => {
Expand Down
25 changes: 11 additions & 14 deletions src/panels/climate/strategies/climate-view-strategy.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { ReactiveElement } from "lit";
import { customElement } from "lit/decorators";
import { getAreasFloorHierarchy } from "../../../common/areas/areas-floor-hierarchy";
import {
findEntities,
generateEntityFilter,
type EntityFilter,
} from "../../../common/entity/entity_filter";
import { floorDefaultIcon } from "../../../components/ha-floor-icon";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import type { LovelaceSectionRawConfig } from "../../../data/lovelace/config/section";
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import type { HomeAssistant } from "../../../types";
import {
computeAreaTileCardConfig,
getAreas,
getFloors,
} from "../../lovelace/strategies/areas/helpers/areas-strategy-helper";
import { getHomeStructure } from "../../lovelace/strategies/home/helpers/home-structure";
import { floorDefaultIcon } from "../../../components/ha-floor-icon";
import { computeAreaTileCardConfig } from "../../lovelace/strategies/areas/helpers/areas-strategy-helper";

export interface ClimateViewStrategyConfig {
type: "climate";
Expand Down Expand Up @@ -139,9 +135,9 @@ export class ClimateViewStrategy extends ReactiveElement {
_config: ClimateViewStrategyConfig,
hass: HomeAssistant
): Promise<LovelaceViewConfig> {
const areas = getAreas(hass.areas);
const floors = getFloors(hass.floors);
const home = getHomeStructure(floors, areas);
const areas = Object.values(hass.areas);
const floors = Object.values(hass.floors);
const hierarchy = getAreasFloorHierarchy(floors, areas);

const sections: LovelaceSectionRawConfig[] = [];

Expand All @@ -153,10 +149,11 @@ export class ClimateViewStrategy extends ReactiveElement {

const entities = findEntities(allEntities, climateFilters);

const floorCount = home.floors.length + (home.areas.length ? 1 : 0);
const floorCount =
hierarchy.floors.length + (hierarchy.areas.length ? 1 : 0);

// Process floors
for (const floorStructure of home.floors) {
for (const floorStructure of hierarchy.floors) {
const floorId = floorStructure.id;
const areaIds = floorStructure.areas;
const floor = hass.floors[floorId];
Expand Down Expand Up @@ -185,7 +182,7 @@ export class ClimateViewStrategy extends ReactiveElement {
}

// Process unassigned areas
if (home.areas.length > 0) {
if (hierarchy.areas.length > 0) {
const section: LovelaceSectionRawConfig = {
type: "grid",
column_span: 2,
Expand All @@ -200,7 +197,7 @@ export class ClimateViewStrategy extends ReactiveElement {
],
};

const areaCards = processAreasForClimate(home.areas, hass, entities);
const areaCards = processAreasForClimate(hierarchy.areas, hass, entities);

if (areaCards.length > 0) {
section.cards!.push(...areaCards);
Expand Down
Loading
Loading