Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable hardware watchdog on 700/800 series controllers #6954

Merged
merged 3 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/core/src/error/ZWaveError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export enum ZWaveErrorCodes {

/** The driver failed to start */
Driver_Failed = 100,
Driver_Reset,
Driver_Reset, // FIXME: This is not used
Driver_Destroyed,
Driver_NotReady,
Driver_InvalidDataReceived,
Expand All @@ -35,6 +35,9 @@ export enum ZWaveErrorCodes {
Controller_ResponseNOK,
Controller_CallbackNOK,
Controller_Jammed,
/** The controller was reset in the middle of a Serial API command */
Controller_Reset,

Controller_InclusionFailed,
Controller_ExclusionFailed,

Expand Down Expand Up @@ -294,6 +297,15 @@ export function isMissingControllerACK(
&& e.context === "ACK";
}

export function wasControllerReset(
e: unknown,
): e is ZWaveError & {
code: ZWaveErrorCodes.Controller_Reset;
} {
return isZWaveError(e)
&& e.code === ZWaveErrorCodes.Controller_Reset;
}

export function isMissingControllerResponse(
e: unknown,
): e is ZWaveError & {
Expand Down
11 changes: 6 additions & 5 deletions packages/serial/src/message/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ export enum FunctionType {

UNKNOWN_FUNC_UNKNOWN_0xB4 = 0xb4, // ??

UNKNOWN_FUNC_WATCH_DOG_ENABLE = 0xb6,
UNKNOWN_FUNC_WATCH_DOG_DISABLE = 0xb7,
UNKNOWN_FUNC_WATCH_DOG_KICK = 0xb8,
EnableWatchdog500 = 0xb6, // Enable Watchdog (500 series and older)
DisableWatchdog500 = 0xb7, // Disable Watchdog (500 series and older)
KickWatchdog500 = 0xb8, // Kick Watchdog (500 series and older)
UNKNOWN_FUNC_UNKNOWN_0xB9 = 0xb9, // ??
UNKNOWN_FUNC_RF_POWERLEVEL_GET = 0xba, // Get RF Power level

Expand All @@ -170,8 +170,9 @@ export enum FunctionType {
FUNC_ID_ZW_SET_PROMISCUOUS_MODE = 0xd0, // Set controller into promiscuous mode to listen to all messages
FUNC_ID_PROMISCUOUS_APPLICATION_COMMAND_HANDLER = 0xd1,

UNKNOWN_FUNC_UNKNOWN_0xD2 = 0xd2, // ??
UNKNOWN_FUNC_UNKNOWN_0xD3 = 0xd3, // ??
StartWatchdog = 0xd2, // Start Hardware Watchdog (700 series and newer)
StopWatchdog = 0xd3, // Stop Hardware Watchdog (700 series and newer)

UNKNOWN_FUNC_UNKNOWN_0xD4 = 0xd4, // ??

Shutdown = 0xd9, // Instruct the Z-Wave API to shut down in order to safely remove the power
Expand Down
6 changes: 6 additions & 0 deletions packages/serial/src/message/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ export class Message {
}
}

/** Tests whether this message expects an ACK from the controller */
public expectsAck(): boolean {
// By default, all commands expect an ACK
return true;
}

/** Tests whether this message expects a response from the controller */
public expectsResponse(): boolean {
return !!this.expectedResponse;
Expand Down
118 changes: 73 additions & 45 deletions packages/zwave-js/src/lib/controller/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,7 @@ import {
ApplicationUpdateRequestSmartStartHomeIDReceived,
ApplicationUpdateRequestSmartStartLongRangeHomeIDReceived,
} from "../serialapi/application/ApplicationUpdateRequest";
import {
type SerialAPIStartedRequest,
SerialAPIWakeUpReason,
} from "../serialapi/application/SerialAPIStartedRequest";

import {
ShutdownRequest,
type ShutdownResponse,
Expand Down Expand Up @@ -216,6 +213,10 @@ import {
SetSerialApiTimeoutsRequest,
type SetSerialApiTimeoutsResponse,
} from "../serialapi/misc/SetSerialApiTimeoutsMessages";
import {
StartWatchdogRequest,
StopWatchdogRequest,
} from "../serialapi/misc/WatchdogMessages";
import {
AddNodeDSKToNetworkRequest,
AddNodeStatus,
Expand Down Expand Up @@ -449,10 +450,6 @@ export class ZWaveController
FunctionType.ReplaceFailedNode,
this.handleReplaceNodeStatusReport.bind(this),
);
driver.registerRequestHandler(
FunctionType.SerialAPIStarted,
this.handleSerialAPIStartedUnexpectedly.bind(this),
);
}

private _type: MaybeNotKnown<ZWaveLibraryTypes>;
Expand Down Expand Up @@ -739,6 +736,10 @@ export class ZWaveController
public get nodeIdType(): NodeIDType {
return this._nodeIdType;
}
/** @internal */
public set nodeIdType(value: NodeIDType) {
this._nodeIdType = value;
}

/** Returns the node with the given DSK */
public getNodeByDSK(dsk: Buffer | string): ZWaveNode | undefined {
Expand Down Expand Up @@ -1888,6 +1889,63 @@ export class ZWaveController
}
}

/**
* Starts the hardware watchdog on supporting 700+ series controllers.
* Returns whether the operation was successful.
*/
public async startWatchdog(): Promise<boolean> {
if (
this.sdkVersionGte("7.0")
&& this.isFunctionSupported(FunctionType.StartWatchdog)
) {
try {
this.driver.controllerLog.print(
"Starting hardware watchdog...",
);
await this.driver.sendMessage(
new StartWatchdogRequest(this.driver),
);

return true;
} catch (e) {
this.driver.controllerLog.print(
`Starting the hardware watchdog failed: ${
getErrorMessage(e)
}`,
"error",
);
}
}
return false;
}

/**
* Stops the hardware watchdog on supporting controllers.
* Returns whether the operation was successful.
*/
public async stopWatchdog(): Promise<boolean> {
if (this.isFunctionSupported(FunctionType.StopWatchdog)) {
try {
this.driver.controllerLog.print(
"Stopping hardware watchdog...",
);
await this.driver.sendMessage(
new StopWatchdogRequest(this.driver),
);

return true;
} catch (e) {
this.driver.controllerLog.print(
`Stopping the hardware watchdog failed: ${
getErrorMessage(e)
}`,
"error",
);
}
}
return false;
}

private _inclusionState: InclusionState = InclusionState.Idle;
public get inclusionState(): InclusionState {
return this._inclusionState;
Expand Down Expand Up @@ -4331,43 +4389,6 @@ supported CCs: ${
return false;
}

/**
* Is called when the Serial API restart unexpectedly.
*/
private async handleSerialAPIStartedUnexpectedly(
msg: SerialAPIStartedRequest,
): Promise<boolean> {
// Normally, the soft reset command includes waiting for this message.
// If we end up here, it is unexpected.

switch (msg.wakeUpReason) {
// All wakeup reasons that indicate a reset of the Serial API
// need to be handled here, so we interpret node IDs correctly.
case SerialAPIWakeUpReason.Reset:
case SerialAPIWakeUpReason.WatchdogReset:
case SerialAPIWakeUpReason.SoftwareReset:
case SerialAPIWakeUpReason.EmergencyWatchdogReset:
case SerialAPIWakeUpReason.BrownoutCircuit: {
// The Serial API restarted unexpectedly
if (this._nodeIdType === NodeIDType.Long) {
this.driver.controllerLog.print(
`Serial API restarted unexpectedly.`,
"warn",
);

// We previously used 16 bit node IDs, but the controller was reset.
// Remember this and try to go back to 16 bit.
this._nodeIdType = NodeIDType.Short;
await this.trySetNodeIDType(NodeIDType.Long);
}

return true; // Don't invoke any more handlers
}
}

return false; // Not handled
}

private _rebuildRoutesProgress = new Map<number, RebuildRoutesStatus>();
/**
* If routes are currently being rebuilt for the entire network, this returns the current progress.
Expand Down Expand Up @@ -6945,13 +6966,17 @@ ${associatedNodes.join(", ")}`,
);
}

// Disable watchdog to prevent resets during NVM access
await this.stopWatchdog();

let ret: Buffer;
try {
if (this.sdkVersionGte("7.0")) {
ret = await this.backupNVMRaw700(onProgress);
// All 7.xx versions so far seem to have a bug where the NVM is not properly closed after reading
// resulting in extremely strange controller behavior after a backup. To work around this, restart the stick if possible
await this.driver.trySoftReset();
// Soft-resetting will enable the watchdog again
} else {
ret = await this.backupNVMRaw500(onProgress);
}
Expand Down Expand Up @@ -7071,6 +7096,9 @@ ${associatedNodes.join(", ")}`,
);
}

// Disable watchdog to prevent resets during NVM access
await this.stopWatchdog();

// Restoring a potentially incompatible NVM happens in three steps:
// 1. the current NVM is read
// 2. the given NVM data is converted to match the current format
Expand Down
Loading
Loading