diff --git a/src/data/zwave_js.ts b/src/data/zwave_js.ts index 6d9ac2dd89..8dd77a3335 100644 --- a/src/data/zwave_js.ts +++ b/src/data/zwave_js.ts @@ -19,7 +19,7 @@ export interface ZWaveJSClient { export interface ZWaveJSController { home_id: string; - node_count: number; + nodes: number[]; } export interface ZWaveJSNode { @@ -28,6 +28,14 @@ export interface ZWaveJSNode { status: number; } +export enum NodeStatus { + Unknown, + Asleep, + Awake, + Dead, + Alive, +} + export const nodeStatus = ["unknown", "asleep", "awake", "dead", "alive"]; export const fetchNetworkStatus = ( diff --git a/src/dialogs/generic/dialog-box.ts b/src/dialogs/generic/dialog-box.ts index 059fff683b..f76c7ae959 100644 --- a/src/dialogs/generic/dialog-box.ts +++ b/src/dialogs/generic/dialog-box.ts @@ -57,7 +57,7 @@ class DialogBox extends LitElement { open ?scrimClickAction=${confirmPrompt} ?escapeKeyAction=${confirmPrompt} - @closed=${this._dialogClosed} + @closing=${this._dialogClosed} defaultAction="ignore" .heading=${this._params.title ? this._params.title diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts index 531ae71988..bc219081a4 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts @@ -1,5 +1,6 @@ import "@material/mwc-button/mwc-button"; -import { mdiCheckCircle, mdiCircle } from "@mdi/js"; +import "@material/mwc-icon-button/mwc-icon-button"; +import { mdiCheckCircle, mdiCircle, mdiRefresh } from "@mdi/js"; import { css, CSSResultArray, @@ -12,11 +13,20 @@ import { } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import "../../../../../components/ha-card"; +import "../../../../../components/ha-svg-icon"; import "../../../../../components/ha-icon-next"; +import { getSignedPath } from "../../../../../data/auth"; import { fetchNetworkStatus, + fetchNodeStatus, + NodeStatus, ZWaveJSNetwork, + ZWaveJSNode, } from "../../../../../data/zwave_js"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../../../dialogs/generic/show-dialog-box"; import "../../../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant, Route } from "../../../../../types"; @@ -39,6 +49,8 @@ class ZWaveJSConfigDashboard extends LitElement { @internalProperty() private _network?: ZWaveJSNetwork; + @internalProperty() private _nodes?: ZWaveJSNode[]; + @internalProperty() private _status = "unknown"; @internalProperty() private _icon = mdiCircle; @@ -57,6 +69,9 @@ class ZWaveJSConfigDashboard extends LitElement { .route=${this.route} .tabs=${configTabs} > + + +
${this.hass.localize("ui.panel.config.zwave_js.dashboard.header")} @@ -117,9 +132,10 @@ class ZWaveJSConfigDashboard extends LitElement { )}: ${this._network.controller.home_id}
${this.hass.localize( - "ui.panel.config.zwave_js.dashboard.node_count" + "ui.panel.config.zwave_js.dashboard.nodes_ready" )}: - ${this._network.controller.node_count} + ${this._nodes?.filter((node) => node.ready).length ?? 0} / + ${this._network.controller.nodes.length}
@@ -153,18 +169,36 @@ class ZWaveJSConfigDashboard extends LitElement { ` : ``} + `; } private async _fetchData() { - if (!this.configEntryId) return; + if (!this.configEntryId) { + return; + } this._network = await fetchNetworkStatus(this.hass!, this.configEntryId); this._status = this._network.client.state; if (this._status === "connected") { this._icon = mdiCheckCircle; } + this._fetchNodeStatus(); + } + + private async _fetchNodeStatus() { + if (!this._network) { + return; + } + const nodeStatePromisses = this._network.controller.nodes.map((nodeId) => + fetchNodeStatus(this.hass, this.configEntryId!, nodeId) + ); + this._nodes = await Promise.all(nodeStatePromisses); } private async _addNodeClicked() { @@ -179,6 +213,65 @@ class ZWaveJSConfigDashboard extends LitElement { }); } + private async _dumpDebugClicked() { + await this._fetchNodeStatus(); + + const notReadyNodes = this._nodes?.filter((node) => !node.ready); + const deadNodes = this._nodes?.filter( + (node) => node.status === NodeStatus.Dead + ); + + if (deadNodes?.length) { + await showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.zwave_js.dashboard.dump_dead_nodes_title" + ), + text: this.hass.localize( + "ui.panel.config.zwave_js.dashboard.dump_dead_nodes_text" + ), + }); + } + + if ( + notReadyNodes?.length && + notReadyNodes.length !== deadNodes?.length && + !(await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.zwave_js.dashboard.dump_not_ready_title" + ), + text: this.hass.localize( + "ui.panel.config.zwave_js.dashboard.dump_not_ready_text" + ), + confirmText: this.hass.localize( + "ui.panel.config.zwave_js.dashboard.dump_not_ready_confirm" + ), + })) + ) { + return; + } + + let signedPath: { path: string }; + try { + signedPath = await getSignedPath( + this.hass, + `/api/zwave_js/dump/${this.configEntryId}` + ); + } catch (err) { + showAlertDialog(this, { + title: "Error", + text: err.error || err.body || err, + }); + return; + } + + const a = document.createElement("a"); + a.href = signedPath.path; + a.download = `zwave_js_dump.jsonl`; + this.shadowRoot!.appendChild(a); + a.click(); + this.shadowRoot!.removeChild(a); + } + static get styles(): CSSResultArray { return [ haStyle, @@ -207,15 +300,10 @@ class ZWaveJSConfigDashboard extends LitElement { .network-status div.heading { display: flex; - justify-content: center; align-items: center; margin-bottom: 16px; } - .network-status { - text-align: center; - } - .network-status div.heading .icon { width: 48px; height: 48px; @@ -238,6 +326,12 @@ class ZWaveJSConfigDashboard extends LitElement { max-width: 600px; } + button.dump { + width: 100%; + text-align: center; + color: var(--secondary-text-color); + } + [hidden] { display: none; } diff --git a/src/translations/en.json b/src/translations/en.json index 7508f55947..05a1238546 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2401,7 +2401,13 @@ "driver_version": "Driver Version", "server_version": "Server Version", "home_id": "Home ID", - "node_count": "Node Count" + "nodes_ready": "Nodes ready", + "dump_debug": "Download a dump of your network to help diagnose issues", + "dump_dead_nodes_title": "Some of your nodes are dead", + "dump_dead_nodes_text": "Some of your nodes didn't respond and are assumed dead. These will not be fully exported.", + "dump_not_ready_title": "Not all nodes are ready yet", + "dump_not_ready_text": "If you create an export while not all nodes are ready, you could miss needed data. Give your network some time to query all nodes. Do you want to continue with the dump?", + "dump_not_ready_confirm": "Download" }, "device_info": { "zwave_info": "Z-Wave Info", @@ -3318,4 +3324,4 @@ } } } -} \ No newline at end of file +}