Skip to content

Commit

Permalink
feat: add command to shut down Z-Wave chip (#5553)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone authored Mar 9, 2023
1 parent 2ac4a9a commit bccda7c
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 1 deletion.
10 changes: 10 additions & 0 deletions docs/api/driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ The following table gives you an overview of what happens during the startup pro
| 4 | - | `"all nodes ready"` event is emitted for the driver when all nodes can be used |
| 5 | - | `"interview completed"` event is emitted for every node when its interview is completed for the first time. This only gets emitted once, unless the node gets re-interviewed. |

### `shutdown`

```ts
async shutdown(): Promise<boolean>
```

If supported by the controller, this instructs it to shut down the Z-Wave API, so it can safely be removed from power. If this is successful (returns `true`), the driver instance will be destroyed and can no longer be used.

> [!WARNING] The controller will have to be restarted manually (e.g. by unplugging and plugging it back in) before it can be used again!
### `interviewNode`

```ts
Expand Down
2 changes: 2 additions & 0 deletions packages/serial/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ export enum FunctionType {
// (undocumented)
SetSUCNodeId = 84,
// (undocumented)
Shutdown = 217,
// (undocumented)
SoftReset = 8,
// (undocumented)
UNKNOWN_FUNC_ClearNetworkStats = 57,
Expand Down
3 changes: 3 additions & 0 deletions packages/serial/src/message/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ export enum FunctionType {
UNKNOWN_FUNC_UNKNOWN_0xD2 = 0xd2, // ??
UNKNOWN_FUNC_UNKNOWN_0xD3 = 0xd3, // ??
UNKNOWN_FUNC_UNKNOWN_0xD4 = 0xd4, // ??

Shutdown = 0xd9, // Instruct the Z-Wave API to shut down in order to safely remove the power

UNKNOWN_FUNC_UNKNOWN_0xEF = 0xef, // ??

// Special commands for Z-Wave.me sticks
Expand Down
1 change: 1 addition & 0 deletions packages/zwave-js/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ export class Driver extends TypedEventEmitter<DriverEventCallbacks> implements Z
sendMessage<TResponse extends Message = Message>(msg: Message, options?: SendMessageOptions): Promise<TResponse>;
get sendThreadIdle(): boolean;
setPreferredScales(scales: ZWaveOptions["preferences"]["scales"]): void;
shutdown(): Promise<boolean>;
softReset(): Promise<void>;
start(): Promise<void>;
get statisticsEnabled(): boolean;
Expand Down
31 changes: 31 additions & 0 deletions packages/zwave-js/src/lib/controller/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ import {
ApplicationUpdateRequestSmartStartHomeIDReceived,
type ApplicationUpdateRequest,
} from "../serialapi/application/ApplicationUpdateRequest";
import {
ShutdownRequest,
ShutdownResponse,
} from "../serialapi/application/ShutdownMessages";
import {
GetControllerCapabilitiesRequest,
GetControllerCapabilitiesResponse,
Expand Down Expand Up @@ -1404,6 +1408,33 @@ export class ZWaveController extends TypedEventEmitter<ControllerEventCallbacks>
}
}

/**
* @internal
*/
public async shutdown(): Promise<boolean> {
// begin the reset process
try {
this.driver.controllerLog.print("Shutting down the Z-Wave API...");
const response = await this.driver.sendMessage<ShutdownResponse>(
new ShutdownRequest(this.driver),
);
if (response.success) {
this.driver.controllerLog.print("Z-Wave API was shut down");
} else {
this.driver.controllerLog.print(
"Failed to shut down the Z-Wave API",
);
}
return response.success;
} catch (e) {
this.driver.controllerLog.print(
`shutdown failed: ${getErrorMessage(e)}`,
"error",
);
throw e;
}
}

private _inclusionState: InclusionState = InclusionState.Idle;
public get inclusionState(): InclusionState {
return this._inclusionState;
Expand Down
27 changes: 26 additions & 1 deletion packages/zwave-js/src/lib/driver/Driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2532,6 +2532,31 @@ export class Driver
void this.initializeControllerAndNodes();
}

/**
* Instructs the Z-Wave API to shut down in order to safely remove the power.
* This will destroy the driver instance if it succeeds.
*/
public async shutdown(): Promise<boolean> {
this.ensureReady(true);

// Not a good idea to abort firmware updates this way
if (this.controller.isAnyOTAFirmwareUpdateInProgress()) {
const message = `Failed to shut down controller: A firmware update is in progress on this network.`;
this.controllerLog.print(message, "error");
throw new ZWaveError(
message,
ZWaveErrorCodes.FirmwareUpdateCC_NetworkBusy,
);
}

const result = await this.controller.shutdown();
try {
if (result) await this.destroy();
} finally {
return result;
}
}

private _destroyPromise: DeferredPromise<void> | undefined;
private get wasDestroyed(): boolean {
return !!this._destroyPromise;
Expand Down Expand Up @@ -5124,7 +5149,7 @@ ${handlers.length} left`,
void this.controller.getBackgroundRSSI().catch(() => {
// ignore errors
});
}, timeout);
}, timeout).unref();
} else {
clearTimeout(this.pollBackgroundRSSITimer);
this.pollBackgroundRSSITimer = undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { MessageOrCCLogEntry, MessagePriority } from "@zwave-js/core";
import type { ZWaveHost } from "@zwave-js/host";
import {
expectedResponse,
FunctionType,
Message,
MessageBaseOptions,
MessageDeserializationOptions,
MessageType,
messageTypes,
priority,
} from "@zwave-js/serial";

export interface ShutdownRequestOptions extends MessageBaseOptions {
someProperty: number;
}

@messageTypes(MessageType.Request, FunctionType.Shutdown)
@priority(MessagePriority.Normal)
@expectedResponse(FunctionType.Shutdown)
export class ShutdownRequest extends Message {}

@messageTypes(MessageType.Response, FunctionType.Shutdown)
export class ShutdownResponse extends Message {
public constructor(
host: ZWaveHost,
options: MessageDeserializationOptions,
) {
super(host, options);
this.success = this.payload[0] !== 0;
}

public readonly success: boolean;

public toLogEntry(): MessageOrCCLogEntry {
return {
...super.toLogEntry(),
message: { success: this.success },
};
}
}

0 comments on commit bccda7c

Please sign in to comment.