Add remove failed node support to Z-Wave JS devices (#9560)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Charles Garwood 2021-07-28 09:38:50 -04:00 committed by GitHub
parent 6ab0f1db57
commit 8cd9f891fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 304 additions and 1 deletions

View File

@ -83,6 +83,12 @@ export interface ZWaveJSHealNetworkStatusMessage {
heal_node_status: { [key: number]: string };
}
export interface ZWaveJSRemovedNode {
node_id: number;
manufacturer: string;
label: string;
}
export enum NodeStatus {
Unknown,
Asleep,
@ -189,6 +195,21 @@ export const healNode = (
node_id: node_id,
});
export const removeFailedNode = (
hass: HomeAssistant,
entry_id: string,
node_id: number,
callbackFunction: (message: any) => void
): Promise<UnsubscribeFunc> =>
hass.connection.subscribeMessage(
(message: any) => callbackFunction(message),
{
type: "zwave_js/remove_failed_node",
entry_id: entry_id,
node_id: node_id,
}
);
export const healNetwork = (
hass: HomeAssistant,
entry_id: string

View File

@ -17,6 +17,7 @@ import { haStyle } from "../../../../../../resources/styles";
import { HomeAssistant } from "../../../../../../types";
import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node";
import { showZWaveJSHealNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node";
import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node";
@customElement("ha-device-actions-zwave_js")
export class HaDeviceActionsZWaveJS extends LitElement {
@ -60,6 +61,11 @@ export class HaDeviceActionsZWaveJS extends LitElement {
<mwc-button @click=${this._healNodeClicked}>
${this.hass.localize("ui.panel.config.zwave_js.device_info.heal_node")}
</mwc-button>
<mwc-button @click=${this._removeFailedNode}>
${this.hass.localize(
"ui.panel.config.zwave_js.device_info.remove_failed"
)}
</mwc-button>
`;
}
@ -84,6 +90,16 @@ export class HaDeviceActionsZWaveJS extends LitElement {
});
}
private async _removeFailedNode() {
if (!this._nodeId || !this._entryId) {
return;
}
showZWaveJSRemoveFailedNodeDialog(this, {
entry_id: this._entryId,
node_id: this._nodeId,
});
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@ -0,0 +1,237 @@
import "@material/mwc-button/mwc-button";
import { mdiCheckCircle, mdiCloseCircle, mdiRobotDead } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-circular-progress";
import { createCloseHeading } from "../../../../../components/ha-dialog";
import {
removeFailedNode,
ZWaveJSRemovedNode,
} from "../../../../../data/zwave_js";
import { haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { ZWaveJSRemoveFailedNodeDialogParams } from "./show-dialog-zwave_js-remove-failed-node";
@customElement("dialog-zwave_js-remove-failed-node")
class DialogZWaveJSRemoveFailedNode extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private entry_id?: string;
@state() private node_id?: number;
@state() private _status = "";
@state() private _error?: any;
@state() private _node?: ZWaveJSRemovedNode;
private _subscribed?: Promise<UnsubscribeFunc | void>;
public disconnectedCallback(): void {
super.disconnectedCallback();
this._unsubscribe();
}
public async showDialog(
params: ZWaveJSRemoveFailedNodeDialogParams
): Promise<void> {
this.entry_id = params.entry_id;
this.node_id = params.node_id;
}
public closeDialog(): void {
this._unsubscribe();
this.entry_id = undefined;
this._status = "";
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
public closeDialogFinished(): void {
history.back();
this.closeDialog();
}
protected render(): TemplateResult {
if (!this.entry_id || !this.node_id) {
return html``;
}
return html`
<ha-dialog
open
@closed="${this.closeDialog}"
.heading=${createCloseHeading(
this.hass,
this.hass.localize(
"ui.panel.config.zwave_js.remove_failed_node.title"
)
)}
>
${this._status === ""
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiRobotDead}
class="introduction"
></ha-svg-icon>
<div class="status">
${this.hass.localize(
"ui.panel.config.zwave_js.remove_failed_node.introduction"
)}
</div>
</div>
<mwc-button slot="primaryAction" @click=${this._startExclusion}>
${this.hass.localize(
"ui.panel.config.zwave_js.remove_failed_node.remove_device"
)}
</mwc-button>
`
: ``}
${this._status === "started"
? html`
<div class="flex-container">
<ha-circular-progress active></ha-circular-progress>
<div class="status">
<p>
<b>
${this.hass.localize(
"ui.panel.config.zwave_js.remove_failed_node.in_progress"
)}
</b>
</p>
</div>
</div>
`
: ``}
${this._status === "failed"
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCloseCircle}
class="error"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.panel.config.zwave_js.remove_failed_node.removal_failed"
)}
</p>
${this._error
? html` <p><em> ${this._error.message} </em></p> `
: ``}
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
</mwc-button>
`
: ``}
${this._status === "finished"
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.panel.config.zwave_js.remove_failed_node.removal_finished",
"id",
this._node!.node_id
)}
</p>
</div>
</div>
<mwc-button
slot="primaryAction"
@click=${this.closeDialogFinished}
>
${this.hass.localize("ui.common.close")}
</mwc-button>
`
: ``}
</ha-dialog>
`;
}
private _startExclusion(): void {
if (!this.hass) {
return;
}
this._status = "started";
this._subscribed = removeFailedNode(
this.hass,
this.entry_id!,
this.node_id!,
(message: any) => this._handleMessage(message)
).catch((error) => {
this._status = "failed";
this._error = error;
});
}
private _handleMessage(message: any): void {
if (message.event === "exclusion started") {
this._status = "started";
}
if (message.event === "node removed") {
this._status = "finished";
this._node = message.node;
this._unsubscribe();
}
}
private async _unsubscribe(): Promise<void> {
if (this._subscribed) {
const unsubFunc = await this._subscribed;
if (unsubFunc instanceof Function) {
unsubFunc();
}
this._subscribed = undefined;
}
if (this._status !== "finished") {
this._status = "";
}
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
.success {
color: var(--success-color);
}
.failed {
color: var(--warning-color);
}
.flex-container {
display: flex;
align-items: center;
}
ha-svg-icon {
width: 68px;
height: 48px;
}
.flex-container ha-circular-progress,
.flex-container ha-svg-icon {
margin-right: 20px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-zwave_js-remove-failed-node": DialogZWaveJSRemoveFailedNode;
}
}

View File

@ -0,0 +1,20 @@
import { fireEvent } from "../../../../../common/dom/fire_event";
export interface ZWaveJSRemoveFailedNodeDialogParams {
entry_id: string;
node_id: number;
}
export const loadRemoveFailedNodeDialog = () =>
import("./dialog-zwave_js-remove-failed-node");
export const showZWaveJSRemoveFailedNodeDialog = (
element: HTMLElement,
removeFailedNodeDialogParams: ZWaveJSRemoveFailedNodeDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-zwave_js-remove-failed-node",
dialogImport: loadRemoveFailedNodeDialog,
dialogParams: removeFailedNodeDialogParams,
});
};

View File

@ -2691,7 +2691,8 @@
"node_ready": "Node Ready",
"device_config": "Configure Device",
"reinterview_device": "Re-interview Device",
"heal_node": "Heal Node"
"heal_node": "Heal Node",
"remove_failed": "Remove Failed Device"
},
"node_config": {
"header": "Z-Wave Device Configuration",
@ -2744,6 +2745,14 @@
"exclusion_failed": "The node could not be removed. Please check the logs for more information.",
"exclusion_finished": "Node {id} has been removed from your Z-Wave network."
},
"remove_failed_node": {
"title": "Remove a Failed Z-Wave Device",
"introduction": "Remove a failed device from your Z-Wave network. Use this if you are unable to exclude a device normally because it is broken.",
"remove_device": "Remove Device",
"in_progress": "The device removal is in progress.",
"removal_finished": "Node {id} has been removed from your Z-Wave network.",
"removal_failed": "The device could not be removed from your Z-Wave network."
},
"reinterview_node": {
"title": "Re-interview a Z-Wave Device",
"introduction": "Re-interview a device on your Z-Wave network. Use this feature if your device has missing or incorrect functionality.",