Compare commits

..

1 Commits

Author SHA1 Message Date
Paulus Schoutsen
163bbbd1eb Add ZHA entry picker 2025-12-30 22:50:46 +01:00
3 changed files with 166 additions and 1 deletions

View File

@@ -1,6 +1,8 @@
import { customElement, property } from "lit/decorators";
import type { RouterOptions } from "../../../../../layouts/hass-router-page";
import { HassRouterPage } from "../../../../../layouts/hass-router-page";
import { navigate } from "../../../../../common/navigate";
import { getConfigEntries } from "../../../../../data/config_entries";
import type { HomeAssistant } from "../../../../../types";
@customElement("zha-config-dashboard-router")
@@ -11,10 +13,18 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
@property({ type: Boolean }) public narrow = false;
private _configEntry = new URLSearchParams(window.location.search).get(
"config_entry"
);
protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
defaultPage: "picker",
showLoading: true,
routes: {
picker: {
tag: "zha-config-entry-picker",
load: () => import("./zha-config-entry-picker"),
},
dashboard: {
tag: "zha-config-dashboard",
load: () => import("./zha-config-dashboard"),
@@ -40,6 +50,7 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
load: () => import("./zha-network-visualization-page"),
},
},
initialLoad: () => this._fetchConfigEntries(),
};
protected updatePageEl(el): void {
@@ -47,6 +58,12 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
el.hass = this.hass;
el.isWide = this.isWide;
el.narrow = this.narrow;
// Only pass configEntryId to pages that need it (not the picker)
if (this.routeTail.path !== "picker") {
el.configEntryId = this._configEntry;
}
if (this._currentPage === "group") {
el.groupId = this.routeTail.path.substr(1);
} else if (this._currentPage === "device") {
@@ -54,6 +71,35 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
} else if (this._currentPage === "visualization") {
el.zoomedDeviceIdFromURL = this.routeTail.path.substr(1);
}
const searchParams = new URLSearchParams(window.location.search);
if (this._configEntry && !searchParams.has("config_entry")) {
searchParams.append("config_entry", this._configEntry);
navigate(
`${this.routeTail.prefix}${
this.routeTail.path
}?${searchParams.toString()}`,
{ replace: true }
);
}
}
private async _fetchConfigEntries() {
if (this._configEntry) {
return;
}
const entries = await getConfigEntries(this.hass, {
domain: "zha",
});
// Only auto-select if there's exactly one entry
if (entries.length === 1) {
this._configEntry = entries[0].entry_id;
// Redirect to dashboard with the config entry
navigate(`/config/zha/dashboard?config_entry=${this._configEntry}`, {
replace: true,
});
}
// Otherwise, let the picker page handle showing the list
}
}

View File

@@ -0,0 +1,115 @@
import type { CSSResultGroup } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-list";
import "../../../../../components/ha-list-item";
import "../../../../../layouts/hass-loading-screen";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
@customElement("zha-config-entry-picker")
class ZHAConfigEntryPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow = false;
@state() private _configEntries?: ConfigEntry[];
protected async firstUpdated() {
await this._fetchConfigEntries();
}
protected render() {
if (!this._configEntries) {
return html`<hass-loading-screen></hass-loading-screen>`;
}
if (this._configEntries.length === 0) {
return html`
<div class="content">
<ha-card>
<div class="card-content">
<p>
${this.hass.localize("ui.panel.config.zha.picker.no_entries")}
</p>
</div>
</ha-card>
</div>
`;
}
return html`
<div class="content">
<ha-card>
<h1 class="card-header">
${this.hass.localize("ui.panel.config.zha.picker.title")}
</h1>
<ha-list>
${this._configEntries.map(
(entry) => html`
<a href="/config/zha/dashboard?config_entry=${entry.entry_id}">
<ha-list-item hasMeta>
<span>${entry.title}</span>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>
`
)}
</ha-list>
</ha-card>
</div>
`;
}
private async _fetchConfigEntries() {
const entries = await getConfigEntries(this.hass, {
domain: "zha",
});
this._configEntries = entries;
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
.content {
padding: 24px;
display: flex;
justify-content: center;
}
ha-card {
max-width: 600px;
width: 100%;
}
.card-header {
font-size: 20px;
font-weight: 500;
padding: 16px;
padding-bottom: 0;
}
a {
text-decoration: none;
color: inherit;
}
ha-list {
--md-list-item-leading-space: var(--ha-space-4);
--md-list-item-trailing-space: var(--ha-space-4);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-config-entry-picker": ZHAConfigEntryPicker;
}
}

View File

@@ -6258,6 +6258,10 @@
"channel_has_been_changed": "Network channel has been changed",
"devices_will_rejoin": "Devices will re-join the network over time. This may take a few minutes.",
"channel_auto": "Smart"
},
"picker": {
"title": "Select Zigbee network",
"no_entries": "No Zigbee networks configured. Please set up ZHA integration first."
}
},
"zwave_js": {