Add OZW Refresh Node Dialog (#6530)

This commit is contained in:
Charles Garwood 2020-08-17 13:54:03 -04:00 committed by GitHub
parent 39f24c41ad
commit c4d8aba5c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 459 additions and 15 deletions

View File

@ -1,4 +1,10 @@
import { HomeAssistant } from "../types";
import { DeviceRegistryEntry } from "./device_registry";
export interface OZWNodeIdentifiers {
ozw_instance: number;
node_id: number;
}
export interface OZWDevice {
node_id: number;
@ -7,15 +13,102 @@ export interface OZWDevice {
is_failed: boolean;
is_zwave_plus: boolean;
ozw_instance: number;
event: string;
}
export interface OZWDeviceMetaDataResponse {
node_id: number;
ozw_instance: number;
metadata: OZWDeviceMetaData;
}
export interface OZWDeviceMetaData {
OZWInfoURL: string;
ZWAProductURL: string;
ProductPic: string;
Description: string;
ProductManualURL: string;
ProductPageURL: string;
InclusionHelp: string;
ExclusionHelp: string;
ResetHelp: string;
WakeupHelp: string;
ProductSupportURL: string;
Frequency: string;
Name: string;
ProductPicBase64: string;
}
export const nodeQueryStages = [
"ProtocolInfo",
"Probe",
"WakeUp",
"ManufacturerSpecific1",
"NodeInfo",
"NodePlusInfo",
"ManufacturerSpecific2",
"Versions",
"Instances",
"Static",
"CacheLoad",
"Associations",
"Neighbors",
"Session",
"Dynamic",
"Configuration",
"Complete",
];
export const getIdentifiersFromDevice = function (
device: DeviceRegistryEntry
): OZWNodeIdentifiers | undefined {
if (!device) {
return undefined;
}
const ozwIdentifier = device.identifiers.find(
(identifier) => identifier[0] === "ozw"
);
if (!ozwIdentifier) {
return undefined;
}
const identifiers = ozwIdentifier[1].split(".");
return {
node_id: parseInt(identifiers[1]),
ozw_instance: parseInt(identifiers[0]),
};
};
export const fetchOZWNodeStatus = (
hass: HomeAssistant,
ozw_instance: string,
node_id: string
ozw_instance: number,
node_id: number
): Promise<OZWDevice> =>
hass.callWS({
type: "ozw/node_status",
ozw_instance: ozw_instance,
node_id: node_id,
});
export const fetchOZWNodeMetadata = (
hass: HomeAssistant,
ozw_instance: number,
node_id: number
): Promise<OZWDeviceMetaDataResponse> =>
hass.callWS({
type: "ozw/node_metadata",
ozw_instance: ozw_instance,
node_id: node_id,
});
export const refreshNodeInfo = (
hass: HomeAssistant,
ozw_instance: number,
node_id: number
): Promise<OZWDevice> =>
hass.callWS({
type: "ozw/refresh_node_info",
ozw_instance: ozw_instance,
node_id: node_id,
});

View File

@ -12,7 +12,13 @@ import {
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
import { haStyle } from "../../../../../../resources/styles";
import { HomeAssistant } from "../../../../../../types";
import { OZWDevice, fetchOZWNodeStatus } from "../../../../../../data/ozw";
import {
OZWDevice,
fetchOZWNodeStatus,
getIdentifiersFromDevice,
OZWNodeIdentifiers,
} from "../../../../../../data/ozw";
import { showOZWRefreshNodeDialog } from "../../../../integrations/integration-panels/ozw/show-dialog-ozw-refresh-node";
@customElement("ha-device-info-ozw")
export class HaDeviceInfoOzw extends LitElement {
@ -20,26 +26,34 @@ export class HaDeviceInfoOzw extends LitElement {
@property() public device!: DeviceRegistryEntry;
@property()
private node_id = 0;
@property()
private ozw_instance = 1;
@internalProperty() private _ozwDevice?: OZWDevice;
protected updated(changedProperties: PropertyValues) {
if (changedProperties.has("device")) {
this._fetchNodeDetails(this.device);
const identifiers:
| OZWNodeIdentifiers
| undefined = getIdentifiersFromDevice(this.device);
if (!identifiers) {
return;
}
this.ozw_instance = identifiers.ozw_instance;
this.node_id = identifiers.node_id;
this._fetchNodeDetails();
}
}
protected async _fetchNodeDetails(device) {
const ozwIdentifier = device.identifiers.find(
(identifier) => identifier[0] === "ozw"
);
if (!ozwIdentifier) {
return;
}
const identifiers = ozwIdentifier[1].split(".");
protected async _fetchNodeDetails() {
this._ozwDevice = await fetchOZWNodeStatus(
this.hass,
identifiers[0],
identifiers[1]
this.ozw_instance,
this.node_id
);
}
@ -69,9 +83,19 @@ export class HaDeviceInfoOzw extends LitElement {
? this.hass.localize("ui.common.yes")
: this.hass.localize("ui.common.no")}
</div>
<mwc-button @click=${this._refreshNodeClicked}>
Refresh Node
</mwc-button>
`;
}
private async _refreshNodeClicked() {
showOZWRefreshNodeDialog(this, {
node_id: this.node_id,
ozw_instance: this.ozw_instance,
});
}
static get styles(): CSSResult[] {
return [
haStyle,

View File

@ -0,0 +1,272 @@
import {
CSSResult,
customElement,
html,
LitElement,
property,
internalProperty,
TemplateResult,
PropertyValues,
css,
} from "lit-element";
import "../../../../../components/ha-code-editor";
import "../../../../../components/ha-circular-progress";
import { createCloseHeading } from "../../../../../components/ha-dialog";
import { haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { OZWRefreshNodeDialogParams } from "./show-dialog-ozw-refresh-node";
import {
fetchOZWNodeMetadata,
OZWDeviceMetaData,
OZWDevice,
nodeQueryStages,
} from "../../../../../data/ozw";
@customElement("dialog-ozw-refresh-node")
class DialogOZWRefreshNode extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _node_id?: number;
@internalProperty() private _ozw_instance = 1;
@internalProperty() private _nodeMetaData?: OZWDeviceMetaData;
@internalProperty() private _node?: OZWDevice;
@internalProperty() private _active = false;
@internalProperty() private _complete = false;
private _refreshDevicesTimeoutHandle?: number;
private _subscribed?: Promise<() => Promise<void>>;
public disconnectedCallback(): void {
super.disconnectedCallback();
this._unsubscribe();
}
protected updated(changedProperties: PropertyValues): void {
super.update(changedProperties);
if (changedProperties.has("node_id")) {
this._fetchData();
}
}
private async _fetchData() {
if (!this._node_id) {
return;
}
const metaDataResponse = await fetchOZWNodeMetadata(
this.hass,
this._ozw_instance,
this._node_id
);
this._nodeMetaData = metaDataResponse.metadata;
}
public async showDialog(params: OZWRefreshNodeDialogParams): Promise<void> {
this._node_id = params.node_id;
this._ozw_instance = params.ozw_instance;
this._fetchData();
}
protected render(): TemplateResult {
if (!this._node_id) {
return html``;
}
return html`
<ha-dialog
open
@closing="${this._close}"
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.ozw.refresh_node.title")
)}
>
${this._complete
? html`
<p>
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.complete"
)}
</p>
<mwc-button slot="primaryAction" @click=${this._close}>
${this.hass.localize("ui.common.close")}
</mwc-button>
`
: html`
${this._active
? html`
<div class="flex-container">
<ha-circular-progress active></ha-circular-progress>
<div>
<p>
<b>
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.refreshing_description"
)}
</b>
</p>
${this._node
? html`
<p>
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.node_status"
)}:
${this._node.node_query_stage}
(${this.hass.localize(
"ui.panel.config.ozw.refresh_node.step"
)}
${nodeQueryStages.indexOf(
this._node.node_query_stage
) + 1}/17)
</p>
<p>
<em>
${this.hass.localize(
"ui.panel.config.ozw.node_query_stages." +
this._node.node_query_stage.toLowerCase()
)}</em
>
</p>
`
: ``}
</div>
</div>
`
: html`
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.description"
)}
<p>
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.battery_note"
)}
</p>
`}
${this._nodeMetaData?.WakeupHelp !== ""
? html`
<b>
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.wakeup_header"
)}
${this._nodeMetaData!.Name}
</b>
<blockquote>
${this._nodeMetaData!.WakeupHelp}
<br />
<em>
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.wakeup_instructions_source"
)}
</em>
</blockquote>
`
: ""}
${!this._active
? html`
<mwc-button
slot="primaryAction"
@click=${this._startRefresh}
>
${this.hass.localize(
"ui.panel.config.ozw.refresh_node.start_refresh_button"
)}
</mwc-button>
`
: html``}
`}
</ha-dialog>
`;
}
private _startRefresh(): void {
this._subscribe();
}
private _handleMessage(message: any): void {
if (message.type === "node_updated") {
this._node = message;
if (message.node_query_stage === "Complete") {
this._unsubscribe();
this._complete = true;
}
}
}
private _unsubscribe(): void {
this._active = false;
if (this._refreshDevicesTimeoutHandle) {
clearTimeout(this._refreshDevicesTimeoutHandle);
}
if (this._subscribed) {
this._subscribed.then((unsub) => unsub());
this._subscribed = undefined;
}
}
private _subscribe(): void {
if (!this.hass) {
return;
}
this._active = true;
this._subscribed = this.hass.connection.subscribeMessage(
(message) => this._handleMessage(message),
{
type: "ozw/refresh_node_info",
node_id: this._node_id,
ozw_instance: this._ozw_instance,
}
);
this._refreshDevicesTimeoutHandle = window.setTimeout(
() => this._unsubscribe(),
120000
);
}
private _close(): void {
this._complete = false;
this._node_id = undefined;
this._node = undefined;
}
static get styles(): CSSResult[] {
return [
haStyleDialog,
css`
blockquote {
display: block;
background-color: #ddd;
padding: 8px;
margin: 8px 0;
font-size: 0.9em;
}
blockquote em {
font-size: 0.9em;
margin-top: 6px;
}
.flex-container {
display: flex;
align-items: center;
}
.flex-container ha-circular-progress {
margin-right: 20px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-ozw-refresh-node": DialogOZWRefreshNode;
}
}

View File

@ -0,0 +1,22 @@
import { fireEvent } from "../../../../../common/dom/fire_event";
export interface OZWRefreshNodeDialogParams {
ozw_instance: number;
node_id: number;
}
export const loadRefreshNodeDialog = () =>
import(
/* webpackChunkName: "dialog-ozw-refresh-node" */ "./dialog-ozw-refresh-node"
);
export const showOZWRefreshNodeDialog = (
element: HTMLElement,
refreshNodeDialogParams: OZWRefreshNodeDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-ozw-refresh-node",
dialogImport: loadRefreshNodeDialog,
dialogParams: refreshNodeDialogParams,
});
};

View File

@ -1066,7 +1066,9 @@
"label": "Repeat",
"type_select": "Repeat type",
"type": {
"count": { "label": "Count" },
"count": {
"label": "Count"
},
"while": {
"label": "While",
"conditions": "While conditions"
@ -1628,6 +1630,37 @@
"zwave_info": "Z-Wave Info",
"stage": "Stage",
"node_failed": "Node Failed"
},
"node_query_stages": {
"protocolinfo": "Obtaining basic Z-Wave capabilities of this node from the controller",
"probe": "Checking if the node is awake/alive",
"wakeup": "Setting up support for wakeup queues and messages",
"manufacturerspecific1": "Obtaining manufacturer and product ID codes from the node",
"nodeinfo": "Obtaining supported command classes from the node",
"nodeplusinfo": "Obtaining Z-Wave+ information from the node",
"manufacturerspecific2": "Obtaining additional manufacturer and product ID codes from the node",
"versions": "Obtaining information about firmware and command class versions",
"instances": "Obtaining details about what instances or channels a device supports",
"static": "Obtaining static values from the device",
"cacheload": "Loading information from the OpenZWave cache file. Battery nodes will stay at this stage until the node wakes up.",
"associations": "Refreshing association groups and memberships",
"neighbors": "Obtaining a list of the node's neighbors",
"session": "Obtaining infrequently changing values from the node",
"dynamic": "Obtaining frequently changing values from the node",
"configuration": "Obtaining configuration values from the node",
"complete": "Interview process is complete"
},
"refresh_node": {
"title": "Refresh Node Information",
"complete": "Node Refresh Complete",
"description": "This will tell OpenZWave to re-interview a node and update the node's command classes, capabilities, and values.",
"battery_note": "If the node is battery powered, be sure to wake it before proceeding",
"wakeup_header": "Wakeup Instructions for",
"wakeup_instructions_source": "Wakeup instructions are sourced from the OpenZWave community device database.",
"start_refresh_button": "Start Refresh",
"refreshing_description": "Refreshing node information...",
"node_status": "Node Status",
"step": "Step"
}
},
"zha": {