Skip to content

Commit e066080

Browse files
piitayatimmo001
authored andcommitted
Display entities without area in summary dashboard (#27777)
* Add support for no area, no floor and no device in entity filter * Display entities without area in summary dashboard
1 parent 48e70e9 commit e066080

File tree

8 files changed

+278
-29
lines changed

8 files changed

+278
-29
lines changed

src/common/entity/entity_filter.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,28 @@ type EntityCategory = "none" | "config" | "diagnostic";
99
export interface EntityFilter {
1010
domain?: string | string[];
1111
device_class?: string | string[];
12-
device?: string | string[];
13-
area?: string | string[];
14-
floor?: string | string[];
12+
device?: string | null | (string | null)[];
13+
area?: string | null | (string | null)[];
14+
floor?: string | null | (string | null)[];
1515
label?: string | string[];
1616
entity_category?: EntityCategory | EntityCategory[];
1717
hidden_platform?: string | string[];
1818
}
1919

2020
export type EntityFilterFunc = (entityId: string) => boolean;
2121

22+
const normalizeFilterArray = <T>(
23+
value: T | null | T[] | (T | null)[] | undefined
24+
): Set<T | null> | undefined => {
25+
if (value === undefined) {
26+
return undefined;
27+
}
28+
if (value === null) {
29+
return new Set([null]);
30+
}
31+
return new Set(ensureArray(value));
32+
};
33+
2234
export const generateEntityFilter = (
2335
hass: HomeAssistant,
2436
filter: EntityFilter
@@ -29,11 +41,9 @@ export const generateEntityFilter = (
2941
const deviceClasses = filter.device_class
3042
? new Set(ensureArray(filter.device_class))
3143
: undefined;
32-
const floors = filter.floor ? new Set(ensureArray(filter.floor)) : undefined;
33-
const areas = filter.area ? new Set(ensureArray(filter.area)) : undefined;
34-
const devices = filter.device
35-
? new Set(ensureArray(filter.device))
36-
: undefined;
44+
const floors = normalizeFilterArray(filter.floor);
45+
const areas = normalizeFilterArray(filter.area);
46+
const devices = normalizeFilterArray(filter.device);
3747
const entityCategories = filter.entity_category
3848
? new Set(ensureArray(filter.entity_category))
3949
: undefined;
@@ -73,23 +83,20 @@ export const generateEntityFilter = (
7383
}
7484

7585
if (floors) {
76-
if (!floor || !floors.has(floor.floor_id)) {
86+
const floorId = floor?.floor_id ?? null;
87+
if (!floors.has(floorId)) {
7788
return false;
7889
}
7990
}
8091
if (areas) {
81-
if (!area) {
82-
return false;
83-
}
84-
if (!areas.has(area.area_id)) {
92+
const areaId = area?.area_id ?? null;
93+
if (!areas.has(areaId)) {
8594
return false;
8695
}
8796
}
8897
if (devices) {
89-
if (!device) {
90-
return false;
91-
}
92-
if (!devices.has(device.id)) {
98+
const deviceId = device?.id ?? null;
99+
if (!devices.has(deviceId)) {
93100
return false;
94101
}
95102
}

src/panels/climate/strategies/climate-view-strategy.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,24 @@ const processAreasForClimate = (
115115
return cards;
116116
};
117117

118+
const processUnassignedEntities = (
119+
hass: HomeAssistant,
120+
entities: string[]
121+
): LovelaceCardConfig[] => {
122+
const unassignedFilter = generateEntityFilter(hass, {
123+
area: null,
124+
});
125+
const unassignedEntities = entities.filter(unassignedFilter);
126+
const areaCards: LovelaceCardConfig[] = [];
127+
const computeTileCard = computeAreaTileCardConfig(hass, "", true);
128+
129+
for (const entityId of unassignedEntities) {
130+
areaCards.push(computeTileCard(entityId));
131+
}
132+
133+
return areaCards;
134+
};
135+
118136
@customElement("climate-view-strategy")
119137
export class ClimateViewStrategy extends ReactiveElement {
120138
static async generate(
@@ -190,10 +208,33 @@ export class ClimateViewStrategy extends ReactiveElement {
190208
}
191209
}
192210

211+
// Process unassigned entities
212+
const unassignedCards = processUnassignedEntities(hass, entities);
213+
214+
if (unassignedCards.length > 0) {
215+
const section: LovelaceSectionRawConfig = {
216+
type: "grid",
217+
column_span: 2,
218+
cards: [
219+
{
220+
type: "heading",
221+
heading:
222+
sections.length > 0
223+
? hass.localize(
224+
"ui.panel.lovelace.strategy.climate.other_devices"
225+
)
226+
: hass.localize("ui.panel.lovelace.strategy.climate.devices"),
227+
},
228+
...unassignedCards,
229+
],
230+
};
231+
sections.push(section);
232+
}
233+
193234
return {
194235
type: "sections",
195236
max_columns: 2,
196-
sections: sections || [],
237+
sections: sections,
197238
};
198239
}
199240
}

src/panels/light/strategies/light-view-strategy.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,24 @@ const processAreasForLight = (
6161
return cards;
6262
};
6363

64+
const processUnassignedLights = (
65+
hass: HomeAssistant,
66+
entities: string[]
67+
): LovelaceCardConfig[] => {
68+
const unassignedFilter = generateEntityFilter(hass, {
69+
area: null,
70+
});
71+
const unassignedLights = entities.filter(unassignedFilter);
72+
const areaCards: LovelaceCardConfig[] = [];
73+
const computeTileCard = computeAreaTileCardConfig(hass, "", false);
74+
75+
for (const entityId of unassignedLights) {
76+
areaCards.push(computeTileCard(entityId));
77+
}
78+
79+
return areaCards;
80+
};
81+
6482
@customElement("light-view-strategy")
6583
export class LightViewStrategy extends ReactiveElement {
6684
static async generate(
@@ -136,10 +154,30 @@ export class LightViewStrategy extends ReactiveElement {
136154
}
137155
}
138156

157+
// Process unassigned lights
158+
const unassignedCards = processUnassignedLights(hass, entities);
159+
if (unassignedCards.length > 0) {
160+
const section: LovelaceSectionRawConfig = {
161+
type: "grid",
162+
column_span: 2,
163+
cards: [
164+
{
165+
type: "heading",
166+
heading:
167+
sections.length > 0
168+
? hass.localize("ui.panel.lovelace.strategy.light.other_lights")
169+
: hass.localize("ui.panel.lovelace.strategy.light.lights"),
170+
},
171+
...unassignedCards,
172+
],
173+
};
174+
sections.push(section);
175+
}
176+
139177
return {
140178
type: "sections",
141179
max_columns: 2,
142-
sections: sections || [],
180+
sections: sections,
143181
};
144182
}
145183
}

src/panels/lovelace/cards/hui-home-summary-card.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,6 @@ export class HuiHomeSummaryCard extends LitElement implements LovelaceCard {
8787
const allEntities = Object.keys(this.hass!.states);
8888

8989
const areas = Object.values(this.hass.areas);
90-
const areasFilter = generateEntityFilter(this.hass, {
91-
area: areas.map((area) => area.area_id),
92-
});
93-
94-
const entitiesInsideArea = allEntities.filter(areasFilter);
9590

9691
switch (this._config.summary) {
9792
case "light": {
@@ -100,7 +95,7 @@ export class HuiHomeSummaryCard extends LitElement implements LovelaceCard {
10095
generateEntityFilter(this.hass!, filter)
10196
);
10297

103-
const lightEntities = findEntities(entitiesInsideArea, lightsFilters);
98+
const lightEntities = findEntities(allEntities, lightsFilters);
10499

105100
const onLights = lightEntities.filter((entityId) => {
106101
const s = this.hass!.states[entityId]?.state;
@@ -153,7 +148,7 @@ export class HuiHomeSummaryCard extends LitElement implements LovelaceCard {
153148
generateEntityFilter(this.hass!, filter)
154149
);
155150

156-
const safetyEntities = findEntities(entitiesInsideArea, safetyFilters);
151+
const safetyEntities = findEntities(allEntities, safetyFilters);
157152

158153
const locks = safetyEntities.filter((entityId) => {
159154
const domain = computeDomain(entityId);
@@ -204,7 +199,7 @@ export class HuiHomeSummaryCard extends LitElement implements LovelaceCard {
204199
);
205200

206201
const mediaPlayerEntities = findEntities(
207-
entitiesInsideArea,
202+
allEntities,
208203
mediaPlayerFilters
209204
);
210205

src/panels/lovelace/strategies/home/home-media-players-view-strategy.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ const processAreasForMediaPlayers = (
5959
return cards;
6060
};
6161

62+
const processUnassignedEntities = (
63+
hass: HomeAssistant,
64+
entities: string[]
65+
): LovelaceCardConfig[] => {
66+
const unassignedFilter = generateEntityFilter(hass, {
67+
area: null,
68+
});
69+
const unassignedEntities = entities.filter(unassignedFilter);
70+
const areaCards: LovelaceCardConfig[] = [];
71+
72+
for (const entityId of unassignedEntities) {
73+
areaCards.push({
74+
type: "media-control",
75+
entity: entityId,
76+
} satisfies MediaControlCardConfig);
77+
}
78+
79+
return areaCards;
80+
};
81+
6282
@customElement("home-media-players-view-strategy")
6383
export class HomeMMediaPlayersViewStrategy extends ReactiveElement {
6484
static async generate(
@@ -134,10 +154,35 @@ export class HomeMMediaPlayersViewStrategy extends ReactiveElement {
134154
}
135155
}
136156

157+
// Process unassigned entities
158+
const unassignedCards = processUnassignedEntities(hass, entities);
159+
160+
if (unassignedCards.length > 0) {
161+
const section: LovelaceSectionRawConfig = {
162+
type: "grid",
163+
column_span: 2,
164+
cards: [
165+
{
166+
type: "heading",
167+
heading:
168+
sections.length > 0
169+
? hass.localize(
170+
"ui.panel.lovelace.strategy.home_media_players.other_media_players"
171+
)
172+
: hass.localize(
173+
"ui.panel.lovelace.strategy.home_media_players.media_players"
174+
),
175+
},
176+
...unassignedCards,
177+
],
178+
};
179+
sections.push(section);
180+
}
181+
137182
return {
138183
type: "sections",
139184
max_columns: 2,
140-
sections: sections || [],
185+
sections: sections,
141186
};
142187
}
143188
}

src/panels/safety/strategies/safety-view-strategy.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,24 @@ const processAreasForSafety = (
103103
return cards;
104104
};
105105

106+
const processUnassignedEntities = (
107+
hass: HomeAssistant,
108+
entities: string[]
109+
): LovelaceCardConfig[] => {
110+
const unassignedFilter = generateEntityFilter(hass, {
111+
area: null,
112+
});
113+
const unassignedLights = entities.filter(unassignedFilter);
114+
const areaCards: LovelaceCardConfig[] = [];
115+
const computeTileCard = computeAreaTileCardConfig(hass, "", false);
116+
117+
for (const entityId of unassignedLights) {
118+
areaCards.push(computeTileCard(entityId));
119+
}
120+
121+
return areaCards;
122+
};
123+
106124
@customElement("safety-view-strategy")
107125
export class SafetyViewStrategy extends ReactiveElement {
108126
static async generate(
@@ -178,10 +196,33 @@ export class SafetyViewStrategy extends ReactiveElement {
178196
}
179197
}
180198

199+
// Process unassigned entities
200+
const unassignedCards = processUnassignedEntities(hass, entities);
201+
202+
if (unassignedCards.length > 0) {
203+
const section: LovelaceSectionRawConfig = {
204+
type: "grid",
205+
column_span: 2,
206+
cards: [
207+
{
208+
type: "heading",
209+
heading:
210+
sections.length > 0
211+
? hass.localize(
212+
"ui.panel.lovelace.strategy.safety.other_devices"
213+
)
214+
: hass.localize("ui.panel.lovelace.strategy.safety.devices"),
215+
},
216+
...unassignedCards,
217+
],
218+
};
219+
sections.push(section);
220+
}
221+
181222
return {
182223
type: "sections",
183224
max_columns: 2,
184-
sections: sections || [],
225+
sections: sections,
185226
};
186227
}
187228
}

src/translations/en.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6975,6 +6975,22 @@
69756975
"common_controls": {
69766976
"not_loaded": "Usage Prediction integration is not loaded.",
69776977
"no_data": "This place will soon fill up with the entities you use most often, based on your activity."
6978+
},
6979+
"light": {
6980+
"lights": "Lights",
6981+
"other_lights": "Other lights"
6982+
},
6983+
"safety": {
6984+
"devices": "Devices",
6985+
"other_devices": "Other devices"
6986+
},
6987+
"climate": {
6988+
"devices": "Devices",
6989+
"other_devices": "Other devices"
6990+
},
6991+
"home_media_players": {
6992+
"media_players": "Media players",
6993+
"other_media_players": "Other media players"
69786994
}
69796995
},
69806996
"cards": {

0 commit comments

Comments
 (0)