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

fix: try to handle non-implemented CCs #7080

Merged
merged 2 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 11 additions & 10 deletions packages/cc/src/lib/CommandClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ export class CommandClass implements ICommandClass {
// empty constructor to parse messages
public constructor(host: ZWaveHost, options: CommandClassOptions) {
this.host = host;
// Extract the cc from declared metadata if not provided by the CC constructor
this.ccId = "ccId" in options && options.ccId != undefined
? options.ccId
: getCommandClass(this);
// Default to the root endpoint - Inherited classes may override this behavior
this.endpointIndex =
("endpoint" in options ? options.endpoint : undefined) ?? 0;
Expand All @@ -130,10 +126,11 @@ export class CommandClass implements ICommandClass {
const CCConstructor =
getCCConstructor(CommandClass.getCommandClass(options.data))
?? CommandClass;
const ccId = CommandClass.getCommandClass(options.data);
const ccCommand = CCConstructor.getCCCommand(options.data);
if (ccCommand != undefined) {
const CommandConstructor = getCCCommandConstructor(
this.ccId,
ccId,
ccCommand,
);
if (
Expand Down Expand Up @@ -167,10 +164,14 @@ export class CommandClass implements ICommandClass {
} else if (gotCCCommandOptions(options)) {
const {
nodeId,
endpoint = 0,
ccId = getCommandClass(this),
ccCommand = getCCCommand(this),
payload = Buffer.allocUnsafe(0),
} = options;
this.nodeId = nodeId;
this.endpointIndex = endpoint;
this.ccId = ccId;
this.ccCommand = ccCommand;
this.payload = payload;
}
Expand Down Expand Up @@ -224,7 +225,7 @@ export class CommandClass implements ICommandClass {
protected host: ZWaveHost;

/** This CC's identifier */
public ccId: CommandClasses;
public ccId!: CommandClasses;
public ccCommand?: number;
public get ccName(): string {
return getCCName(this.ccId);
Expand Down Expand Up @@ -369,7 +370,6 @@ export class CommandClass implements ICommandClass {
* It is assumed that the buffer only contains the serialized CC. This throws if the CC is not implemented.
*/
public static getConstructor(ccData: Buffer): CCConstructor<CommandClass> {
// Encapsulated CCs don't have the two header bytes
const cc = CommandClass.getCommandClass(ccData);
const ret = getCCConstructor(cc);
if (!ret) {
Expand All @@ -390,10 +390,11 @@ export class CommandClass implements ICommandClass {
options: CommandClassDeserializationOptions,
): CommandClass {
// Fall back to unspecified command class in case we receive one that is not implemented
const Constructor = CommandClass.getConstructor(options.data);
const ccId = CommandClass.getCommandClass(options.data);
const Constructor = getCCConstructor(ccId) ?? CommandClass;

try {
const ret = new Constructor(host, options);
return ret;
return new Constructor(host, options);
} catch (e) {
// Indicate invalid payloads with a special CC type
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { CommandClass } from "@zwave-js/cc";
import { CommandClasses } from "@zwave-js/core";
import { createMockZWaveRequestFrame } from "@zwave-js/testing";
import { integrationTest } from "../integrationTestSuite";

integrationTest(
"Command classes that are not implemented are passed to awaiters before being dropped",
{
// debug: true,

nodeCapabilities: {
commandClasses: [
CommandClasses.Version,
CommandClasses.Basic,
],
},

testBody: async (t, driver, node, mockController, mockNode) => {
// Anti theft will not be implemented, so we can use it to test this
const awaited = driver.waitForCommand(
(cc) => cc.ccId === CommandClasses["Anti-Theft"],
1000,
);

const cc = new CommandClass(mockNode.host, {
nodeId: mockController.host.ownNodeId,
ccId: CommandClasses["Anti-Theft"],
ccCommand: 0x02, // Get
payload: Buffer.from([0x00, 0x01]), // Technically invalid
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);

const result = await awaited;
t.like(result, {
ccId: CommandClasses["Anti-Theft"],
ccCommand: 0x02,
payload: Buffer.from([0x00, 0x01]),
});
},
},
);
Loading