Add SmartStart/QR scan support for Z-Wave JS (#10726)
parent
68373e6372
commit
4b49da58b1
|
@ -79,6 +79,11 @@ function copyFonts(staticDir) {
|
|||
);
|
||||
}
|
||||
|
||||
function copyQrScannerWorker(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js"));
|
||||
}
|
||||
|
||||
function copyMapPanel(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(
|
||||
|
@ -125,6 +130,9 @@ gulp.task("copy-static-app", async () => {
|
|||
|
||||
// Panel assets
|
||||
copyMapPanel(staticDir);
|
||||
|
||||
// Qr Scanner assets
|
||||
copyQrScannerWorker(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-static-demo", async () => {
|
||||
|
|
|
@ -115,6 +115,7 @@
|
|||
"node-vibrant": "3.2.1-alpha.1",
|
||||
"proxy-polyfill": "^0.3.2",
|
||||
"punycode": "^2.1.1",
|
||||
"qr-scanner": "^1.3.0",
|
||||
"qrcode": "^1.4.4",
|
||||
"regenerator-runtime": "^0.13.8",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@material/mwc-select/mwc-select";
|
||||
import type { Select } from "@material/mwc-select/mwc-select";
|
||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import type QrScanner from "qr-scanner";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { LocalizeFunc } from "../common/translations/localize";
|
||||
import "./ha-alert";
|
||||
|
||||
@customElement("ha-qr-scanner")
|
||||
class HaQrScanner extends LitElement {
|
||||
@property() localize!: LocalizeFunc;
|
||||
|
||||
@state() private _cameras?: QrScanner.Camera[];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
private _qrScanner?: QrScanner;
|
||||
|
||||
private _qrNotFoundCount = 0;
|
||||
|
||||
@query("video", true) private _video!: HTMLVideoElement;
|
||||
|
||||
@query("#canvas-container", true) private _canvasContainer!: HTMLDivElement;
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._qrNotFoundCount = 0;
|
||||
if (this._qrScanner) {
|
||||
this._qrScanner.stop();
|
||||
this._qrScanner.destroy();
|
||||
this._qrScanner = undefined;
|
||||
}
|
||||
while (this._canvasContainer.lastChild) {
|
||||
this._canvasContainer.removeChild(this._canvasContainer.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this.hasUpdated && navigator.mediaDevices) {
|
||||
this._loadQrScanner();
|
||||
}
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
if (navigator.mediaDevices) {
|
||||
this._loadQrScanner();
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
if (changedProps.has("_error") && this._error) {
|
||||
fireEvent(this, "qr-code-error", { message: this._error });
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`${this._cameras && this._cameras.length > 1
|
||||
? html`<mwc-select
|
||||
.label=${this.localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_camera"
|
||||
)}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@closed=${stopPropagation}
|
||||
@selected=${this._cameraChanged}
|
||||
>
|
||||
${this._cameras!.map(
|
||||
(camera) => html`
|
||||
<mwc-list-item .value=${camera.id}>${camera.label}</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</mwc-select>`
|
||||
: ""}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${navigator.mediaDevices
|
||||
? html`<video></video>
|
||||
<div id="canvas-container"></div>`
|
||||
: html`<ha-alert alert-type="warning"
|
||||
>${!window.isSecureContext
|
||||
? "You can only use your camera to scan a QR core when using HTTPS."
|
||||
: "Your browser doesn't support QR scanning."}</ha-alert
|
||||
>`}`;
|
||||
}
|
||||
|
||||
private async _loadQrScanner() {
|
||||
const QrScanner = (await import("qr-scanner")).default;
|
||||
if (!(await QrScanner.hasCamera())) {
|
||||
this._error = "No camera found";
|
||||
return;
|
||||
}
|
||||
QrScanner.WORKER_PATH = "/static/js/qr-scanner-worker.min.js";
|
||||
this._listCameras(QrScanner);
|
||||
this._qrScanner = new QrScanner(
|
||||
this._video,
|
||||
this._qrCodeScanned,
|
||||
this._qrCodeError
|
||||
);
|
||||
// @ts-ignore
|
||||
const canvas = this._qrScanner.$canvas;
|
||||
this._canvasContainer.appendChild(canvas);
|
||||
canvas.style.display = "block";
|
||||
try {
|
||||
await this._qrScanner.start();
|
||||
} catch (err: any) {
|
||||
this._error = err;
|
||||
}
|
||||
}
|
||||
|
||||
private async _listCameras(qrScanner: typeof QrScanner): Promise<void> {
|
||||
this._cameras = await qrScanner.listCameras(true);
|
||||
}
|
||||
|
||||
private _qrCodeError = (err: any) => {
|
||||
if (err === "No QR code found") {
|
||||
this._qrNotFoundCount++;
|
||||
if (this._qrNotFoundCount === 250) {
|
||||
this._error = err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._error = err.message || err;
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err);
|
||||
};
|
||||
|
||||
private _qrCodeScanned = async (qrCodeString: string): Promise<void> => {
|
||||
this._qrNotFoundCount = 0;
|
||||
fireEvent(this, "qr-code-scanned", { value: qrCodeString });
|
||||
};
|
||||
|
||||
private _cameraChanged(ev: CustomEvent): void {
|
||||
this._qrScanner?.setCamera((ev.target as Select).value);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
canvas {
|
||||
width: 100%;
|
||||
}
|
||||
mwc-select {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"qr-code-scanned": { value: string };
|
||||
"qr-code-error": { message: string };
|
||||
}
|
||||
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-qr-scanner": HaQrScanner;
|
||||
}
|
||||
}
|
|
@ -57,6 +57,45 @@ export enum SecurityClass {
|
|||
S0_Legacy = 7,
|
||||
}
|
||||
|
||||
/** A named list of Z-Wave features */
|
||||
export enum ZWaveFeature {
|
||||
// Available starting with Z-Wave SDK 6.81
|
||||
SmartStart,
|
||||
}
|
||||
|
||||
enum QRCodeVersion {
|
||||
S2 = 0,
|
||||
SmartStart = 1,
|
||||
}
|
||||
|
||||
enum Protocols {
|
||||
ZWave = 0,
|
||||
ZWaveLongRange = 1,
|
||||
}
|
||||
export interface QRProvisioningInformation {
|
||||
version: QRCodeVersion;
|
||||
securityClasses: SecurityClass[];
|
||||
dsk: string;
|
||||
genericDeviceClass: number;
|
||||
specificDeviceClass: number;
|
||||
installerIconType: number;
|
||||
manufacturerId: number;
|
||||
productType: number;
|
||||
productId: number;
|
||||
applicationVersion: string;
|
||||
maxInclusionRequestInterval?: number | undefined;
|
||||
uuid?: string | undefined;
|
||||
supportedProtocols?: Protocols[] | undefined;
|
||||
}
|
||||
|
||||
export interface PlannedProvisioningEntry {
|
||||
/** The device specific key (DSK) in the form aaaaa-bbbbb-ccccc-ddddd-eeeee-fffff-11111-22222 */
|
||||
dsk: string;
|
||||
security_classes: SecurityClass[];
|
||||
}
|
||||
|
||||
export const MINIMUM_QR_STRING_LENGTH = 52;
|
||||
|
||||
export interface ZWaveJSNodeIdentifiers {
|
||||
home_id: string;
|
||||
node_id: number;
|
||||
|
@ -197,7 +236,7 @@ export const migrateZwave = (
|
|||
dry_run,
|
||||
});
|
||||
|
||||
export const fetchNetworkStatus = (
|
||||
export const fetchZwaveNetworkStatus = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string
|
||||
): Promise<ZWaveJSNetwork> =>
|
||||
|
@ -206,7 +245,7 @@ export const fetchNetworkStatus = (
|
|||
entry_id,
|
||||
});
|
||||
|
||||
export const fetchDataCollectionStatus = (
|
||||
export const fetchZwaveDataCollectionStatus = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string
|
||||
): Promise<ZWaveJSDataCollectionStatus> =>
|
||||
|
@ -215,7 +254,7 @@ export const fetchDataCollectionStatus = (
|
|||
entry_id,
|
||||
});
|
||||
|
||||
export const setDataCollectionPreference = (
|
||||
export const setZwaveDataCollectionPreference = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
opted_in: boolean
|
||||
|
@ -226,25 +265,31 @@ export const setDataCollectionPreference = (
|
|||
opted_in,
|
||||
});
|
||||
|
||||
export const subscribeAddNode = (
|
||||
export const subscribeAddZwaveNode = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
callbackFunction: (message: any) => void,
|
||||
inclusion_strategy: InclusionStrategy = InclusionStrategy.Default
|
||||
inclusion_strategy: InclusionStrategy = InclusionStrategy.Default,
|
||||
qr_provisioning_information?: QRProvisioningInformation,
|
||||
qr_code_string?: string,
|
||||
planned_provisioning_entry?: PlannedProvisioningEntry
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
hass.connection.subscribeMessage((message) => callbackFunction(message), {
|
||||
type: "zwave_js/add_node",
|
||||
entry_id: entry_id,
|
||||
inclusion_strategy,
|
||||
qr_code_string,
|
||||
qr_provisioning_information,
|
||||
planned_provisioning_entry,
|
||||
});
|
||||
|
||||
export const stopInclusion = (hass: HomeAssistant, entry_id: string) =>
|
||||
export const stopZwaveInclusion = (hass: HomeAssistant, entry_id: string) =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/stop_inclusion",
|
||||
entry_id,
|
||||
});
|
||||
|
||||
export const grantSecurityClasses = (
|
||||
export const zwaveGrantSecurityClasses = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
security_classes: SecurityClass[],
|
||||
|
@ -257,7 +302,7 @@ export const grantSecurityClasses = (
|
|||
client_side_auth,
|
||||
});
|
||||
|
||||
export const validateDskAndEnterPin = (
|
||||
export const zwaveValidateDskAndEnterPin = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
pin: string
|
||||
|
@ -268,7 +313,44 @@ export const validateDskAndEnterPin = (
|
|||
pin,
|
||||
});
|
||||
|
||||
export const fetchNodeStatus = (
|
||||
export const zwaveSupportsFeature = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
feature: ZWaveFeature
|
||||
): Promise<{ supported: boolean }> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/supports_feature",
|
||||
entry_id,
|
||||
feature,
|
||||
});
|
||||
|
||||
export const zwaveParseQrCode = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
qr_code_string: string
|
||||
): Promise<QRProvisioningInformation> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/parse_qr_code_string",
|
||||
entry_id,
|
||||
qr_code_string,
|
||||
});
|
||||
|
||||
export const provisionZwaveSmartStartNode = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
qr_provisioning_information?: QRProvisioningInformation,
|
||||
qr_code_string?: string,
|
||||
planned_provisioning_entry?: PlannedProvisioningEntry
|
||||
): Promise<QRProvisioningInformation> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/provision_smart_start_node",
|
||||
entry_id,
|
||||
qr_code_string,
|
||||
qr_provisioning_information,
|
||||
planned_provisioning_entry,
|
||||
});
|
||||
|
||||
export const fetchZwaveNodeStatus = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number
|
||||
|
@ -279,7 +361,7 @@ export const fetchNodeStatus = (
|
|||
node_id,
|
||||
});
|
||||
|
||||
export const fetchNodeMetadata = (
|
||||
export const fetchZwaveNodeMetadata = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number
|
||||
|
@ -290,7 +372,7 @@ export const fetchNodeMetadata = (
|
|||
node_id,
|
||||
});
|
||||
|
||||
export const fetchNodeConfigParameters = (
|
||||
export const fetchZwaveNodeConfigParameters = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number
|
||||
|
@ -301,7 +383,7 @@ export const fetchNodeConfigParameters = (
|
|||
node_id,
|
||||
});
|
||||
|
||||
export const setNodeConfigParameter = (
|
||||
export const setZwaveNodeConfigParameter = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number,
|
||||
|
@ -320,7 +402,7 @@ export const setNodeConfigParameter = (
|
|||
return hass.callWS(data);
|
||||
};
|
||||
|
||||
export const reinterviewNode = (
|
||||
export const reinterviewZwaveNode = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number,
|
||||
|
@ -335,7 +417,7 @@ export const reinterviewNode = (
|
|||
}
|
||||
);
|
||||
|
||||
export const healNode = (
|
||||
export const healZwaveNode = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number
|
||||
|
@ -346,7 +428,7 @@ export const healNode = (
|
|||
node_id,
|
||||
});
|
||||
|
||||
export const removeFailedNode = (
|
||||
export const removeFailedZwaveNode = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number,
|
||||
|
@ -361,7 +443,7 @@ export const removeFailedNode = (
|
|||
}
|
||||
);
|
||||
|
||||
export const healNetwork = (
|
||||
export const healZwaveNetwork = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
|
@ -370,7 +452,7 @@ export const healNetwork = (
|
|||
entry_id,
|
||||
});
|
||||
|
||||
export const stopHealNetwork = (
|
||||
export const stopHealZwaveNetwork = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
|
@ -379,7 +461,7 @@ export const stopHealNetwork = (
|
|||
entry_id,
|
||||
});
|
||||
|
||||
export const subscribeNodeReady = (
|
||||
export const subscribeZwaveNodeReady = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number,
|
||||
|
@ -394,7 +476,7 @@ export const subscribeNodeReady = (
|
|||
}
|
||||
);
|
||||
|
||||
export const subscribeHealNetworkProgress = (
|
||||
export const subscribeHealZwaveNetworkProgress = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
callbackFunction: (message: ZWaveJSHealNetworkStatusMessage) => void
|
||||
|
@ -407,7 +489,7 @@ export const subscribeHealNetworkProgress = (
|
|||
}
|
||||
);
|
||||
|
||||
export const getIdentifiersFromDevice = (
|
||||
export const getZwaveJsIdentifiersFromDevice = (
|
||||
device: DeviceRegistryEntry
|
||||
): ZWaveJSNodeIdentifiers | undefined => {
|
||||
if (!device) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
import { customElement, property, state } from "lit/decorators";
|
||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
||||
import {
|
||||
getIdentifiersFromDevice,
|
||||
getZwaveJsIdentifiersFromDevice,
|
||||
ZWaveJSNodeIdentifiers,
|
||||
} from "../../../../../../data/zwave_js";
|
||||
import { haStyle } from "../../../../../../resources/styles";
|
||||
|
@ -34,7 +34,7 @@ export class HaDeviceActionsZWaveJS extends LitElement {
|
|||
this._entryId = this.device.config_entries[0];
|
||||
|
||||
const identifiers: ZWaveJSNodeIdentifiers | undefined =
|
||||
getIdentifiersFromDevice(this.device);
|
||||
getZwaveJsIdentifiersFromDevice(this.device);
|
||||
if (!identifiers) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ import {
|
|||
getConfigEntries,
|
||||
} from "../../../../../../data/config_entries";
|
||||
import {
|
||||
fetchNodeStatus,
|
||||
getIdentifiersFromDevice,
|
||||
fetchZwaveNodeStatus,
|
||||
getZwaveJsIdentifiersFromDevice,
|
||||
nodeStatus,
|
||||
ZWaveJSNodeStatus,
|
||||
ZWaveJSNodeIdentifiers,
|
||||
|
@ -42,7 +42,7 @@ export class HaDeviceInfoZWaveJS extends LitElement {
|
|||
protected updated(changedProperties: PropertyValues) {
|
||||
if (changedProperties.has("device")) {
|
||||
const identifiers: ZWaveJSNodeIdentifiers | undefined =
|
||||
getIdentifiersFromDevice(this.device);
|
||||
getZwaveJsIdentifiersFromDevice(this.device);
|
||||
if (!identifiers) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,7 +76,11 @@ export class HaDeviceInfoZWaveJS extends LitElement {
|
|||
zwaveJsConfEntries++;
|
||||
}
|
||||
|
||||
this._node = await fetchNodeStatus(this.hass, this._entryId, this._nodeId);
|
||||
this._node = await fetchZwaveNodeStatus(
|
||||
this.hass,
|
||||
this._entryId,
|
||||
this._nodeId
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
|
|
|
@ -21,10 +21,10 @@ import {
|
|||
import {
|
||||
migrateZwave,
|
||||
ZWaveJsMigrationData,
|
||||
fetchNetworkStatus as fetchZwaveJsNetworkStatus,
|
||||
fetchNodeStatus,
|
||||
getIdentifiersFromDevice,
|
||||
subscribeNodeReady,
|
||||
fetchZwaveNetworkStatus as fetchZwaveJsNetworkStatus,
|
||||
fetchZwaveNodeStatus,
|
||||
getZwaveJsIdentifiersFromDevice,
|
||||
subscribeZwaveNodeReady,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import {
|
||||
fetchMigrationConfig,
|
||||
|
@ -425,7 +425,7 @@ export class ZwaveMigration extends LitElement {
|
|||
this._zwaveJsEntryId!
|
||||
);
|
||||
const nodeStatePromisses = networkStatus.controller.nodes.map((nodeId) =>
|
||||
fetchNodeStatus(this.hass, this._zwaveJsEntryId!, nodeId)
|
||||
fetchZwaveNodeStatus(this.hass, this._zwaveJsEntryId!, nodeId)
|
||||
);
|
||||
const nodesNotReady = (await Promise.all(nodeStatePromisses)).filter(
|
||||
(node) => !node.ready
|
||||
|
@ -436,13 +436,18 @@ export class ZwaveMigration extends LitElement {
|
|||
return;
|
||||
}
|
||||
this._nodeReadySubscriptions = nodesNotReady.map((node) =>
|
||||
subscribeNodeReady(this.hass, this._zwaveJsEntryId!, node.node_id, () => {
|
||||
subscribeZwaveNodeReady(
|
||||
this.hass,
|
||||
this._zwaveJsEntryId!,
|
||||
node.node_id,
|
||||
() => {
|
||||
this._getZwaveJSNodesStatus();
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
const deviceReg = await fetchDeviceRegistry(this.hass);
|
||||
this._waitingOnDevices = deviceReg
|
||||
.map((device) => getIdentifiersFromDevice(device))
|
||||
.map((device) => getZwaveJsIdentifiersFromDevice(device))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +1,40 @@
|
|||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiAlertCircle, mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
|
||||
import type { TextField } from "@material/mwc-textfield/mwc-textfield";
|
||||
import "@material/mwc-textfield/mwc-textfield";
|
||||
import { mdiAlertCircle, mdiCheckCircle, mdiQrcodeScan } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-alert";
|
||||
import { HaCheckbox } from "../../../../../components/ha-checkbox";
|
||||
import "../../../../../components/ha-circular-progress";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import "../../../../../components/ha-formfield";
|
||||
import "../../../../../components/ha-radio";
|
||||
import "../../../../../components/ha-switch";
|
||||
import {
|
||||
grantSecurityClasses,
|
||||
zwaveGrantSecurityClasses,
|
||||
InclusionStrategy,
|
||||
MINIMUM_QR_STRING_LENGTH,
|
||||
zwaveParseQrCode,
|
||||
provisionZwaveSmartStartNode,
|
||||
QRProvisioningInformation,
|
||||
RequestedGrant,
|
||||
SecurityClass,
|
||||
stopInclusion,
|
||||
subscribeAddNode,
|
||||
validateDskAndEnterPin,
|
||||
stopZwaveInclusion,
|
||||
subscribeAddZwaveNode,
|
||||
zwaveSupportsFeature,
|
||||
zwaveValidateDskAndEnterPin,
|
||||
ZWaveFeature,
|
||||
PlannedProvisioningEntry,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import { haStyle, haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZWaveJSAddNodeDialogParams } from "./show-dialog-zwave_js-add-node";
|
||||
import "../../../../../components/ha-radio";
|
||||
import { HaCheckbox } from "../../../../../components/ha-checkbox";
|
||||
import "../../../../../components/ha-alert";
|
||||
import "../../../../../components/ha-qr-scanner";
|
||||
|
||||
export interface ZWaveJSAddNodeDevice {
|
||||
id: string;
|
||||
|
@ -40,11 +50,14 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
@state() private _status?:
|
||||
| "loading"
|
||||
| "started"
|
||||
| "started_specific"
|
||||
| "choose_strategy"
|
||||
| "qr_scan"
|
||||
| "interviewing"
|
||||
| "failed"
|
||||
| "timed_out"
|
||||
| "finished"
|
||||
| "provisioned"
|
||||
| "validate_dsk_enter_pin"
|
||||
| "grant_security_classes";
|
||||
|
||||
|
@ -64,10 +77,14 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
|
||||
@state() private _lowSecurity = false;
|
||||
|
||||
@state() private _supportsSmartStart?: boolean;
|
||||
|
||||
private _addNodeTimeoutHandle?: number;
|
||||
|
||||
private _subscribed?: Promise<UnsubscribeFunc>;
|
||||
|
||||
private _qrProcessing = false;
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._unsubscribe();
|
||||
|
@ -76,6 +93,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
public async showDialog(params: ZWaveJSAddNodeDialogParams): Promise<void> {
|
||||
this._entryId = params.entry_id;
|
||||
this._status = "loading";
|
||||
this._checkSmartStartSupport();
|
||||
this._startInclusion();
|
||||
}
|
||||
|
||||
|
@ -157,6 +175,22 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
>
|
||||
Search device
|
||||
</mwc-button>`
|
||||
: this._status === "qr_scan"
|
||||
? html`<ha-qr-scanner
|
||||
.localize=${this.hass.localize}
|
||||
@qr-code-scanned=${this._qrCodeScanned}
|
||||
></ha-qr-scanner>
|
||||
<p>
|
||||
If scanning doesn't work, you can enter the QR code value
|
||||
manually:
|
||||
</p>
|
||||
<mwc-textfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.enter_qr_code"
|
||||
)}
|
||||
.disabled=${this._qrProcessing}
|
||||
@keydown=${this._qrKeyDown}
|
||||
></mwc-textfield>`
|
||||
: this._status === "validate_dsk_enter_pin"
|
||||
? html`
|
||||
<p>
|
||||
|
@ -241,18 +275,28 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
Retry
|
||||
</mwc-button>
|
||||
`
|
||||
: this._status === "started_specific"
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.searching_device"
|
||||
)}
|
||||
</h3>
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.follow_device_instructions"
|
||||
)}
|
||||
</p>`
|
||||
: this._status === "started"
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<div class="select-inclusion">
|
||||
<div class="outline">
|
||||
<h2>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.searching_device"
|
||||
)}
|
||||
</h2>
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<div class="status">
|
||||
<p>
|
||||
<b
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.controller_in_inclusion_mode"
|
||||
)}</b
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.follow_device_instructions"
|
||||
|
@ -263,15 +307,37 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
class="link"
|
||||
@click=${this._chooseInclusionStrategy}
|
||||
>
|
||||
Advanced inclusion
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.choose_inclusion_strategy"
|
||||
)}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
${this._supportsSmartStart
|
||||
? html` <div class="outline">
|
||||
<h2>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.qr_code"
|
||||
)}
|
||||
</h2>
|
||||
<ha-svg-icon .path=${mdiQrcodeScan}></ha-svg-icon>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.qr_code_paragraph"
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
<mwc-button @click=${this._scanQRCode}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.scan_qr_code"
|
||||
)}
|
||||
</mwc-button>
|
||||
</p>
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.cancel_inclusion"
|
||||
)}
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</mwc-button>
|
||||
`
|
||||
: this._status === "interviewing"
|
||||
|
@ -310,16 +376,18 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
: this._status === "failed"
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<ha-svg-icon
|
||||
.path=${mdiCloseCircle}
|
||||
class="failed"
|
||||
></ha-svg-icon>
|
||||
<div class="status">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
<ha-alert
|
||||
alert-type="error"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.inclusion_failed"
|
||||
)}
|
||||
</p>
|
||||
>
|
||||
${this._error ||
|
||||
this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.check_logs"
|
||||
)}
|
||||
</ha-alert>
|
||||
${this._stages
|
||||
? html` <div class="stages">
|
||||
${this._stages.map(
|
||||
|
@ -391,6 +459,23 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
${this.hass.localize("ui.panel.config.zwave_js.common.close")}
|
||||
</mwc-button>
|
||||
`
|
||||
: this._status === "provisioned"
|
||||
? 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.add_node.provisioning_finished"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.panel.config.zwave_js.common.close")}
|
||||
</mwc-button>`
|
||||
: ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
|
@ -417,6 +502,83 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
}
|
||||
}
|
||||
|
||||
private async _scanQRCode(): Promise<void> {
|
||||
this._unsubscribe();
|
||||
this._status = "qr_scan";
|
||||
}
|
||||
|
||||
private _qrKeyDown(ev: KeyboardEvent) {
|
||||
if (this._qrProcessing) {
|
||||
return;
|
||||
}
|
||||
if (ev.key === "Enter") {
|
||||
this._handleQrCodeScanned((ev.target as TextField).value);
|
||||
}
|
||||
}
|
||||
|
||||
private _qrCodeScanned(ev: CustomEvent): void {
|
||||
if (this._qrProcessing) {
|
||||
return;
|
||||
}
|
||||
this._handleQrCodeScanned(ev.detail.value);
|
||||
}
|
||||
|
||||
private async _handleQrCodeScanned(qrCodeString: string): Promise<void> {
|
||||
this._error = undefined;
|
||||
if (this._status !== "qr_scan" || this._qrProcessing) {
|
||||
return;
|
||||
}
|
||||
this._qrProcessing = true;
|
||||
if (
|
||||
qrCodeString.length < MINIMUM_QR_STRING_LENGTH ||
|
||||
!qrCodeString.startsWith("90")
|
||||
) {
|
||||
this._qrProcessing = false;
|
||||
this._error = `Invalid QR code (${qrCodeString})`;
|
||||
return;
|
||||
}
|
||||
let provisioningInfo: QRProvisioningInformation;
|
||||
try {
|
||||
provisioningInfo = await zwaveParseQrCode(
|
||||
this.hass,
|
||||
this._entryId!,
|
||||
qrCodeString
|
||||
);
|
||||
} catch (err: any) {
|
||||
this._qrProcessing = false;
|
||||
this._error = err.message;
|
||||
return;
|
||||
}
|
||||
this._status = "loading";
|
||||
// wait for QR scanner to be removed before resetting qr processing
|
||||
this.updateComplete.then(() => {
|
||||
this._qrProcessing = false;
|
||||
});
|
||||
if (provisioningInfo.version === 1) {
|
||||
try {
|
||||
await provisionZwaveSmartStartNode(
|
||||
this.hass,
|
||||
this._entryId!,
|
||||
provisioningInfo
|
||||
);
|
||||
this._status = "provisioned";
|
||||
} catch (err: any) {
|
||||
this._error = err.message;
|
||||
this._status = "failed";
|
||||
}
|
||||
} else if (provisioningInfo.version === 0) {
|
||||
this._inclusionStrategy = InclusionStrategy.Security_S2;
|
||||
// this._startInclusion(provisioningInfo);
|
||||
this._startInclusion(undefined, undefined, {
|
||||
dsk: "34673-15546-46480-39591-32400-22155-07715-45994",
|
||||
security_classes: [0, 1, 7],
|
||||
});
|
||||
} else {
|
||||
this._error = "This QR code is not supported";
|
||||
this._status = "failed";
|
||||
}
|
||||
}
|
||||
|
||||
private _handlePinKeyUp(ev: KeyboardEvent) {
|
||||
if (ev.key === "Enter") {
|
||||
this._validateDskAndEnterPin();
|
||||
|
@ -427,7 +589,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
this._status = "loading";
|
||||
this._error = undefined;
|
||||
try {
|
||||
await validateDskAndEnterPin(
|
||||
await zwaveValidateDskAndEnterPin(
|
||||
this.hass,
|
||||
this._entryId!,
|
||||
this._pinInput!.value as string
|
||||
|
@ -442,7 +604,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
this._status = "loading";
|
||||
this._error = undefined;
|
||||
try {
|
||||
await grantSecurityClasses(
|
||||
await zwaveGrantSecurityClasses(
|
||||
this.hass,
|
||||
this._entryId!,
|
||||
this._securityClasses
|
||||
|
@ -460,17 +622,33 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
this._startInclusion();
|
||||
}
|
||||
|
||||
private _startInclusion(): void {
|
||||
private async _checkSmartStartSupport() {
|
||||
this._supportsSmartStart = (
|
||||
await zwaveSupportsFeature(
|
||||
this.hass,
|
||||
this._entryId!,
|
||||
ZWaveFeature.SmartStart
|
||||
)
|
||||
).supported;
|
||||
}
|
||||
|
||||
private _startInclusion(
|
||||
qrProvisioningInformation?: QRProvisioningInformation,
|
||||
qrCodeString?: string,
|
||||
plannedProvisioningEntry?: PlannedProvisioningEntry
|
||||
): void {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
this._lowSecurity = false;
|
||||
this._subscribed = subscribeAddNode(
|
||||
const specificDevice =
|
||||
qrProvisioningInformation || qrCodeString || plannedProvisioningEntry;
|
||||
this._subscribed = subscribeAddZwaveNode(
|
||||
this.hass,
|
||||
this._entryId!,
|
||||
(message) => {
|
||||
if (message.event === "inclusion started") {
|
||||
this._status = "started";
|
||||
this._status = specificDevice ? "started_specific" : "started";
|
||||
}
|
||||
if (message.event === "inclusion failed") {
|
||||
this._unsubscribe();
|
||||
|
@ -491,7 +669,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
|
||||
if (message.event === "grant security classes") {
|
||||
if (this._inclusionStrategy === undefined) {
|
||||
grantSecurityClasses(
|
||||
zwaveGrantSecurityClasses(
|
||||
this.hass,
|
||||
this._entryId!,
|
||||
message.requested_grant.securityClasses,
|
||||
|
@ -525,7 +703,10 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
}
|
||||
}
|
||||
},
|
||||
this._inclusionStrategy
|
||||
this._inclusionStrategy,
|
||||
qrProvisioningInformation,
|
||||
qrCodeString,
|
||||
plannedProvisioningEntry
|
||||
);
|
||||
this._addNodeTimeoutHandle = window.setTimeout(() => {
|
||||
this._unsubscribe();
|
||||
|
@ -539,7 +720,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
this._subscribed = undefined;
|
||||
}
|
||||
if (this._entryId) {
|
||||
stopInclusion(this.hass, this._entryId);
|
||||
stopZwaveInclusion(this.hass, this._entryId);
|
||||
}
|
||||
this._requestedGrant = undefined;
|
||||
this._dsk = undefined;
|
||||
|
@ -558,6 +739,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
this._status = undefined;
|
||||
this._device = undefined;
|
||||
this._stages = undefined;
|
||||
this._error = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
|
@ -578,10 +760,6 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
.failed {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.stages {
|
||||
margin-top: 16px;
|
||||
display: grid;
|
||||
|
@ -610,6 +788,39 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.select-inclusion {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select-inclusion .outline:nth-child(2) {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.select-inclusion .outline {
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
min-height: 250px;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media all and (max-width: 500px) {
|
||||
.select-inclusion {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.select-inclusion .outline:nth-child(2) {
|
||||
margin-left: 0;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
mwc-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
width: 68px;
|
||||
height: 48px;
|
||||
|
|
|
@ -7,10 +7,10 @@ import { customElement, property, state } from "lit/decorators";
|
|||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import {
|
||||
fetchNetworkStatus,
|
||||
healNetwork,
|
||||
stopHealNetwork,
|
||||
subscribeHealNetworkProgress,
|
||||
fetchZwaveNetworkStatus,
|
||||
healZwaveNetwork,
|
||||
stopHealZwaveNetwork,
|
||||
subscribeHealZwaveNetworkProgress,
|
||||
ZWaveJSHealNetworkStatusMessage,
|
||||
ZWaveJSNetwork,
|
||||
} from "../../../../../data/zwave_js";
|
||||
|
@ -202,13 +202,13 @@ class DialogZWaveJSHealNetwork extends LitElement {
|
|||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
const network: ZWaveJSNetwork = await fetchNetworkStatus(
|
||||
const network: ZWaveJSNetwork = await fetchZwaveNetworkStatus(
|
||||
this.hass!,
|
||||
this.entry_id!
|
||||
);
|
||||
if (network.controller.is_heal_network_active) {
|
||||
this._status = "started";
|
||||
this._subscribed = subscribeHealNetworkProgress(
|
||||
this._subscribed = subscribeHealZwaveNetworkProgress(
|
||||
this.hass,
|
||||
this.entry_id!,
|
||||
this._handleMessage.bind(this)
|
||||
|
@ -220,9 +220,9 @@ class DialogZWaveJSHealNetwork extends LitElement {
|
|||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
healNetwork(this.hass, this.entry_id!);
|
||||
healZwaveNetwork(this.hass, this.entry_id!);
|
||||
this._status = "started";
|
||||
this._subscribed = subscribeHealNetworkProgress(
|
||||
this._subscribed = subscribeHealZwaveNetworkProgress(
|
||||
this.hass,
|
||||
this.entry_id!,
|
||||
this._handleMessage.bind(this)
|
||||
|
@ -233,7 +233,7 @@ class DialogZWaveJSHealNetwork extends LitElement {
|
|||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
stopHealNetwork(this.hass, this.entry_id!);
|
||||
stopHealZwaveNetwork(this.hass, this.entry_id!);
|
||||
this._unsubscribe();
|
||||
this._status = "cancelled";
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import {
|
|||
computeDeviceName,
|
||||
} from "../../../../../data/device_registry";
|
||||
import {
|
||||
fetchNetworkStatus,
|
||||
healNode,
|
||||
fetchZwaveNetworkStatus,
|
||||
healZwaveNode,
|
||||
ZWaveJSNetwork,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
|
@ -206,7 +206,7 @@ class DialogZWaveJSHealNode extends LitElement {
|
|||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
const network: ZWaveJSNetwork = await fetchNetworkStatus(
|
||||
const network: ZWaveJSNetwork = await fetchZwaveNetworkStatus(
|
||||
this.hass!,
|
||||
this.entry_id!
|
||||
);
|
||||
|
@ -221,7 +221,11 @@ class DialogZWaveJSHealNode extends LitElement {
|
|||
}
|
||||
this._status = "started";
|
||||
try {
|
||||
this._status = (await healNode(this.hass, this.entry_id!, this.node_id!))
|
||||
this._status = (await healZwaveNode(
|
||||
this.hass,
|
||||
this.entry_id!,
|
||||
this.node_id!
|
||||
))
|
||||
? "finished"
|
||||
: "failed";
|
||||
} catch (err: any) {
|
||||
|
|
|
@ -6,7 +6,7 @@ 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 { reinterviewNode } from "../../../../../data/zwave_js";
|
||||
import { reinterviewZwaveNode } from "../../../../../data/zwave_js";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZWaveJSReinterviewNodeDialogParams } from "./show-dialog-zwave_js-reinterview-node";
|
||||
|
@ -157,7 +157,7 @@ class DialogZWaveJSReinterviewNode extends LitElement {
|
|||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
this._subscribed = reinterviewNode(
|
||||
this._subscribed = reinterviewZwaveNode(
|
||||
this.hass,
|
||||
this.entry_id!,
|
||||
this.node_id!,
|
||||
|
|
|
@ -7,7 +7,7 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
|||
import "../../../../../components/ha-circular-progress";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import {
|
||||
removeFailedNode,
|
||||
removeFailedZwaveNode,
|
||||
ZWaveJSRemovedNode,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
|
@ -164,7 +164,7 @@ class DialogZWaveJSRemoveFailedNode extends LitElement {
|
|||
return;
|
||||
}
|
||||
this._status = "started";
|
||||
this._subscribed = removeFailedNode(
|
||||
this._subscribed = removeFailedZwaveNode(
|
||||
this.hass,
|
||||
this.entry_id!,
|
||||
this.node_id!,
|
||||
|
|
|
@ -9,11 +9,11 @@ import "../../../../../components/ha-icon-next";
|
|||
import "../../../../../components/ha-svg-icon";
|
||||
import { getSignedPath } from "../../../../../data/auth";
|
||||
import {
|
||||
fetchDataCollectionStatus,
|
||||
fetchNetworkStatus,
|
||||
fetchNodeStatus,
|
||||
fetchZwaveDataCollectionStatus,
|
||||
fetchZwaveNetworkStatus,
|
||||
fetchZwaveNodeStatus,
|
||||
NodeStatus,
|
||||
setDataCollectionPreference,
|
||||
setZwaveDataCollectionPreference,
|
||||
ZWaveJSNetwork,
|
||||
ZWaveJSNodeStatus,
|
||||
} from "../../../../../data/zwave_js";
|
||||
|
@ -317,8 +317,8 @@ class ZWaveJSConfigDashboard extends LitElement {
|
|||
}
|
||||
|
||||
const [network, dataCollectionStatus] = await Promise.all([
|
||||
fetchNetworkStatus(this.hass!, this.configEntryId),
|
||||
fetchDataCollectionStatus(this.hass!, this.configEntryId),
|
||||
fetchZwaveNetworkStatus(this.hass!, this.configEntryId),
|
||||
fetchZwaveDataCollectionStatus(this.hass!, this.configEntryId),
|
||||
]);
|
||||
|
||||
this._network = network;
|
||||
|
@ -340,7 +340,7 @@ class ZWaveJSConfigDashboard extends LitElement {
|
|||
return;
|
||||
}
|
||||
const nodeStatePromisses = this._network.controller.nodes.map((nodeId) =>
|
||||
fetchNodeStatus(this.hass, this.configEntryId!, nodeId)
|
||||
fetchZwaveNodeStatus(this.hass, this.configEntryId!, nodeId)
|
||||
);
|
||||
this._nodes = await Promise.all(nodeStatePromisses);
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ class ZWaveJSConfigDashboard extends LitElement {
|
|||
}
|
||||
|
||||
private _dataCollectionToggled(ev) {
|
||||
setDataCollectionPreference(
|
||||
setZwaveDataCollectionPreference(
|
||||
this.hass!,
|
||||
this.configEntryId!,
|
||||
ev.target.checked
|
||||
|
|
|
@ -32,9 +32,9 @@ import {
|
|||
subscribeDeviceRegistry,
|
||||
} from "../../../../../data/device_registry";
|
||||
import {
|
||||
fetchNodeConfigParameters,
|
||||
fetchNodeMetadata,
|
||||
setNodeConfigParameter,
|
||||
fetchZwaveNodeConfigParameters,
|
||||
fetchZwaveNodeMetadata,
|
||||
setZwaveNodeConfigParameter,
|
||||
ZWaveJSNodeConfigParams,
|
||||
ZwaveJSNodeMetadata,
|
||||
ZWaveJSSetConfigParamResult,
|
||||
|
@ -377,7 +377,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||
private async _updateConfigParameter(target, value) {
|
||||
const nodeId = getNodeId(this._device!);
|
||||
try {
|
||||
const result = await setNodeConfigParameter(
|
||||
const result = await setZwaveNodeConfigParameter(
|
||||
this.hass,
|
||||
this.configEntryId!,
|
||||
nodeId!,
|
||||
|
@ -429,8 +429,8 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||
}
|
||||
|
||||
[this._nodeMetadata, this._config] = await Promise.all([
|
||||
fetchNodeMetadata(this.hass, this.configEntryId, nodeId!),
|
||||
fetchNodeConfigParameters(this.hass, this.configEntryId, nodeId!),
|
||||
fetchZwaveNodeMetadata(this.hass, this.configEntryId, nodeId!),
|
||||
fetchZwaveNodeConfigParameters(this.hass, this.configEntryId, nodeId!),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -2855,11 +2855,18 @@
|
|||
},
|
||||
"add_node": {
|
||||
"title": "Add a Z-Wave Device",
|
||||
"cancel_inclusion": "Cancel Inclusion",
|
||||
"controller_in_inclusion_mode": "Your Z-Wave controller is now in inclusion mode.",
|
||||
"searching_device": "Searching for device",
|
||||
"follow_device_instructions": "Follow the directions that came with your device to trigger pairing on the device.",
|
||||
"inclusion_failed": "The device could not be added. Please check the logs for more information.",
|
||||
"choose_inclusion_strategy": "How do you want to add your device",
|
||||
"qr_code": "QR Code",
|
||||
"qr_code_paragraph": "If your device supports SmartStart you can scan the QR code for easy pairing.",
|
||||
"scan_qr_code": "Scan QR code",
|
||||
"enter_qr_code": "Enter QR code value",
|
||||
"select_camera": "Select camera",
|
||||
"inclusion_failed": "The device could not be added.",
|
||||
"check_logs": "Please check the logs for more information.",
|
||||
"inclusion_finished": "The device has been added.",
|
||||
"provisioning_finished": "The device has been added. Once you power it on, it will become available.",
|
||||
"view_device": "View Device",
|
||||
"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."
|
||||
|
|
|
@ -9139,6 +9139,7 @@ fsevents@^1.2.7:
|
|||
prettier: ^2.4.1
|
||||
proxy-polyfill: ^0.3.2
|
||||
punycode: ^2.1.1
|
||||
qr-scanner: ^1.3.0
|
||||
qrcode: ^1.4.4
|
||||
regenerator-runtime: ^0.13.8
|
||||
require-dir: ^1.2.0
|
||||
|
@ -13007,6 +13008,13 @@ fsevents@^1.2.7:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qr-scanner@npm:^1.3.0":
|
||||
version: 1.3.0
|
||||
resolution: "qr-scanner@npm:1.3.0"
|
||||
checksum: 421ff00626252d0f9e50550fb550a463166e4d0438baffb469c9450079f1f802f6df22784509bb571ef50ece81aecaadc00f91d442959f37655ad29710c81c8b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qrcode@npm:^1.4.4":
|
||||
version: 1.4.4
|
||||
resolution: "qrcode@npm:1.4.4"
|
||||
|
|
Loading…
Reference in New Issue