mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 03:06:41 +00:00
Add OZW Refresh Node Dialog (#6530)
This commit is contained in:
parent
39f24c41ad
commit
c4d8aba5c8
@ -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,
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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,
|
||||
});
|
||||
};
|
@ -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": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user