Related blueprints (#16618)

This commit is contained in:
Bram Kragten 2023-05-30 15:04:45 +02:00 committed by GitHub
parent e0c1f98803
commit a70d7d8de3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 501 additions and 234 deletions

View File

@ -1,31 +1,30 @@
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import "@material/mwc-list/mwc-list";
import { mdiDevices, mdiPaletteSwatch, mdiSofa } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
nothing,
PropertyValues,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
import {
AreaRegistryEntry,
subscribeAreaRegistry,
} from "../data/area_registry";
import { Blueprints, fetchBlueprints } from "../data/blueprint";
import { ConfigEntry, getConfigEntries } from "../data/config_entries";
import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../data/device_registry";
import { SceneEntity } from "../data/scene";
import { findRelated, ItemType, RelatedResult } from "../data/search";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { HomeAssistant } from "../types";
import { brandsUrl } from "../util/brands-url";
import "./ha-icon-next";
import "./ha-list-item";
import "./ha-state-icon";
import "./ha-switch";
@customElement("ha-related-items")
export class HaRelatedItems extends SubscribeMixin(LitElement) {
export class HaRelatedItems extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public itemType!: ItemType;
@ -34,29 +33,31 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
@state() private _entries?: ConfigEntry[];
@state() private _devices?: DeviceRegistryEntry[];
@state() private _areas?: AreaRegistryEntry[];
@state() private _blueprints?: Record<"automation" | "script", Blueprints>;
@state() private _related?: RelatedResult;
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
this._devices = devices;
}),
subscribeAreaRegistry(this.hass.connection!, (areas) => {
this._areas = areas;
}),
];
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
getConfigEntries(this.hass).then((configEntries) => {
this._entries = configEntries;
});
}
private async _fetchConfigEntries() {
if (this._entries) {
return;
}
this.hass.loadBackendTranslation("title");
this._entries = await getConfigEntries(this.hass);
}
private async _fetchBlueprints() {
if (this._blueprints) {
return;
}
const [automation, script] = await Promise.all([
fetchBlueprints(this.hass, "automation"),
fetchBlueprints(this.hass, "script"),
]);
this._blueprints = { automation, script };
}
protected updated(changedProps: PropertyValues) {
@ -81,7 +82,10 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
}
return html`
${this._related.config_entry && this._entries
? this._related.config_entry.map((relatedConfigEntryId) => {
? html`<h3>
${this.hass.localize("ui.components.related-items.integration")}:
</h3><mwc-list>${this._related.config_entry.map(
(relatedConfigEntryId) => {
const entry: ConfigEntry | undefined = this._entries!.find(
(configEntry) => configEntry.entry_id === relatedConfigEntryId
);
@ -89,69 +93,101 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
return "";
}
return html`
<h3>
${this.hass.localize(
"ui.components.related-items.integration"
)}:
</h3>
<a
href=${`/config/integrations#config_entry=${relatedConfigEntryId}`}
@click=${this._navigateAwayClose}
>
<ha-list-item hasMeta graphic="icon">
<img
.src=${brandsUrl({
domain: entry.domain,
type: "icon",
useFallback: true,
darkOptimized: this.hass.themes?.darkMode,
})}
alt=${entry.domain}
slot="graphic"
/>
${this.hass.localize(`component.${entry.domain}.title`)}:
${entry.title}
${entry.title} <ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>
`;
})
}
)}</mw-list>`
: ""}
${this._related.device && this._devices
? this._related.device.map((relatedDeviceId) => {
const device: DeviceRegistryEntry | undefined = this._devices!.find(
(dev) => dev.id === relatedDeviceId
);
${this._related.device
? html`<h3>
${this.hass.localize("ui.components.related-items.device")}:
</h3>
${this._related.device.map((relatedDeviceId) => {
const device = this.hass.devices[relatedDeviceId];
if (!device) {
return "";
}
return html`
<h3>
${this.hass.localize("ui.components.related-items.device")}:
</h3>
<a
href="/config/devices/device/${relatedDeviceId}"
@click=${this._navigateAwayClose}
>
<ha-list-item hasMeta graphic="icon">
<ha-svg-icon
.path=${mdiDevices}
slot="graphic"
></ha-svg-icon>
${device.name_by_user || device.name}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>
`;
})
})} </mwc-list>
`
: ""}
${this._related.area && this._areas
? this._related.area.map((relatedAreaId) => {
const area: AreaRegistryEntry | undefined = this._areas!.find(
(ar) => ar.area_id === relatedAreaId
);
${this._related.area
? html`<h3>
${this.hass.localize("ui.components.related-items.area")}:
</h3>
<mwc-list
>${this._related.area.map((relatedAreaId) => {
const area = this.hass.areas[relatedAreaId];
if (!area) {
return "";
}
return html`
<h3>
${this.hass.localize("ui.components.related-items.area")}:
</h3>
<a
href="/config/areas/area/${relatedAreaId}"
@click=${this._navigateAwayClose}
>
<ha-list-item
hasMeta
.graphic=${area.picture ? "avatar" : "icon"}
>
${area.picture
? html` <div
class="avatar"
style=${styleMap({
backgroundImage: `url(${area.picture})`,
})}
slot="graphic"
></div>`
: html`<ha-svg-icon
.path=${mdiSofa}
slot="graphic"
></ha-svg-icon>`}
${area.name}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>
`;
})
})}</mwc-list
>`
: ""}
${this._related.entity
? html`
<h3>
${this.hass.localize("ui.components.related-items.entity")}:
</h3>
<ul>
<mwc-list>
${this._related.entity.map((entityId) => {
const entity: HassEntity | undefined =
this.hass.states[entityId];
@ -159,48 +195,56 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
return "";
}
return html`
<li>
<button
<ha-list-item
@click=${this._openMoreInfo}
.entityId=${entityId}
class="link"
hasMeta
graphic="icon"
>
${entity.attributes.friendly_name || entityId}
</button>
</li>
<ha-state-icon
.state=${entity}
slot="graphic"
></ha-state-icon>
${entity.attributes.friendly_name || entity.entity_id}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`;
})}
</ul>
</mwc-list>
`
: ""}
${this._related.group
? html`
<h3>${this.hass.localize("ui.components.related-items.group")}:</h3>
<ul>
<mwc-list>
${this._related.group.map((groupId) => {
const group: HassEntity | undefined = this.hass.states[groupId];
if (!group) {
return "";
}
return html`
<li>
<button
class="link"
<ha-list-item
@click=${this._openMoreInfo}
.entityId=${groupId}
hasMeta
graphic="icon"
>
<ha-state-icon
.state=${group}
slot="graphic"
></ha-state-icon>
${group.attributes.friendly_name || group.entity_id}
</button>
</li>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`;
})}
</ul>
</mwc-list>
`
: ""}
${this._related.scene
? html`
<h3>${this.hass.localize("ui.components.related-items.scene")}:</h3>
<ul>
<mwc-list>
${this._related.scene.map((sceneId) => {
const scene: SceneEntity | undefined =
this.hass.states[sceneId];
@ -208,18 +252,51 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
return "";
}
return html`
<li>
<button
class="link"
<ha-list-item
@click=${this._openMoreInfo}
.entityId=${sceneId}
hasMeta
graphic="icon"
>
<ha-state-icon
.state=${scene}
slot="graphic"
></ha-state-icon>
${scene.attributes.friendly_name || scene.entity_id}
</button>
</li>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`;
})}
</ul>
</mwc-list>
`
: ""}
${this._related.automation_blueprint
? html`
<h3>
${this.hass.localize("ui.components.related-items.blueprint")}:
</h3>
<mwc-list>
${this._related.automation_blueprint.map((path) => {
const blueprintMeta = this._blueprints
? this._blueprints.automation[path]
: undefined;
return html`<a
href="/config/blueprint/dashboard"
@click=${this._navigateAwayClose}
>
<ha-list-item hasMeta graphic="icon">
<ha-svg-icon
.path=${mdiPaletteSwatch}
slot="graphic"
></ha-svg-icon>
${!blueprintMeta || "error" in blueprintMeta
? path
: blueprintMeta.metadata.name || path}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>`;
})}
</mwc-list>
`
: ""}
${this._related.automation
@ -227,7 +304,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
<h3>
${this.hass.localize("ui.components.related-items.automation")}:
</h3>
<ul>
<mwc-list>
${this._related.automation.map((automationId) => {
const automation: HassEntity | undefined =
this.hass.states[automationId];
@ -235,19 +312,52 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
return "";
}
return html`
<li>
<button
class="link"
<ha-list-item
@click=${this._openMoreInfo}
.entityId=${automationId}
hasMeta
graphic="icon"
>
<ha-state-icon
.state=${automation}
slot="graphic"
></ha-state-icon>
${automation.attributes.friendly_name ||
automation.entity_id}
</button>
</li>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`;
})}
</ul>
</mwc-list>
`
: ""}
${this._related.script_blueprint
? html`
<h3>
${this.hass.localize("ui.components.related-items.blueprint")}:
</h3>
<mwc-list>
${this._related.script_blueprint.map((path) => {
const blueprintMeta = this._blueprints
? this._blueprints.script[path]
: undefined;
return html`<a
href="/config/blueprint/dashboard"
@click=${this._navigateAwayClose}
>
<ha-list-item hasMeta graphic="icon">
<ha-svg-icon
.path=${mdiPaletteSwatch}
slot="graphic"
></ha-svg-icon>
${!blueprintMeta || "error" in blueprintMeta
? path
: blueprintMeta.metadata.name || path}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>`;
})}
</mwc-list>
`
: ""}
${this._related.script
@ -255,7 +365,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
<h3>
${this.hass.localize("ui.components.related-items.script")}:
</h3>
<ul>
<mwc-list>
${this._related.script.map((scriptId) => {
const script: HassEntity | undefined =
this.hass.states[scriptId];
@ -263,18 +373,22 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
return "";
}
return html`
<li>
<button
class="link"
<ha-list-item
@click=${this._openMoreInfo}
.entityId=${scriptId}
hasMeta
graphic="icon"
>
<ha-state-icon
.state=${script}
slot="graphic"
></ha-state-icon>
${script.attributes.friendly_name || script.entity_id}
</button>
</li>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`;
})}
</ul>
</mwc-list>
`
: ""}
`;
@ -290,8 +404,12 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
private async _findRelated() {
this._related = await findRelated(this.hass, this.itemType, this.itemId);
await this.updateComplete;
fireEvent(this, "iron-resize");
if (this._related.config_entry) {
this._fetchConfigEntries();
}
if (this._related.script_blueprint || this._related.automation_blueprint) {
this._fetchBlueprints();
}
}
private _openMoreInfo(ev: CustomEvent) {
@ -303,19 +421,10 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
return css`
a {
color: var(--primary-color);
text-decoration: none;
}
button.link {
color: var(--primary-color);
text-align: left;
cursor: pointer;
background: none;
border-width: initial;
border-style: none;
border-color: initial;
border-image: initial;
padding: 0px;
font: inherit;
text-decoration: underline;
ha-list-item {
--mdc-list-side-padding: 24px;
}
h3 {
font-family: var(--paper-font-title_-_font-family);
@ -327,10 +436,15 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
letter-spacing: var(--paper-font-title_-_letter-spacing);
line-height: var(--paper-font-title_-_line-height);
opacity: var(--dark-primary-opacity);
padding: 0 24px;
}
h3:first-child {
margin-top: 0;
}
.avatar {
background-position: center center;
background-size: cover;
}
`;
}
}

View File

@ -3,14 +3,23 @@ import { HomeAssistant } from "../types";
export interface RelatedResult {
area?: string[];
automation?: string[];
automation_blueprint?: string[];
config_entry?: string[];
device?: string[];
entity?: string[];
group?: string[];
scene?: string[];
script?: string[];
script_blueprint?: string[];
}
export const SearchableDomains = new Set([
"automation",
"script",
"scene",
"group",
]);
export type ItemType =
| "area"
| "automation"
@ -19,7 +28,9 @@ export type ItemType =
| "entity"
| "group"
| "scene"
| "script";
| "script"
| "automation_blueprint"
| "script_blueprint";
export const findRelated = (
hass: HomeAssistant,

View File

@ -30,6 +30,7 @@ import {
ExtEntityRegistryEntry,
getExtendedEntityRegistryEntry,
} from "../../data/entity_registry";
import { SearchableDomains } from "../../data/search";
import { haStyleDialog } from "../../resources/styles";
import "../../state-summary/state-card-content";
import { HomeAssistant } from "../../types";
@ -406,7 +407,9 @@ export class MoreInfoDialog extends LitElement {
<ha-related-items
.hass=${this.hass}
.itemId=${entityId}
itemType="entity"
.itemType=${SearchableDomains.has(domain)
? domain
: "entity"}
></ha-related-items>
`
: nothing
@ -464,7 +467,6 @@ export class MoreInfoDialog extends LitElement {
flex: 1;
}
ha-related-items,
ha-more-info-history-and-logbook {
padding: 8px 24px 24px 24px;
display: block;

View File

@ -50,6 +50,8 @@ import { HomeAssistant, Route } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { configSections } from "../ha-panel-config";
import { showNewAutomationDialog } from "./show-dialog-new-automation";
import { findRelated } from "../../../data/search";
import { fetchBlueprints } from "../../../data/blueprint";
@customElement("ha-automation-picker")
class HaAutomationPicker extends LitElement {
@ -65,6 +67,8 @@ class HaAutomationPicker extends LitElement {
@property() private _activeFilters?: string[];
@state() private _searchParms = new URLSearchParams(window.location.search);
@state() private _filteredAutomations?: string[] | null;
@state() private _filterValue?;
@ -308,6 +312,34 @@ class HaAutomationPicker extends LitElement {
`;
}
firstUpdated() {
if (this._searchParms.has("blueprint")) {
this._filterBlueprint();
}
}
private async _filterBlueprint() {
const blueprint = this._searchParms.get("blueprint");
if (!blueprint) {
return;
}
const [related, blueprints] = await Promise.all([
findRelated(this.hass, "automation_blueprint", blueprint),
fetchBlueprints(this.hass, "automation"),
]);
this._filteredAutomations = related.automation || [];
const blueprintMeta = blueprints[blueprint];
this._activeFilters = [
this.hass.localize(
"ui.panel.config.automation.picker.filtered_by_blueprint",
"name",
!blueprintMeta || "error" in blueprintMeta
? blueprint
: blueprintMeta.metadata.name || blueprint
),
];
}
private _relatedFilterChanged(ev: CustomEvent) {
this._filterValue = ev.detail.value;
if (!this._filterValue) {

View File

@ -1,11 +1,13 @@
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
import {
mdiAlertCircle,
mdiDelete,
mdiDownload,
mdiEye,
mdiHelpCircle,
mdiRobot,
mdiPlus,
mdiShareVariant,
} from "@mdi/js";
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
import {
CSSResultGroup,
html,
@ -15,13 +17,18 @@ import {
} from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { extractSearchParam } from "../../../common/url/search-params";
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
import {
DataTableColumnContainer,
RowClickedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-svg-icon";
import { showAutomationEditor } from "../../../data/automation";
import {
@ -31,6 +38,7 @@ import {
deleteBlueprint,
} from "../../../data/blueprint";
import { showScriptEditor } from "../../../data/script";
import { findRelated } from "../../../data/search";
import {
showAlertDialog,
showConfirmationDialog,
@ -73,7 +81,7 @@ class HaBlueprintOverview extends LitElement {
@property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public blueprints!: Record<
string,
"automation" | "script",
Blueprints
>;
@ -104,7 +112,7 @@ class HaBlueprintOverview extends LitElement {
);
private _columns = memoizeOne(
(narrow, _language): DataTableColumnContainer => ({
(narrow, _language): DataTableColumnContainer<BlueprintMetaDataPath> => ({
name: {
title: this.hass.localize(
"ui.panel.config.blueprint.overview.headers.name"
@ -146,64 +154,60 @@ class HaBlueprintOverview extends LitElement {
direction: "asc",
width: "25%",
},
create: {
actions: {
title: "",
width: narrow ? undefined : "20%",
type: narrow ? "icon-button" : undefined,
template: (_, blueprint: BlueprintMetaDataPath) =>
width: this.narrow ? undefined : "10%",
type: "overflow-menu",
template: (_: string, blueprint) =>
blueprint.error
? ""
: narrow
? html`<ha-icon-button
.blueprint=${blueprint}
.label=${this.hass.localize(
? html`<ha-svg-icon
style="color: var(--error-color); display: block; margin-inline-end: 12px; margin-inline-start: auto;"
.path=${mdiAlertCircle}
></ha-svg-icon>`
: html`
<ha-icon-overflow-menu
.hass=${this.hass}
narrow
.items=${[
{
path: mdiPlus,
label: this.hass.localize(
`ui.panel.config.blueprint.overview.create_${blueprint.domain}`
)}
@click=${this._createNew}
.path=${mdiRobot}
>
</ha-icon-button>`
: html`<mwc-button
.blueprint=${blueprint}
@click=${this._createNew}
>
${this.hass.localize(
`ui.panel.config.blueprint.overview.create_${blueprint.domain}`
)}
</mwc-button>`,
),
action: () => this._createNew(blueprint),
},
share: {
title: "",
type: "icon-button",
template: (_, blueprint: any) =>
blueprint.error
? ""
: html`<ha-icon-button
.blueprint=${blueprint}
.disabled=${!blueprint.source_url}
.label=${this.hass.localize(
{
path: mdiEye,
label: this.hass.localize(
`ui.panel.config.blueprint.overview.view_${blueprint.domain}`
),
action: () => this._showUsed(blueprint),
},
{
path: mdiShareVariant,
disabled: !blueprint.source_url,
label: this.hass.localize(
blueprint.source_url
? "ui.panel.config.blueprint.overview.share_blueprint"
: "ui.panel.config.blueprint.overview.share_blueprint_no_url"
)}
.path=${mdiShareVariant}
@click=${this._share}
></ha-icon-button>`,
),
action: () => this._share(blueprint),
},
delete: {
title: "",
type: "icon-button",
template: (_, blueprint: any) =>
blueprint.error
? ""
: html`<ha-icon-button
.blueprint=${blueprint}
.label=${this.hass.localize(
{
divider: true,
},
{
label: this.hass.localize(
"ui.panel.config.blueprint.overview.delete_blueprint"
)}
.path=${mdiDelete}
@click=${this._delete}
></ha-icon-button>`,
),
path: mdiDelete,
action: () => this._delete(blueprint),
warning: true,
},
]}
>
</ha-icon-overflow-menu>
`,
},
})
);
@ -229,11 +233,13 @@ class HaBlueprintOverview extends LitElement {
.tabs=${configSections.automations}
.columns=${this._columns(this.narrow, this.hass.language)}
.data=${this._processedBlueprints(this.blueprints)}
id="entity_id"
id="path"
.noDataText=${this.hass.localize(
"ui.panel.config.blueprint.overview.no_blueprints"
)}
hasFab
clickable
@row-click=${this._handleRowClicked}
.appendRow=${html` <div
class="mdc-data-table__cell"
style="width: 100%; text-align: center;"
@ -310,23 +316,81 @@ class HaBlueprintOverview extends LitElement {
fireEvent(this, "reload-blueprints");
}
private _createNew = (ev) => {
const blueprint = ev.currentTarget.blueprint as BlueprintMetaDataPath;
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const blueprint = this._processedBlueprints(this.blueprints).find(
(b) => b.path === ev.detail.id
);
if (blueprint.error) {
return;
}
this._createNew(blueprint);
}
private _showUsed = (blueprint: BlueprintMetaDataPath) => {
navigate(
`/config/${blueprint.domain}/dashboard?blueprint=${encodeURIComponent(
blueprint.path
)}`
);
};
private _createNew = (blueprint: BlueprintMetaDataPath) => {
createNewFunctions[blueprint.domain](blueprint);
};
private _share = (ev) => {
const blueprint = ev.currentTarget.blueprint;
private _share = (blueprint: BlueprintMetaDataPath) => {
const params = new URLSearchParams();
params.append("redirect", "blueprint_import");
params.append("blueprint_url", blueprint.source_url);
params.append("blueprint_url", blueprint.source_url!);
window.open(
`https://my.home-assistant.io/create-link/?${params.toString()}`
);
};
private _delete = async (ev) => {
const blueprint = ev.currentTarget.blueprint;
private _delete = async (blueprint: BlueprintMetaDataPath) => {
const related = await findRelated(
this.hass,
`${blueprint.domain}_blueprint`,
blueprint.path
);
if (related.automation?.length || related.script?.length) {
const type = this.hass.localize(
`ui.panel.config.blueprint.overview.types_plural.${blueprint.domain}`
);
const result = await showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.config.blueprint.overview.blueprint_in_use_title"
),
text: this.hass.localize(
"ui.panel.config.blueprint.overview.blueprint_in_use_text",
{
type,
list: html`<ul>
${[...(related.automation || []), ...(related.script || [])].map(
(item) => {
const state = this.hass.states[item];
return html`<li>
${state ? `${computeStateName(state)} (${item})` : item}
</li>`;
}
)}
</ul>`,
}
),
confirmText: this.hass!.localize(
"ui.panel.config.blueprint.overview.blueprint_in_use_view",
{ type }
),
});
if (result) {
navigate(
`/config/${blueprint.domain}/dashboard?blueprint=${encodeURIComponent(
blueprint.path
)}`
);
}
return;
}
if (
!(await showConfirmationDialog(this, {
title: this.hass.localize(

View File

@ -45,6 +45,8 @@ import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import { configSections } from "../ha-panel-config";
import { EntityRegistryEntry } from "../../../data/entity_registry";
import { findRelated } from "../../../data/search";
import { fetchBlueprints } from "../../../data/blueprint";
@customElement("ha-script-picker")
class HaScriptPicker extends LitElement {
@ -60,6 +62,8 @@ class HaScriptPicker extends LitElement {
@property({ attribute: false }) public entityRegistry!: EntityRegistryEntry[];
@state() private _searchParms = new URLSearchParams(window.location.search);
@state() private _activeFilters?: string[];
@state() private _filteredScripts?: string[] | null;
@ -251,6 +255,34 @@ class HaScriptPicker extends LitElement {
`;
}
firstUpdated() {
if (this._searchParms.has("blueprint")) {
this._filterBlueprint();
}
}
private async _filterBlueprint() {
const blueprint = this._searchParms.get("blueprint");
if (!blueprint) {
return;
}
const [related, blueprints] = await Promise.all([
findRelated(this.hass, "script_blueprint", blueprint),
fetchBlueprints(this.hass, "script"),
]);
this._filteredScripts = related.script || [];
const blueprintMeta = blueprints[blueprint];
this._activeFilters = [
this.hass.localize(
"ui.panel.config.script.picker.filtered_by_blueprint",
"name",
!blueprintMeta || "error" in blueprintMeta
? blueprint
: blueprintMeta.metadata.name || blueprint
),
];
}
private _relatedFilterChanged(ev: CustomEvent) {
this._filterValue = ev.detail.value;
if (!this._filterValue) {

View File

@ -540,7 +540,8 @@
"group": "Part of the following groups",
"scene": "Part of the following scenes",
"script": "Part of the following scripts",
"automation": "Part of the following automations"
"automation": "Part of the following automations",
"blueprint": "Using the following blueprints"
},
"data-table": {
"search": "Search",
@ -2147,6 +2148,7 @@
"delete_confirm_text": "{name} will be permanently deleted.",
"duplicate": "[%key:ui::common::duplicate%]",
"disabled": "Disabled",
"filtered_by_blueprint": "blueprint: {name}",
"headers": {
"toggle": "Enable/disable",
"name": "Name",
@ -2622,12 +2624,21 @@
"automation": "Automation",
"script": "Script"
},
"types_plural": {
"automation": "automations",
"script": "scripts"
},
"blueprint_in_use_title": "This blueprint is in use, and can not be deleted",
"blueprint_in_use_text": "Please remove all below {type} that use this blueprint before deleting it. {list}",
"blueprint_in_use_view": "view {type}",
"confirm_delete_title": "Delete blueprint?",
"confirm_delete_text": "{name} will be permanently deleted.",
"add_blueprint": "Import blueprint",
"no_blueprints": "[%key:ui::panel::config::automation::editor::blueprint::no_blueprints%]",
"create_automation": "Create automation",
"create_script": "Create script",
"view_automation": "Show automations using this blueprint",
"view_script": "Show scripts using this blueprint",
"delete_blueprint": "Delete blueprint",
"share_blueprint": "Share blueprint",
"share_blueprint_no_url": "Unable to share blueprint: no source url",
@ -2662,6 +2673,7 @@
"run": "[%key:ui::panel::config::automation::editor::actions::run%]",
"show_trace": "[%key:ui::panel::config::automation::editor::show_trace%]",
"show_info": "[%key:ui::panel::config::automation::editor::show_info%]",
"filtered_by_blueprint": "[%key:ui::panel::config::automation::picker::filtered_by_blueprint%]",
"headers": {
"name": "Name",
"state": "State"