mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Add manual firmware update support for zwave-js devices (#12910)
* Subscribe to zwave_js node status updates in device panel * Add typing for message * Add manual firmware update support for zwave-js devices * Tweaks based on upstream changes * Tweaks * remove unused CSS * Update zwave_js.ts * Tweaks after somet esting * Bold device name instead of italic, catch abort errors and show the message * Incorporate new commands tweak the UI and messaging * Add a warning about firmware updates potentially bricking a device, and use Promise.all where possible * Better typing so we can clean up code * Additional tweaks * Remove commented out code * change style a bit * prettier * Be more precise with progress because it always helps the user if they can see progress * nit * Update src/translations/en.json Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Bram's review * Only ask for firmware target if there is more than one available * Only offer another firmware update if the original firmware update failed * Only offer firmware upgrade if node is ready and pass firmware capabilities into dialog so we don't have to make call again * Use ha-form * Add comment * Switch schema name * Import icon Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
7d118a5715
commit
2812b467ec
@ -85,6 +85,22 @@ enum Protocols {
|
||||
ZWaveLongRange = 1,
|
||||
}
|
||||
|
||||
export enum FirmwareUpdateStatus {
|
||||
Error_Timeout = -1,
|
||||
Error_Checksum = 0,
|
||||
Error_TransmissionFailed = 1,
|
||||
Error_InvalidManufacturerID = 2,
|
||||
Error_InvalidFirmwareID = 3,
|
||||
Error_InvalidFirmwareTarget = 4,
|
||||
Error_InvalidHeaderInformation = 5,
|
||||
Error_InvalidHeaderFormat = 6,
|
||||
Error_InsufficientMemory = 7,
|
||||
Error_InvalidHardwareVersion = 8,
|
||||
OK_WaitingForActivation = 0xfd,
|
||||
OK_NoRestart = 0xfe,
|
||||
OK_RestartPending = 0xff,
|
||||
}
|
||||
|
||||
export interface QRProvisioningInformation {
|
||||
version: QRCodeVersion;
|
||||
securityClasses: SecurityClass[];
|
||||
@ -147,7 +163,7 @@ export interface ZWaveJSController {
|
||||
export interface ZWaveJSNodeStatus {
|
||||
node_id: number;
|
||||
ready: boolean;
|
||||
status: number;
|
||||
status: NodeStatus;
|
||||
is_secure: boolean | string;
|
||||
is_routing: boolean | null;
|
||||
zwave_plus_version: number | null;
|
||||
@ -281,6 +297,27 @@ export interface ZWaveJSNodeStatusUpdatedMessage {
|
||||
status: NodeStatus;
|
||||
}
|
||||
|
||||
export interface ZWaveJSNodeFirmwareUpdateProgressMessage {
|
||||
event: "firmware update progress";
|
||||
sent_fragments: number;
|
||||
total_fragments: number;
|
||||
}
|
||||
|
||||
export interface ZWaveJSNodeFirmwareUpdateFinishedMessage {
|
||||
event: "firmware update finished";
|
||||
status: FirmwareUpdateStatus;
|
||||
wait_time: number;
|
||||
}
|
||||
|
||||
export type ZWaveJSNodeFirmwareUpdateCapabilities =
|
||||
| { firmware_upgradable: false }
|
||||
| {
|
||||
firmware_upgradable: true;
|
||||
firmware_targets: number[];
|
||||
continues_to_function: boolean | null;
|
||||
supports_activation: boolean | null;
|
||||
};
|
||||
|
||||
export interface ZWaveJSRemovedNode {
|
||||
node_id: number;
|
||||
manufacturer: string;
|
||||
@ -628,6 +665,74 @@ export const subscribeZwaveNodeStatistics = (
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchZwaveNodeIsFirmwareUpdateInProgress = (
|
||||
hass: HomeAssistant,
|
||||
device_id: string
|
||||
): Promise<boolean> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/get_firmware_update_progress",
|
||||
device_id,
|
||||
});
|
||||
|
||||
export const fetchZwaveNodeFirmwareUpdateCapabilities = (
|
||||
hass: HomeAssistant,
|
||||
device_id: string
|
||||
): Promise<ZWaveJSNodeFirmwareUpdateCapabilities> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/get_firmware_update_capabilities",
|
||||
device_id,
|
||||
});
|
||||
|
||||
export const uploadFirmwareAndBeginUpdate = async (
|
||||
hass: HomeAssistant,
|
||||
device_id: string,
|
||||
file: File,
|
||||
target?: number
|
||||
) => {
|
||||
const fd = new FormData();
|
||||
fd.append("file", file);
|
||||
if (target !== undefined) {
|
||||
fd.append("target", target.toString());
|
||||
}
|
||||
const resp = await hass.fetchWithAuth(
|
||||
`/api/zwave_js/firmware/upload/${device_id}`,
|
||||
{
|
||||
method: "POST",
|
||||
body: fd,
|
||||
}
|
||||
);
|
||||
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
};
|
||||
|
||||
export const subscribeZwaveNodeFirmwareUpdate = (
|
||||
hass: HomeAssistant,
|
||||
device_id: string,
|
||||
callbackFunction: (
|
||||
message:
|
||||
| ZWaveJSNodeFirmwareUpdateFinishedMessage
|
||||
| ZWaveJSNodeFirmwareUpdateProgressMessage
|
||||
) => void
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
hass.connection.subscribeMessage(
|
||||
(message: any) => callbackFunction(message),
|
||||
{
|
||||
type: "zwave_js/subscribe_firmware_update_status",
|
||||
device_id,
|
||||
}
|
||||
);
|
||||
|
||||
export const abortZwaveNodeFirmwareUpdate = (
|
||||
hass: HomeAssistant,
|
||||
device_id: string
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/abort_firmware_update",
|
||||
device_id,
|
||||
});
|
||||
|
||||
export type ZWaveJSLogUpdate = ZWaveJSLogMessageUpdate | ZWaveJSLogConfigUpdate;
|
||||
|
||||
interface ZWaveJSLogMessageUpdate {
|
||||
|
@ -1,11 +1,16 @@
|
||||
import { getConfigEntries } from "../../../../../../data/config_entries";
|
||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
||||
import { fetchZwaveNodeStatus } from "../../../../../../data/zwave_js";
|
||||
import {
|
||||
fetchZwaveNodeFirmwareUpdateCapabilities,
|
||||
fetchZwaveNodeStatus,
|
||||
} from "../../../../../../data/zwave_js";
|
||||
import { showConfirmationDialog } from "../../../../../../dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import { showZWaveJSHealNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node";
|
||||
import { showZWaveJSNodeStatisticsDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-node-statistics";
|
||||
import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node";
|
||||
import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node";
|
||||
import { showZWaveJUpdateFirmwareNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-update-firmware-node";
|
||||
import type { DeviceAction } from "../../../ha-config-device-page";
|
||||
|
||||
export const getZwaveDeviceActions = async (
|
||||
@ -27,13 +32,13 @@ export const getZwaveDeviceActions = async (
|
||||
|
||||
const entryId = configEntry.entry_id;
|
||||
|
||||
const node = await fetchZwaveNodeStatus(hass, device.id);
|
||||
const nodeStatus = await fetchZwaveNodeStatus(hass, device.id);
|
||||
|
||||
if (!node || node.is_controller_node) {
|
||||
if (!nodeStatus || nodeStatus.is_controller_node) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
const actions = [
|
||||
{
|
||||
label: hass.localize(
|
||||
"ui.panel.config.zwave_js.device_info.device_config"
|
||||
@ -53,7 +58,7 @@ export const getZwaveDeviceActions = async (
|
||||
label: hass.localize("ui.panel.config.zwave_js.device_info.heal_node"),
|
||||
action: () =>
|
||||
showZWaveJSHealNodeDialog(el, {
|
||||
device: device,
|
||||
device,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -71,8 +76,41 @@ export const getZwaveDeviceActions = async (
|
||||
),
|
||||
action: () =>
|
||||
showZWaveJSNodeStatisticsDialog(el, {
|
||||
device: device,
|
||||
device,
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
if (!nodeStatus.ready) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
const firmwareUpdateCapabilities =
|
||||
await fetchZwaveNodeFirmwareUpdateCapabilities(hass, device.id);
|
||||
|
||||
if (firmwareUpdateCapabilities.firmware_upgradable) {
|
||||
actions.push({
|
||||
label: hass.localize(
|
||||
"ui.panel.config.zwave_js.device_info.update_firmware"
|
||||
),
|
||||
action: async () => {
|
||||
if (
|
||||
await showConfirmationDialog(el, {
|
||||
text: hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.warning"
|
||||
),
|
||||
dismissText: hass.localize("ui.common.no"),
|
||||
confirmText: hass.localize("ui.common.yes"),
|
||||
})
|
||||
) {
|
||||
showZWaveJUpdateFirmwareNodeDialog(el, {
|
||||
device,
|
||||
firmwareUpdateCapabilities,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
@ -0,0 +1,461 @@
|
||||
import "../../../../../components/ha-file-upload";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import "../../../../../components/ha-svg-icon";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import { mdiCheckCircle, mdiCloseCircle, mdiFileUpload } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import {
|
||||
DeviceRegistryEntry,
|
||||
computeDeviceName,
|
||||
} from "../../../../../data/device_registry";
|
||||
import {
|
||||
abortZwaveNodeFirmwareUpdate,
|
||||
fetchZwaveNodeIsFirmwareUpdateInProgress,
|
||||
fetchZwaveNodeStatus,
|
||||
FirmwareUpdateStatus,
|
||||
NodeStatus,
|
||||
subscribeZwaveNodeStatus,
|
||||
subscribeZwaveNodeFirmwareUpdate,
|
||||
uploadFirmwareAndBeginUpdate,
|
||||
ZWaveJSNodeFirmwareUpdateFinishedMessage,
|
||||
ZWaveJSNodeFirmwareUpdateProgressMessage,
|
||||
ZWaveJSNodeStatusUpdatedMessage,
|
||||
ZWaveJSNodeFirmwareUpdateCapabilities,
|
||||
ZWaveJSNodeStatus,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZWaveJSUpdateFirmwareNodeDialogParams } from "./show-dialog-zwave_js-update-firmware-node";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import { HaFormIntegerSchema } from "../../../../../components/ha-form/types";
|
||||
|
||||
@customElement("dialog-zwave_js-update-firmware-node")
|
||||
class DialogZWaveJSUpdateFirmwareNode extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private device?: DeviceRegistryEntry;
|
||||
|
||||
@state() private _uploading = false;
|
||||
|
||||
@state()
|
||||
private _updateFinishedMessage?: ZWaveJSNodeFirmwareUpdateFinishedMessage;
|
||||
|
||||
@state()
|
||||
private _updateProgressMessage?: ZWaveJSNodeFirmwareUpdateProgressMessage;
|
||||
|
||||
@state() private _updateInProgress = false;
|
||||
|
||||
@state() private _firmwareFile?: File;
|
||||
|
||||
@state() private _nodeStatus?: ZWaveJSNodeStatus;
|
||||
|
||||
@state() private _firmwareTarget? = 0;
|
||||
|
||||
private _subscribedNodeStatus?: Promise<UnsubscribeFunc>;
|
||||
|
||||
private _subscribedNodeFirmwareUpdate?: Promise<UnsubscribeFunc>;
|
||||
|
||||
private _deviceName?: string;
|
||||
|
||||
private _firmwareUpdateCapabilities?: ZWaveJSNodeFirmwareUpdateCapabilities;
|
||||
|
||||
public showDialog(params: ZWaveJSUpdateFirmwareNodeDialogParams): void {
|
||||
this._deviceName = computeDeviceName(params.device, this.hass!);
|
||||
this.device = params.device;
|
||||
this._firmwareUpdateCapabilities = params.firmwareUpdateCapabilities;
|
||||
this._fetchData();
|
||||
this._subscribeNodeStatus();
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._unsubscribeNodeFirmwareUpdate();
|
||||
this._unsubscribeNodeStatus();
|
||||
this.device =
|
||||
this._updateProgressMessage =
|
||||
this._updateFinishedMessage =
|
||||
this._firmwareFile =
|
||||
this._nodeStatus =
|
||||
this._firmwareUpdateCapabilities =
|
||||
undefined;
|
||||
this._firmwareTarget = 0;
|
||||
this._uploading = this._updateInProgress = false;
|
||||
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
firmwareUpdateCapabilities: ZWaveJSNodeFirmwareUpdateCapabilities
|
||||
): HaFormIntegerSchema => {
|
||||
if (!firmwareUpdateCapabilities.firmware_upgradable) {
|
||||
// We should never get here, this is to pass type checks
|
||||
throw new Error();
|
||||
}
|
||||
return {
|
||||
name: "firmware_target",
|
||||
type: "integer",
|
||||
valueMin: Math.min(...firmwareUpdateCapabilities.firmware_targets),
|
||||
valueMax: Math.max(...firmwareUpdateCapabilities.firmware_targets),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (
|
||||
!this.device ||
|
||||
!this._nodeStatus ||
|
||||
!this._firmwareUpdateCapabilities ||
|
||||
!this._firmwareUpdateCapabilities.firmware_upgradable ||
|
||||
this._updateInProgress === undefined
|
||||
) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const beginFirmwareUpdateHTML = html`<ha-file-upload
|
||||
.hass=${this.hass}
|
||||
.uploading=${this._uploading}
|
||||
.icon=${mdiFileUpload}
|
||||
label=${this._firmwareFile?.name ??
|
||||
this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.upload_firmware"
|
||||
)}
|
||||
@file-picked=${this._uploadFile}
|
||||
></ha-file-upload>
|
||||
${this._firmwareUpdateCapabilities.firmware_targets.length > 1
|
||||
? html`<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.firmware_target_intro"
|
||||
)}
|
||||
</p>
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{ firmware_target: this._firmwareTarget }}
|
||||
.schema=${[this._schema(this._firmwareUpdateCapabilities)]}
|
||||
@value-changed=${this._firmwareTargetChanged}
|
||||
></ha-form>`
|
||||
: ""}
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
@click=${this._beginFirmwareUpdate}
|
||||
.disabled=${this._firmwareFile === undefined}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.begin_update"
|
||||
)}
|
||||
</mwc-button>`;
|
||||
|
||||
const abortFirmwareUpdateButton = html`
|
||||
<mwc-button slot="primaryAction" @click=${this._abortFirmwareUpdate}>
|
||||
${this.hass.localize("ui.panel.config.zwave_js.update_firmware.abort")}
|
||||
</mwc-button>
|
||||
`;
|
||||
|
||||
const status = this._updateFinishedMessage
|
||||
? FirmwareUpdateStatus[this._updateFinishedMessage.status]
|
||||
.split("_")[0]
|
||||
.toLowerCase()
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.panel.config.zwave_js.update_firmware.title")
|
||||
)}
|
||||
>
|
||||
${!this._updateProgressMessage && !this._updateFinishedMessage
|
||||
? !this._updateInProgress
|
||||
? html`
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.introduction",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
${beginFirmwareUpdateHTML}
|
||||
`
|
||||
: html`
|
||||
<p>
|
||||
${this._nodeStatus.status === NodeStatus.Asleep
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.queued",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
}
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.awake",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
${this._nodeStatus.status === NodeStatus.Asleep
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.close_queued",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
}
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.close",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
${abortFirmwareUpdateButton}
|
||||
`
|
||||
: this._updateProgressMessage && !this._updateFinishedMessage
|
||||
? html`
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.in_progress",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
progress: (
|
||||
(this._updateProgressMessage.sent_fragments * 100) /
|
||||
this._updateProgressMessage.total_fragments
|
||||
).toFixed(2),
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<mwc-linear-progress
|
||||
determinate
|
||||
.progress=${this._updateProgressMessage.sent_fragments /
|
||||
this._updateProgressMessage.total_fragments}
|
||||
></mwc-linear-progress>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.close",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
${abortFirmwareUpdateButton}
|
||||
`
|
||||
: html`
|
||||
<div class="flex-container">
|
||||
<ha-svg-icon
|
||||
.path=${status === "ok" ? mdiCheckCircle : mdiCloseCircle}
|
||||
.class=${status}
|
||||
></ha-svg-icon>
|
||||
<div class="status">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.update_firmware.finished_status.${status}`,
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
message: this.hass.localize(
|
||||
`ui.panel.config.zwave_js.update_firmware.finished_status.${
|
||||
FirmwareUpdateStatus[
|
||||
this._updateFinishedMessage!.status
|
||||
]
|
||||
}`
|
||||
),
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
${status === "ok"
|
||||
? html`<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.finished_status.done"
|
||||
)}
|
||||
</p>`
|
||||
: html`<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.finished_status.try_again"
|
||||
)}
|
||||
</p>
|
||||
${beginFirmwareUpdateHTML}`}
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.finished_status.try_again"
|
||||
)}
|
||||
</p>
|
||||
${beginFirmwareUpdateHTML}
|
||||
`}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
[this._nodeStatus, this._updateInProgress] = await Promise.all([
|
||||
fetchZwaveNodeStatus(this.hass, this.device!.id),
|
||||
fetchZwaveNodeIsFirmwareUpdateInProgress(this.hass, this.device!.id),
|
||||
]);
|
||||
if (this._updateInProgress) {
|
||||
this._subscribeNodeFirmwareUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private async _beginFirmwareUpdate(): Promise<void> {
|
||||
this._uploading = true;
|
||||
this._updateProgressMessage = this._updateFinishedMessage = undefined;
|
||||
try {
|
||||
this._subscribeNodeFirmwareUpdate();
|
||||
await uploadFirmwareAndBeginUpdate(
|
||||
this.hass,
|
||||
this.device!.id,
|
||||
this._firmwareFile!,
|
||||
this._firmwareTarget
|
||||
);
|
||||
this._updateInProgress = true;
|
||||
this._uploading = false;
|
||||
} catch (err: any) {
|
||||
this._unsubscribeNodeFirmwareUpdate();
|
||||
this._uploading = false;
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.upload_failed"
|
||||
),
|
||||
text: err.message,
|
||||
confirmText: this.hass!.localize("ui.common.close"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _abortFirmwareUpdate(): Promise<void> {
|
||||
if (
|
||||
await showConfirmationDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.confirm_abort",
|
||||
{
|
||||
device: html`<strong>${this._deviceName}</strong>`,
|
||||
}
|
||||
),
|
||||
dismissText: this.hass!.localize("ui.common.no"),
|
||||
confirmText: this.hass!.localize("ui.common.yes"),
|
||||
})
|
||||
) {
|
||||
this._unsubscribeNodeFirmwareUpdate();
|
||||
try {
|
||||
await abortZwaveNodeFirmwareUpdate(this.hass, this.device!.id);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.update_firmware.abort_failed"
|
||||
),
|
||||
text: err.message,
|
||||
confirmText: this.hass!.localize("ui.common.close"),
|
||||
});
|
||||
}
|
||||
this._firmwareFile = undefined;
|
||||
this._updateFinishedMessage = undefined;
|
||||
this._updateProgressMessage = undefined;
|
||||
this._updateInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _subscribeNodeStatus(): void {
|
||||
if (!this.hass || !this.device || this._subscribedNodeStatus) {
|
||||
return;
|
||||
}
|
||||
this._subscribedNodeStatus = subscribeZwaveNodeStatus(
|
||||
this.hass,
|
||||
this.device.id,
|
||||
(message: ZWaveJSNodeStatusUpdatedMessage) => {
|
||||
this._nodeStatus!.status = message.status;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _unsubscribeNodeStatus(): void {
|
||||
if (!this._subscribedNodeStatus) {
|
||||
return;
|
||||
}
|
||||
this._subscribedNodeStatus.then((unsub) => unsub());
|
||||
this._subscribedNodeStatus = undefined;
|
||||
}
|
||||
|
||||
private _subscribeNodeFirmwareUpdate(): void {
|
||||
if (!this.hass || !this.device || this._subscribedNodeFirmwareUpdate) {
|
||||
return;
|
||||
}
|
||||
this._subscribedNodeFirmwareUpdate = subscribeZwaveNodeFirmwareUpdate(
|
||||
this.hass,
|
||||
this.device.id,
|
||||
(
|
||||
message:
|
||||
| ZWaveJSNodeFirmwareUpdateFinishedMessage
|
||||
| ZWaveJSNodeFirmwareUpdateProgressMessage
|
||||
) => {
|
||||
if (message.event === "firmware update progress") {
|
||||
if (!this._updateFinishedMessage) {
|
||||
this._updateProgressMessage = message;
|
||||
}
|
||||
} else {
|
||||
this._unsubscribeNodeFirmwareUpdate();
|
||||
this._updateProgressMessage = undefined;
|
||||
this._updateInProgress = false;
|
||||
this._updateFinishedMessage = message;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _unsubscribeNodeFirmwareUpdate(): void {
|
||||
if (!this._subscribedNodeFirmwareUpdate) {
|
||||
return;
|
||||
}
|
||||
this._subscribedNodeFirmwareUpdate.then((unsub) => unsub());
|
||||
this._subscribedNodeFirmwareUpdate = undefined;
|
||||
}
|
||||
|
||||
private async _firmwareTargetChanged(ev) {
|
||||
this._firmwareTarget = ev.detail.value.firmware_target;
|
||||
}
|
||||
|
||||
private async _uploadFile(ev) {
|
||||
this._firmwareFile = ev.detail.files[0];
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
.ok {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
width: 68px;
|
||||
height: 48px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zwave_js-update-firmware-node": DialogZWaveJSUpdateFirmwareNode;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
|
||||
import { ZWaveJSNodeFirmwareUpdateCapabilities } from "../../../../../data/zwave_js";
|
||||
|
||||
export interface ZWaveJSUpdateFirmwareNodeDialogParams {
|
||||
device: DeviceRegistryEntry;
|
||||
firmwareUpdateCapabilities: ZWaveJSNodeFirmwareUpdateCapabilities;
|
||||
}
|
||||
|
||||
export const loadUpdateFirmwareNodeDialog = () =>
|
||||
import("./dialog-zwave_js-update-firmware-node");
|
||||
|
||||
export const showZWaveJUpdateFirmwareNodeDialog = (
|
||||
element: HTMLElement,
|
||||
updateFirmwareNodeDialogParams: ZWaveJSUpdateFirmwareNodeDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zwave_js-update-firmware-node",
|
||||
dialogImport: loadUpdateFirmwareNodeDialog,
|
||||
dialogParams: updateFirmwareNodeDialogParams,
|
||||
});
|
||||
};
|
@ -3107,6 +3107,7 @@
|
||||
"reinterview_device": "Re-interview Device",
|
||||
"heal_node": "Heal Device",
|
||||
"remove_failed": "Remove Failed Device",
|
||||
"update_firmware": "Update Device Firmware",
|
||||
"highest_security": "Highest Security",
|
||||
"unknown": "Unknown",
|
||||
"zwave_plus": "Z-Wave Plus",
|
||||
@ -3314,6 +3315,43 @@
|
||||
"in_progress": "{device} healing is in progress.",
|
||||
"network_heal_in_progress": "A Z-Wave network heal is already in progress. Please wait for it to finish before healing an individual device."
|
||||
},
|
||||
"update_firmware": {
|
||||
"title": "Update Device Firmware",
|
||||
"warning": "WARNING: Firmware updates can brick your device if you do not correctly follow the manufacturer's guidance. The Home Assistant and Z-Wave JS teams do not take any responsibility for any damages to your device as a result of the firmware update and will not be able to help you if you brick your device. Would you still like to continue?",
|
||||
"introduction": "Select the firmware file you would like to use to update {device}.",
|
||||
"upload_firmware": "Upload Firmware",
|
||||
"firmware_target_intro": "Select the firmware target (0 for the Z-Wave chip, ≥1 for other chips if they exist) for this update, or uncheck the box to have the driver attempt to figure it out from the firmware file.",
|
||||
"firmware_target": "Firmware Target (chip)",
|
||||
"upload_failed": "Upload Failed",
|
||||
"begin_update": "Begin Firmware Update",
|
||||
"queued": "The firmware update is ready to be sent to {device} but the device is asleep, wake the device to start the update.",
|
||||
"close_queued": "If you close this dialog, the update will continue to be queued in the background and start automatically once the device wakes up.",
|
||||
"awake": "The firmware update should start being sent to {device} shortly.",
|
||||
"close": "If you close this dialog, the update will continue in the background.",
|
||||
"in_progress": "The firmware update on {device} is in progress ({progress}%).",
|
||||
"abort": "Abort Firmware Update",
|
||||
"abort_failed": "Abort Failed",
|
||||
"confirm_abort": "Are you sure you want to abort the firmware update on {device}?",
|
||||
"finished_status": {
|
||||
"ok": "Successfully updated firmware on {device}: {message}.",
|
||||
"error": "Unable to update firmware on {device}: {message}.",
|
||||
"try_again": "To attempt the firmware update again, select the new firmware file you would like to use.",
|
||||
"done": "The firmware update is complete! If you want to attempt another firmware update on this device, please wait until it gets re-interviewed.",
|
||||
"Error_Timeout": "Timed Out",
|
||||
"Error_Checksum": "Checksum Error",
|
||||
"Error_TransmissionFailed": "Transmission Failed",
|
||||
"Error_InvalidManufacturerID": "Invalid Manufacturer ID",
|
||||
"Error_InvalidFirmwareID": "Invalid Firmware ID",
|
||||
"Error_InvalidFirmwareTarget": "Invalid Firmware Target",
|
||||
"Error_InvalidHeaderInformation": "Invalid Header Information",
|
||||
"Error_InvalidHeaderFormat": "Invalid Header Format",
|
||||
"Error_InsufficientMemory": "Insufficient Memory",
|
||||
"Error_InvalidHardwareVersion": "Invalid Hardware Version",
|
||||
"OK_WaitingForActivation": "Waiting for Activiation",
|
||||
"OK_NoRestart": "No Restart",
|
||||
"OK_RestartPending": "Restart Pending"
|
||||
}
|
||||
},
|
||||
"logs": {
|
||||
"title": "Z-Wave JS Logs",
|
||||
"log_level": "Log Level",
|
||||
|
Loading…
x
Reference in New Issue
Block a user