Skip to content

Commit c3201ee

Browse files
EarMasterMindFreezeCopilot
authored
Show blueprint usage count in overview (#28054)
* Show blueprint usage count in overview * feat(blueprint): add usage count functionality and update table rendering * feat(blueprint): enhance usage count display with interactive chip * fix(blueprint): convert _handleUsageClick to an arrow function for consistent context binding * Update src/panels/config/blueprint/ha-blueprint-overview.ts Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Petar Petrov <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent bd67a3b commit c3201ee

File tree

2 files changed

+117
-6
lines changed

2 files changed

+117
-6
lines changed

src/panels/config/blueprint/ha-blueprint-overview.ts

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
} from "../../../data/blueprint";
4444
import { showScriptEditor } from "../../../data/script";
4545
import { findRelated } from "../../../data/search";
46+
import "../../../components/chips/ha-assist-chip";
4647
import {
4748
showAlertDialog,
4849
showConfirmationDialog,
@@ -60,6 +61,7 @@ type BlueprintMetaDataPath = BlueprintMetaData & {
6061
error: boolean;
6162
type: "automation" | "script";
6263
fullpath: string;
64+
usageCount?: number;
6365
};
6466

6567
const createNewFunctions = {
@@ -128,14 +130,20 @@ class HaBlueprintOverview extends LitElement {
128130
})
129131
private _filter = "";
130132

133+
@state() private _usageCounts: Record<string, number> = {};
134+
135+
private _usageCountRequest = 0;
136+
131137
private _processedBlueprints = memoizeOne(
132138
(
133139
blueprints: Record<string, Blueprints>,
134-
localize: LocalizeFunc
140+
localize: LocalizeFunc,
141+
usageCounts: Record<string, number>
135142
): BlueprintMetaDataPath[] => {
136143
const result: any[] = [];
137144
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
138145
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
146+
const fullpath = `${type}/${path}`;
139147
if ("error" in blueprint) {
140148
result.push({
141149
name: blueprint.error,
@@ -145,7 +153,8 @@ class HaBlueprintOverview extends LitElement {
145153
),
146154
error: true,
147155
path,
148-
fullpath: `${type}/${path}`,
156+
fullpath,
157+
usageCount: 0,
149158
});
150159
} else {
151160
result.push({
@@ -156,7 +165,8 @@ class HaBlueprintOverview extends LitElement {
156165
),
157166
error: false,
158167
path,
159-
fullpath: `${type}/${path}`,
168+
fullpath,
169+
usageCount: usageCounts[fullpath] || 0,
160170
});
161171
}
162172
})
@@ -189,6 +199,34 @@ class HaBlueprintOverview extends LitElement {
189199
filterable: true,
190200
flex: 2,
191201
},
202+
usage_count: {
203+
title: localize(
204+
"ui.panel.config.blueprint.overview.headers.usage_count"
205+
),
206+
sortable: true,
207+
valueColumn: "usageCount",
208+
type: "numeric",
209+
minWidth: "100px",
210+
maxWidth: "120px",
211+
template: (blueprint) => {
212+
const count = blueprint.usageCount ?? 0;
213+
return html`
214+
<ha-assist-chip
215+
filled
216+
.active=${count > 0}
217+
label=${String(count)}
218+
title=${blueprint.error
219+
? String(count)
220+
: this.hass.localize(
221+
`ui.panel.config.blueprint.overview.view_${blueprint.type}`
222+
)}
223+
?disabled=${blueprint.error}
224+
data-fullpath=${blueprint.fullpath}
225+
@click=${this._handleUsageClick}
226+
></ha-assist-chip>
227+
`;
228+
},
229+
},
192230
fullpath: {
193231
title: "fullpath",
194232
hidden: true,
@@ -266,6 +304,7 @@ class HaBlueprintOverview extends LitElement {
266304

267305
protected firstUpdated(changedProps: PropertyValues) {
268306
super.firstUpdated(changedProps);
307+
this._loadUsageCounts();
269308
if (this.route.path === "/import") {
270309
const url = extractSearchParam("blueprint_url");
271310
navigate("/config/blueprint/dashboard", { replace: true });
@@ -275,6 +314,13 @@ class HaBlueprintOverview extends LitElement {
275314
}
276315
}
277316

317+
protected updated(changedProps: PropertyValues) {
318+
super.updated(changedProps);
319+
if (changedProps.has("blueprints")) {
320+
this._loadUsageCounts();
321+
}
322+
}
323+
278324
protected render(): TemplateResult {
279325
return html`
280326
<hass-tabs-subpage-data-table
@@ -284,7 +330,11 @@ class HaBlueprintOverview extends LitElement {
284330
.route=${this.route}
285331
.tabs=${configSections.automations}
286332
.columns=${this._columns(this.hass.localize)}
287-
.data=${this._processedBlueprints(this.blueprints, this.hass.localize)}
333+
.data=${this._processedBlueprints(
334+
this.blueprints,
335+
this.hass.localize,
336+
this._usageCounts
337+
)}
288338
id="fullpath"
289339
.noDataText=${this.hass.localize(
290340
"ui.panel.config.blueprint.overview.no_blueprints"
@@ -380,10 +430,51 @@ class HaBlueprintOverview extends LitElement {
380430
fireEvent(this, "reload-blueprints");
381431
}
382432

433+
private async _loadUsageCounts() {
434+
if (!this.blueprints) {
435+
return;
436+
}
437+
438+
const request = ++this._usageCountRequest;
439+
const usageCounts: Record<string, number> = {};
440+
441+
const blueprintList = this._processedBlueprints(
442+
this.blueprints,
443+
this.hass.localize,
444+
{}
445+
);
446+
447+
await Promise.all(
448+
blueprintList.map(async (blueprint) => {
449+
if (blueprint.error) {
450+
usageCounts[blueprint.fullpath] = 0;
451+
return;
452+
}
453+
try {
454+
const related = await findRelated(
455+
this.hass,
456+
`${blueprint.domain}_blueprint`,
457+
blueprint.path
458+
);
459+
const count =
460+
(related.automation?.length || 0) + (related.script?.length || 0);
461+
usageCounts[blueprint.fullpath] = count;
462+
} catch (_err) {
463+
usageCounts[blueprint.fullpath] = 0;
464+
}
465+
})
466+
);
467+
468+
if (request === this._usageCountRequest) {
469+
this._usageCounts = usageCounts;
470+
}
471+
}
472+
383473
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
384474
const blueprint = this._processedBlueprints(
385475
this.blueprints,
386-
this.hass.localize
476+
this.hass.localize,
477+
this._usageCounts
387478
).find((b) => b.fullpath === ev.detail.id)!;
388479
if (blueprint.error) {
389480
showAlertDialog(this, {
@@ -397,6 +488,25 @@ class HaBlueprintOverview extends LitElement {
397488
this._createNew(blueprint);
398489
}
399490

491+
private _handleUsageClick = (ev: Event) => {
492+
ev.stopPropagation();
493+
ev.preventDefault();
494+
const target = ev.currentTarget as HTMLElement | null;
495+
const fullpath = target?.dataset.fullpath;
496+
if (!fullpath) {
497+
return;
498+
}
499+
const blueprint = this._processedBlueprints(
500+
this.blueprints,
501+
this.hass.localize,
502+
this._usageCounts
503+
).find((item) => item.fullpath === fullpath);
504+
if (!blueprint || blueprint.error) {
505+
return;
506+
}
507+
this._showUsed(blueprint);
508+
};
509+
400510
private _showUsed = (blueprint: BlueprintMetaDataPath) => {
401511
navigate(
402512
`/config/${blueprint.domain}/dashboard?blueprint=${encodeURIComponent(

src/translations/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4796,7 +4796,8 @@
47964796
"headers": {
47974797
"name": "Name",
47984798
"type": "Type",
4799-
"file_name": "File name"
4799+
"file_name": "File name",
4800+
"usage_count": "In use"
48004801
},
48014802
"types": {
48024803
"automation": "Automation",

0 commit comments

Comments
 (0)