Add provisioned device overview to zwave js (#10785)

This commit is contained in:
Bram Kragten 2021-12-03 17:34:26 +01:00 committed by GitHub
parent db4aa05bf4
commit c71b2e6b9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 197 additions and 8 deletions

View File

@ -205,6 +205,16 @@ export const enum NodeStatus {
Alive,
}
export interface ZwaveJSProvisioningEntry {
/** The device specific key (DSK) in the form aaaaa-bbbbb-ccccc-ddddd-eeeee-fffff-11111-22222 */
dsk: string;
securityClasses: SecurityClass[];
/**
* Additional properties to be stored in this provisioning entry, e.g. the device ID from a scanned QR code
*/
[prop: string]: any;
}
export interface RequestedGrant {
/**
* An array of security classes that are requested or to be granted.
@ -265,6 +275,15 @@ export const setZwaveDataCollectionPreference = (
opted_in,
});
export const fetchZwaveProvisioningEntries = (
hass: HomeAssistant,
entry_id: string
): Promise<any> =>
hass.callWS({
type: "zwave_js/get_provisioning_entries",
entry_id,
});
export const subscribeAddZwaveNode = (
hass: HomeAssistant,
entry_id: string,
@ -350,6 +369,19 @@ export const provisionZwaveSmartStartNode = (
planned_provisioning_entry,
});
export const unprovisionZwaveSmartStartNode = (
hass: HomeAssistant,
entry_id: string,
dsk?: string,
node_id?: number
): Promise<QRProvisioningInformation> =>
hass.callWS({
type: "zwave_js/unprovision_smart_start_node",
entry_id,
dsk,
node_id,
});
export const fetchZwaveNodeStatus = (
hass: HomeAssistant,
entry_id: string,

View File

@ -171,7 +171,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
>
${OVERRIDE_DEVICE_CLASSES[domain].map(
(deviceClass: string) => html`
<paper-item .itemValue=${String(deviceClass)}>
<paper-item .itemValue=${deviceClass}>
${this.hass.localize(
`ui.dialogs.entity_registry.editor.device_classes.${domain}.${deviceClass}`
)}
@ -307,8 +307,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
if (ev.detail.value === null) {
return;
}
const value = (ev.detail.value as any).itemValue;
this._deviceClass = value === "null" ? null : value;
this._deviceClass = (ev.detail.value as any).itemValue;
}
private _areaPicked(ev: CustomEvent) {

View File

@ -19,10 +19,12 @@ import {
fetchZwaveDataCollectionStatus,
fetchZwaveNetworkStatus,
fetchZwaveNodeStatus,
fetchZwaveProvisioningEntries,
NodeStatus,
setZwaveDataCollectionPreference,
ZWaveJSNetwork,
ZWaveJSNodeStatus,
ZwaveJSProvisioningEntry,
} from "../../../../../data/zwave_js";
import {
ConfigEntry,
@ -63,6 +65,8 @@ class ZWaveJSConfigDashboard extends LitElement {
@state() private _nodes?: ZWaveJSNodeStatus[];
@state() private _provisioningEntries?: ZwaveJSProvisioningEntry[];
@state() private _status = "unknown";
@state() private _icon = mdiCircle;
@ -85,7 +89,7 @@ class ZWaveJSConfigDashboard extends LitElement {
}
const notReadyDevices =
this._nodes?.filter((node) => node.ready).length ?? 0;
this._nodes?.filter((node) => !node.ready).length ?? 0;
return html`
<hass-tabs-subpage
@ -176,6 +180,16 @@ class ZWaveJSConfigDashboard extends LitElement {
)}
</mwc-button>
</a>
${this._provisioningEntries?.length
? html`<a
href=${`provisioned?config_entry=${this.configEntryId}`}
><mwc-button>
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.provisioned_devices"
)}
</mwc-button></a
>`
: ""}
</div>
</ha-card>
<ha-card header="Diagnostics">
@ -361,10 +375,14 @@ class ZWaveJSConfigDashboard extends LitElement {
return;
}
const [network, dataCollectionStatus] = await Promise.all([
fetchZwaveNetworkStatus(this.hass!, this.configEntryId),
fetchZwaveDataCollectionStatus(this.hass!, this.configEntryId),
]);
const [network, dataCollectionStatus, provisioningEntries] =
await Promise.all([
fetchZwaveNetworkStatus(this.hass!, this.configEntryId),
fetchZwaveDataCollectionStatus(this.hass!, this.configEntryId),
fetchZwaveProvisioningEntries(this.hass!, this.configEntryId),
]);
this._provisioningEntries = provisioningEntries;
this._network = network;

View File

@ -49,6 +49,10 @@ class ZWaveJSConfigRouter extends HassRouterPage {
tag: "zwave_js-logs",
load: () => import("./zwave_js-logs"),
},
provisioned: {
tag: "zwave_js-provisioned",
load: () => import("./zwave_js-provisioned"),
},
},
};

View File

@ -0,0 +1,128 @@
import { mdiDelete } from "@mdi/js";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { DataTableColumnContainer } from "../../../../../components/data-table/ha-data-table";
import {
ZwaveJSProvisioningEntry,
fetchZwaveProvisioningEntries,
SecurityClass,
unprovisionZwaveSmartStartNode,
} from "../../../../../data/zwave_js";
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
import "../../../../../layouts/hass-tabs-subpage-data-table";
import { HomeAssistant, Route } from "../../../../../types";
import { configTabs } from "./zwave_js-config-router";
@customElement("zwave_js-provisioned")
class ZWaveJSProvisioned extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Object }) public route!: Route;
@property({ type: Boolean }) public narrow!: boolean;
@property() public configEntryId!: string;
@state() private _provisioningEntries: ZwaveJSProvisioningEntry[] = [];
protected render() {
return html`
<hass-tabs-subpage-data-table
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${configTabs}
.columns=${this._columns(this.narrow)}
.data=${this._provisioningEntries}
>
</hass-tabs-subpage-data-table>
`;
}
private _columns = memoizeOne(
(narrow: boolean): DataTableColumnContainer => ({
dsk: {
title: this.hass.localize("ui.panel.config.zwave_js.provisioned.dsk"),
sortable: true,
filterable: true,
grows: true,
},
securityClasses: {
title: this.hass.localize(
"ui.panel.config.zwave_js.provisioned.security_classes"
),
width: "15%",
hidden: narrow,
filterable: true,
sortable: true,
template: (securityClasses: SecurityClass[]) =>
securityClasses
.map((secClass) =>
this.hass.localize(
`ui.panel.config.zwave_js.security_classes.${SecurityClass[secClass]}`
)
)
.join(", "),
},
unprovision: {
title: this.hass.localize(
"ui.panel.config.zwave_js.provisioned.unprovison"
),
type: "icon-button",
template: (_info, provisioningEntry: any) => html`
<ha-icon-button
.label=${this.hass.localize(
"ui.panel.config.zwave_js.provisioned.unprovison"
)}
.path=${mdiDelete}
.provisioningEntry=${provisioningEntry}
@click=${this._unprovision}
></ha-icon-button>
`,
},
})
);
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._fetchData();
}
private async _fetchData() {
this._provisioningEntries = await fetchZwaveProvisioningEntries(
this.hass!,
this.configEntryId
);
}
private _unprovision = async (ev) => {
const confirm = await showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.config.zwave_js.provisioned.confirm_unprovision_title"
),
text: this.hass.localize(
"ui.panel.config.zwave_js.provisioned.confirm_unprovision_text"
),
confirmText: this.hass.localize(
"ui.panel.config.zwave_js.provisioned.unprovison"
),
});
if (!confirm) {
return;
}
await unprovisionZwaveSmartStartNode(
this.hass,
this.configEntryId,
ev.currentTarget.provisioningEntry.dsk
);
};
}
declare global {
interface HTMLElementTagNameMap {
"zwave_js-provisioned": ZWaveJSProvisioned;
}
}

View File

@ -2828,6 +2828,7 @@
"home_id": "Home ID",
"server_url": "Server URL",
"devices": "{count} {count, plural,\n one {device}\n other {devices}\n}",
"provisioned_devices": "Provisioned devices",
"not_ready": "{count} not ready",
"dump_debug": "Download data",
"dump_dead_nodes_title": "Some of your devices are dead",
@ -2892,6 +2893,13 @@
"interview_started": "The device is being interviewed. This may take some time.",
"interview_failed": "The device interview failed. Additional information may be available in the logs."
},
"provisioned": {
"dsk": "DSK",
"security_classes": "Security classes",
"unprovison": "Unprovison",
"confirm_unprovision_title": "Are you sure you want to unprovision the device?",
"confirm_unprovision_text": "If you unprovision the device it will not be added to Home Assistant when it is powered on. If it is already added to Home Assistant, removing the provisioned device will not remove it from Home Assistant."
},
"security_classes": {
"None": {
"title": "None"