Skip to content

Commit eac2df0

Browse files
raman325claudeAlCalzone
authored
feat: add schema 47 driver commands (2/4) (#1510)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: AlCalzone <dominic.griesel@nabucasa.com>
1 parent cc64b5c commit eac2df0

File tree

7 files changed

+270
-12
lines changed

7 files changed

+270
-12
lines changed

API_SCHEMA.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,5 @@ Base schema.
343343
- Added controller state properties: `isSIS`, `maxPayloadSize`, `maxPayloadSizeLR`, `zwaveApiVersion`, `zwaveChipType`
344344
- Added driver events: `all nodes ready`, `error`, `bootloader ready`
345345
- Added controller events: `network found`, `network joined`, `network left`, `joining network failed`, `leaving network failed`
346+
- Added driver commands: `soft_reset_and_restart`, `enter_bootloader`, `leave_bootloader`, `get_supported_cc_version`, `get_safe_cc_version`, `update_user_agent`, `enable_frequent_rssi_monitoring`, `disable_frequent_rssi_monitoring`
347+
- Automatic ZIP extraction for firmware update commands

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,8 @@ interface {
443443
}
444444
```
445445

446+
The server will automatically attempt to extract firmware from a ZIP archive.
447+
446448
If `fileFormat` is not provided in Option 1, the format will be guessed based on the filename and file payload.
447449

448450
Returns:
@@ -472,6 +474,102 @@ interface {
472474
}
473475
```
474476

477+
#### [Soft Reset and Restart](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=softresetandrestart)
478+
479+
[compatible with schema version: 47+]
480+
481+
```ts
482+
interface {
483+
messageId: string;
484+
command: "driver.soft_reset_and_restart";
485+
}
486+
```
487+
488+
#### [Enter Bootloader](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=enterbootloader)
489+
490+
[compatible with schema version: 47+]
491+
492+
```ts
493+
interface {
494+
messageId: string;
495+
command: "driver.enter_bootloader";
496+
}
497+
```
498+
499+
#### [Leave Bootloader](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=leavebootloader)
500+
501+
[compatible with schema version: 47+]
502+
503+
```ts
504+
interface {
505+
messageId: string;
506+
command: "driver.leave_bootloader";
507+
}
508+
```
509+
510+
#### [Get Supported CC Version](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=getsupportedccversion)
511+
512+
[compatible with schema version: 47+]
513+
514+
```ts
515+
interface {
516+
messageId: string;
517+
command: "driver.get_supported_cc_version";
518+
cc: CommandClasses;
519+
nodeId: number;
520+
endpointIndex?: number;
521+
}
522+
```
523+
524+
#### [Get Safe CC Version](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=getsafeccversion)
525+
526+
[compatible with schema version: 47+]
527+
528+
```ts
529+
interface {
530+
messageId: string;
531+
command: "driver.get_safe_cc_version";
532+
cc: CommandClasses;
533+
nodeId: number;
534+
endpointIndex?: number;
535+
}
536+
```
537+
538+
#### [Update User Agent](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=updateuseragent)
539+
540+
[compatible with schema version: 47+]
541+
542+
```ts
543+
interface {
544+
messageId: string;
545+
command: "driver.update_user_agent";
546+
components: Record<string, string | null | undefined>;
547+
}
548+
```
549+
550+
#### [Enable Frequent RSSI Monitoring](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=enablefrequentrssimonitoring)
551+
552+
[compatible with schema version: 47+]
553+
554+
```ts
555+
interface {
556+
messageId: string;
557+
command: "driver.enable_frequent_rssi_monitoring";
558+
durationMs: number;
559+
}
560+
```
561+
562+
#### [Disable Frequent RSSI Monitoring](https://zwave-js.github.io/node-zwave-js/#/api/driver?id=disablefrequentrssimonitoring)
563+
564+
[compatible with schema version: 47+]
565+
566+
```ts
567+
interface {
568+
messageId: string;
569+
command: "driver.disable_frequent_rssi_monitoring";
570+
}
571+
```
572+
475573
### Controller level commands
476574

477575
`zwave-js-server` supports all of the controller methods listed in the [Z-Wave JS documentation](https://zwave-js.github.io/node-zwave-js/#/api/controller?id=controller-methods). `zwave-js-server` uses [snake casing](https://en.wikipedia.org/wiki/Snake_case) for commands and prefixes every controller command with `controller.`, so `beginInclusion` is called using the `controller.begin_inclusion` command.

src/lib/common.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ import {
88
ZWaveNode,
99
} from "zwave-js";
1010
import type { ConfigurationCCAPISetOptions } from "@zwave-js/cc";
11-
import { SupervisionResult, MaybeNotKnown } from "@zwave-js/core";
11+
import {
12+
extractFirmware,
13+
Firmware,
14+
FirmwareFileFormat,
15+
guessFirmwareFileFormat,
16+
MaybeNotKnown,
17+
SupervisionResult,
18+
tryUnzipFirmwareFile,
19+
} from "@zwave-js/core";
1220
import {
1321
IncomingCommandNodeGetRawConfigParameterValue,
1422
IncomingCommandNodeSetRawConfigParameterValue,
@@ -109,3 +117,41 @@ export async function getRawConfigParameterValue(
109117
);
110118
return { value };
111119
}
120+
121+
// Precedence:
122+
// 1. Always try ZIP extraction first — firmware files may be zipped
123+
// regardless of filename extension or explicit format.
124+
// 2. If an explicit format is provided, use it (on the unzipped data if
125+
// applicable, otherwise on the raw data as-is).
126+
// 3. Fall back to guessing the format from the filename and raw data.
127+
function parseFirmwareFile(
128+
filename: string,
129+
rawData: Uint8Array<ArrayBuffer>,
130+
explicitFormat?: FirmwareFileFormat,
131+
): { rawData: Uint8Array<ArrayBuffer>; format: FirmwareFileFormat } {
132+
const unzipped = tryUnzipFirmwareFile(rawData);
133+
if (unzipped) {
134+
return explicitFormat !== undefined
135+
? { rawData: unzipped.rawData, format: explicitFormat }
136+
: unzipped;
137+
}
138+
139+
if (explicitFormat !== undefined) {
140+
return { rawData, format: explicitFormat };
141+
}
142+
143+
return { rawData, format: guessFirmwareFileFormat(filename, rawData) };
144+
}
145+
146+
/**
147+
* Parses a firmware file (handling format detection and ZIP extraction)
148+
* and extracts the firmware data.
149+
*/
150+
export async function parseAndExtractFirmware(
151+
filename: string,
152+
rawData: Uint8Array<ArrayBuffer>,
153+
explicitFormat?: FirmwareFileFormat,
154+
): Promise<Firmware> {
155+
const parsed = parseFirmwareFile(filename, rawData, explicitFormat);
156+
return extractFirmware(parsed.rawData, parsed.format);
157+
}

src/lib/driver/command.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,15 @@ export enum DriverCommand {
2121
firmwareUpdateOTW = "driver.firmware_update_otw",
2222
// Schema version >= 41:
2323
isOTWFirmwareUpdateInProgress = "driver.is_otw_firmware_update_in_progress",
24+
softResetAndRestart = "driver.soft_reset_and_restart",
25+
enterBootloader = "driver.enter_bootloader",
26+
leaveBootloader = "driver.leave_bootloader",
27+
// CC version queries
28+
getSupportedCCVersion = "driver.get_supported_cc_version",
29+
getSafeCCVersion = "driver.get_safe_cc_version",
30+
// User agent
31+
updateUserAgent = "driver.update_user_agent",
32+
// RSSI monitoring
33+
enableFrequentRSSIMonitoring = "driver.enable_frequent_rssi_monitoring",
34+
disableFrequentRSSIMonitoring = "driver.disable_frequent_rssi_monitoring",
2435
}

src/lib/driver/incoming_message.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FirmwareFileFormat, LogConfig } from "@zwave-js/core";
1+
import { CommandClasses, FirmwareFileFormat, LogConfig } from "@zwave-js/core";
22
import { DriverCommand } from "./command.js";
33
import { IncomingCommandBase } from "../incoming_message_base.js";
44
import {
@@ -109,6 +109,49 @@ export interface IncomingCommandIsOTWFirmwareUpdateInProgress extends IncomingCo
109109
command: DriverCommand.isOTWFirmwareUpdateInProgress;
110110
}
111111

112+
interface IncomingCommandSoftResetAndRestart extends IncomingCommandBase {
113+
command: DriverCommand.softResetAndRestart;
114+
}
115+
116+
interface IncomingCommandEnterBootloader extends IncomingCommandBase {
117+
command: DriverCommand.enterBootloader;
118+
}
119+
120+
interface IncomingCommandLeaveBootloader extends IncomingCommandBase {
121+
command: DriverCommand.leaveBootloader;
122+
}
123+
124+
// CC version queries
125+
interface IncomingCommandGetSupportedCCVersion extends IncomingCommandBase {
126+
command: DriverCommand.getSupportedCCVersion;
127+
cc: CommandClasses;
128+
nodeId: number;
129+
endpointIndex?: number;
130+
}
131+
132+
interface IncomingCommandGetSafeCCVersion extends IncomingCommandBase {
133+
command: DriverCommand.getSafeCCVersion;
134+
cc: CommandClasses;
135+
nodeId: number;
136+
endpointIndex?: number;
137+
}
138+
139+
// User agent
140+
interface IncomingCommandUpdateUserAgent extends IncomingCommandBase {
141+
command: DriverCommand.updateUserAgent;
142+
components: Record<string, string | null | undefined>;
143+
}
144+
145+
// RSSI monitoring
146+
interface IncomingCommandEnableFrequentRSSIMonitoring extends IncomingCommandBase {
147+
command: DriverCommand.enableFrequentRSSIMonitoring;
148+
durationMs: number;
149+
}
150+
151+
interface IncomingCommandDisableFrequentRSSIMonitoring extends IncomingCommandBase {
152+
command: DriverCommand.disableFrequentRSSIMonitoring;
153+
}
154+
112155
export type IncomingMessageDriver =
113156
| IncomingCommandGetConfig
114157
| IncomingCommandUpdateLogConfig
@@ -129,4 +172,12 @@ export type IncomingMessageDriver =
129172
| IncomingCommandUpdateOptions
130173
| IncomingCommandSendTestFrame
131174
| IncomingCommandFirmwareUpdateOTW
132-
| IncomingCommandIsOTWFirmwareUpdateInProgress;
175+
| IncomingCommandIsOTWFirmwareUpdateInProgress
176+
| IncomingCommandSoftResetAndRestart
177+
| IncomingCommandEnterBootloader
178+
| IncomingCommandLeaveBootloader
179+
| IncomingCommandGetSupportedCCVersion
180+
| IncomingCommandGetSafeCCVersion
181+
| IncomingCommandUpdateUserAgent
182+
| IncomingCommandEnableFrequentRSSIMonitoring
183+
| IncomingCommandDisableFrequentRSSIMonitoring;

src/lib/driver/message_handler.ts

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
import {
2-
Driver,
3-
extractFirmware,
4-
guessFirmwareFileFormat,
5-
OTWFirmwareUpdateResult,
6-
} from "zwave-js";
1+
import { Driver, OTWFirmwareUpdateResult } from "zwave-js";
72
import { UnknownCommandError } from "../error.js";
3+
import { parseAndExtractFirmware } from "../common.js";
84
import {
95
Client,
106
ClientsController,
@@ -136,10 +132,10 @@ export class DriverMessageHandler implements MessageHandler {
136132
result = await this.driver.firmwareUpdateOTW(message.updateInfo);
137133
} else {
138134
const file = Buffer.from(message.file, "base64");
139-
const { data } = await extractFirmware(
135+
const { data } = await parseAndExtractFirmware(
136+
message.filename,
140137
file,
141-
message.fileFormat ??
142-
guessFirmwareFileFormat(message.filename, file),
138+
message.fileFormat,
143139
);
144140
result = await this.driver.firmwareUpdateOTW(data);
145141
}
@@ -149,6 +145,49 @@ export class DriverMessageHandler implements MessageHandler {
149145
const progress = this.driver.isOTWFirmwareUpdateInProgress();
150146
return { progress };
151147
}
148+
case DriverCommand.softResetAndRestart: {
149+
await this.driver.softResetAndRestart();
150+
return {};
151+
}
152+
case DriverCommand.enterBootloader: {
153+
await this.driver.enterBootloader();
154+
return {};
155+
}
156+
case DriverCommand.leaveBootloader: {
157+
await this.driver.leaveBootloader();
158+
return {};
159+
}
160+
// CC version queries
161+
case DriverCommand.getSupportedCCVersion: {
162+
const version = this.driver.getSupportedCCVersion(
163+
message.cc,
164+
message.nodeId,
165+
message.endpointIndex,
166+
);
167+
return { version };
168+
}
169+
case DriverCommand.getSafeCCVersion: {
170+
const version = this.driver.getSafeCCVersion(
171+
message.cc,
172+
message.nodeId,
173+
message.endpointIndex,
174+
);
175+
return { version };
176+
}
177+
// User agent
178+
case DriverCommand.updateUserAgent: {
179+
this.driver.updateUserAgent(message.components);
180+
return {};
181+
}
182+
// RSSI monitoring
183+
case DriverCommand.enableFrequentRSSIMonitoring: {
184+
this.driver.enableFrequentRSSIMonitoring(message.durationMs);
185+
return {};
186+
}
187+
case DriverCommand.disableFrequentRSSIMonitoring: {
188+
this.driver.disableFrequentRSSIMonitoring();
189+
return {};
190+
}
152191
default: {
153192
throw new UnknownCommandError(command);
154193
}

src/lib/driver/outgoing_message.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,15 @@ export interface DriverResultTypes {
2828
[DriverCommand.sendTestFrame]: { status?: TransmitStatus };
2929
[DriverCommand.firmwareUpdateOTW]: OTWFirmwareUpdateResultType;
3030
[DriverCommand.isOTWFirmwareUpdateInProgress]: { progress: boolean };
31+
[DriverCommand.softResetAndRestart]: Record<string, never>;
32+
[DriverCommand.enterBootloader]: Record<string, never>;
33+
[DriverCommand.leaveBootloader]: Record<string, never>;
34+
// CC version queries
35+
[DriverCommand.getSupportedCCVersion]: { version: number };
36+
[DriverCommand.getSafeCCVersion]: { version?: number };
37+
// User agent
38+
[DriverCommand.updateUserAgent]: Record<string, never>;
39+
// RSSI monitoring
40+
[DriverCommand.enableFrequentRSSIMonitoring]: Record<string, never>;
41+
[DriverCommand.disableFrequentRSSIMonitoring]: Record<string, never>;
3142
}

0 commit comments

Comments
 (0)