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

refactor: decouple Serial API Message parsing and creation #7306

Merged
merged 8 commits into from
Oct 23, 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
3 changes: 0 additions & 3 deletions packages/cc/src/cc/LanguageCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,11 @@ export class LanguageCCReport extends LanguageCC {
options: WithAddress<LanguageCCReportOptions>,
) {
super(options);

// TODO: Check implementation:
this.language = options.language;
this.country = options.country;
}

public static from(raw: CCRaw, ctx: CCParsingContext): LanguageCCReport {
// if (gotDeserializationOptions(options)) {
validatePayload(raw.payload.length >= 3);
const language = raw.payload.toString("ascii", 0, 3);
let country: MaybeNotKnown<string>;
Expand Down
12 changes: 0 additions & 12 deletions packages/cc/src/cc/NoOperationCC.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { CommandClasses, MessagePriority } from "@zwave-js/core/safe";
import type { Message } from "@zwave-js/serial";
import { PhysicalCCAPI } from "../lib/API";
import { CommandClass } from "../lib/CommandClass";
import {
API,
commandClass,
implementedVersion,
} from "../lib/CommandClassDecorators";
import { isCommandClassContainer } from "../lib/ICommandClassContainer";

// @noSetValueAPI This CC has no set-type commands
// @noInterview There's nothing to interview here
Expand Down Expand Up @@ -37,13 +35,3 @@ export class NoOperationCCAPI extends PhysicalCCAPI {
export class NoOperationCC extends CommandClass {
declare ccCommand: undefined;
}

/**
* @publicAPI
* Tests if a given message is a ping
*/
export function messageIsPing<T extends Message>(
msg: T,
): msg is T & { command: NoOperationCC } {
return isCommandClassContainer(msg) && msg.command instanceof NoOperationCC;
}
1 change: 1 addition & 0 deletions packages/cc/src/cc/NotificationCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,7 @@ export class NotificationCCReport extends NotificationCC {
// Convert CommandClass instances to a standardized object representation
const cc = CommandClass.parse(this.eventParameters, {
...ctx,
frameType: "singlecast",
sourceNodeId: this.nodeId as number,
// Security encapsulation is handled outside of this CC,
// so it is not needed here:
Expand Down
2 changes: 1 addition & 1 deletion packages/cc/src/cc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ export {
MultilevelSwitchCCSupportedReport,
MultilevelSwitchCCValues,
} from "./MultilevelSwitchCC";
export { NoOperationCC, messageIsPing } from "./NoOperationCC";
export { NoOperationCC } from "./NoOperationCC";
export type {
NodeNamingAndLocationCCLocationReportOptions,
NodeNamingAndLocationCCLocationSetOptions,
Expand Down
1 change: 0 additions & 1 deletion packages/cc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export * from "./lib/API";
export * from "./lib/CommandClass";
export * from "./lib/CommandClassDecorators";
export * from "./lib/EncapsulatingCommandClass";
export * from "./lib/ICommandClassContainer";
export {
MGRPExtension,
MOSExtension,
Expand Down
37 changes: 2 additions & 35 deletions packages/cc/src/lib/CommandClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ import {
isEncapsulatingCommandClass,
isMultiEncapsulatingCommandClass,
} from "./EncapsulatingCommandClass";
import {
type ICommandClassContainer,
isCommandClassContainer,
} from "./ICommandClassContainer";
import {
type CCValue,
type DynamicCCValue,
Expand Down Expand Up @@ -195,15 +191,6 @@ export class CCRaw {
public withPayload(payload: Buffer): CCRaw {
return new CCRaw(this.ccId, this.ccCommand, payload);
}

public serialize(): Buffer {
const ccIdLength = this.ccId >= 0xf100 ? 2 : 1;
const data = Buffer.allocUnsafe(ccIdLength + 1 + this.payload.length);
data.writeUIntBE(this.ccId, 0, ccIdLength);
data[ccIdLength] = this.ccCommand ?? 0;
this.payload.copy(data, ccIdLength + 1);
return data;
}
}

// @publicAPI
Expand All @@ -226,10 +213,10 @@ export class CommandClass implements CCId {
}

public static parse(
payload: Buffer,
data: Buffer,
ctx: CCParsingContext,
): CommandClass {
const raw = CCRaw.parse(payload);
const raw = CCRaw.parse(data);

// Find the correct subclass constructor to invoke
const CCConstructor = getCCConstructor(raw.ccId);
Expand Down Expand Up @@ -1189,26 +1176,6 @@ export class InvalidCC extends CommandClass {
}
}

/** @publicAPI */
export function assertValidCCs(container: ICommandClassContainer): void {
if (container.command instanceof InvalidCC) {
if (typeof container.command.reason === "number") {
throw new ZWaveError(
"The message payload failed validation!",
container.command.reason,
);
} else {
throw new ZWaveError(
"The message payload is invalid!",
ZWaveErrorCodes.PacketFormat_InvalidPayload,
container.command.reason,
);
}
} else if (isCommandClassContainer(container.command)) {
assertValidCCs(container.command);
}
}

export type CCConstructor<T extends CommandClass> = typeof CommandClass & {
// I don't like the any, but we need it to support half-implemented CCs (e.g. report classes)
new (options: any): T;
Expand Down
14 changes: 0 additions & 14 deletions packages/cc/src/lib/ICommandClassContainer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +0,0 @@
import { CommandClass } from "./CommandClass";

export interface ICommandClassContainer {
command: CommandClass;
}

/**
* Tests if the given message contains a CC
*/
export function isCommandClassContainer<T>(
msg: T | undefined,
): msg is T & ICommandClassContainer {
return (msg as any)?.command instanceof CommandClass;
}
2 changes: 0 additions & 2 deletions packages/core/src/consts/Transmission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ export function routingSchemeToString(scheme: RoutingScheme): string {
export interface TXReport {
/** Transmission time in ticks (multiples of 10ms) */
txTicks: number;
/** Number of repeaters used in the route to the destination, 0 for direct range */
numRepeaters: number;
/** RSSI value of the acknowledgement frame */
ackRSSI?: RSSI;
/** RSSI values of the incoming acknowledgement frame, measured by repeater 0...3 */
Expand Down
12 changes: 1 addition & 11 deletions packages/host/src/ZWaveHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,6 @@ export interface HostIDs {
homeId: number;
}

// FIXME: This should not be needed. Instead have the driver set callback IDs during sendMessage
/** Allows generating a new callback ID */
export interface GetNextCallbackId {
/**
* Returns the next callback ID. Callback IDs are used to correlate requests
* to the controller/nodes with its response
*/
getNextCallbackId(): number;
}

/** Allows querying device configuration for a node */
export interface GetDeviceConfig {
getDeviceConfig(nodeId: number): DeviceConfig | undefined;
Expand Down Expand Up @@ -71,7 +61,7 @@ export interface CCParsingContext
__internalIsMockNode?: boolean;

/** If known, the frame type of the containing message */
frameType?: FrameType;
frameType: FrameType;

getHighestSecurityClass(nodeId: number): MaybeNotKnown<SecurityClass>;

Expand Down
5 changes: 2 additions & 3 deletions packages/maintenance/src/generateTypedDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,8 @@ async function processCCDocFile(
if (!APIClass) return;

const ccId = getCommandClassFromClassDeclaration(
// FIXME: there seems to be some discrepancy between ts-morph's bundled typescript and our typescript
file.compilerNode as any,
APIClass.compilerNode as any,
file.compilerNode,
APIClass.compilerNode,
);
if (ccId == undefined) return;
const ccName = getCCName(ccId);
Expand Down
Loading
Loading