Skip to content

Commit

Permalink
refactor: decouple CC parsing from creation (#7288)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone authored Oct 22, 2024
1 parent 72cb446 commit 02fced2
Show file tree
Hide file tree
Showing 129 changed files with 11,767 additions and 7,747 deletions.
132 changes: 90 additions & 42 deletions packages/cc/src/cc/AlarmSensorCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,26 @@ import {
MessagePriority,
type MessageRecord,
ValueMetadata,
type WithAddress,
ZWaveError,
ZWaveErrorCodes,
parseBitMask,
validatePayload,
} from "@zwave-js/core/safe";
import type { CCEncodingContext, GetValueDB } from "@zwave-js/host/safe";
import type {
CCEncodingContext,
CCParsingContext,
GetValueDB,
} from "@zwave-js/host/safe";
import { getEnumMemberName, isEnumMember, pick } from "@zwave-js/shared/safe";
import { validateArgs } from "@zwave-js/transformers";
import { CCAPI, PhysicalCCAPI } from "../lib/API";
import {
type CCCommandOptions,
type CCRaw,
CommandClass,
type CommandClassDeserializationOptions,
type InterviewContext,
type PersistValuesContext,
type RefreshValuesContext,
gotDeserializationOptions,
} from "../lib/CommandClass";
import {
API,
Expand Down Expand Up @@ -130,7 +133,7 @@ export class AlarmSensorCCAPI extends PhysicalCCAPI {

const cc = new AlarmSensorCCGet({
nodeId: this.endpoint.nodeId,
endpoint: this.endpoint.index,
endpointIndex: this.endpoint.index,
sensorType,
});
const response = await this.host.sendCommand<AlarmSensorCCReport>(
Expand All @@ -149,7 +152,7 @@ export class AlarmSensorCCAPI extends PhysicalCCAPI {

const cc = new AlarmSensorCCSupportedGet({
nodeId: this.endpoint.nodeId,
endpoint: this.endpoint.index,
endpointIndex: this.endpoint.index,
});
const response = await this.host.sendCommand<
AlarmSensorCCSupportedReport
Expand Down Expand Up @@ -318,28 +321,53 @@ duration: ${currentValue.duration}`;
}
}

// @publicAPI
export interface AlarmSensorCCReportOptions {
sensorType: AlarmSensorType;
state: boolean;
severity?: number;
duration?: number;
}

@CCCommand(AlarmSensorCommand.Report)
export class AlarmSensorCCReport extends AlarmSensorCC {
public constructor(
options: CommandClassDeserializationOptions,
options: WithAddress<AlarmSensorCCReportOptions>,
) {
super(options);
validatePayload(this.payload.length >= 5, this.payload[1] !== 0xff);
// Alarm Sensor reports may be forwarded by a different node, in this case
// (and only then!) the payload contains the original node ID
const sourceNodeId = this.payload[0];
if (sourceNodeId !== 0) {
this.nodeId = sourceNodeId;
}
this.sensorType = this.payload[1];

// TODO: Check implementation:
this.sensorType = options.sensorType;
this.state = options.state;
this.severity = options.severity;
this.duration = options.duration;
}

public static from(raw: CCRaw, ctx: CCParsingContext): AlarmSensorCCReport {
validatePayload(raw.payload.length >= 5, raw.payload[1] !== 0xff);
const sourceNodeId = raw.payload[0];

const sensorType: AlarmSensorType = raw.payload[1];
// Any positive value gets interpreted as alarm
this.state = this.payload[2] > 0;
const state: boolean = raw.payload[2] > 0;
// Severity only ranges from 1 to 100
if (this.payload[2] > 0 && this.payload[2] <= 0x64) {
this.severity = this.payload[2];
let severity: number | undefined;
if (raw.payload[2] > 0 && raw.payload[2] <= 0x64) {
severity = raw.payload[2];
}

// ignore zero durations
this.duration = this.payload.readUInt16BE(3) || undefined;
const duration = raw.payload.readUInt16BE(3) || undefined;

return new AlarmSensorCCReport({
// Alarm Sensor reports may be forwarded by a different node, in this case
// (and only then!) the payload contains the original node ID
nodeId: sourceNodeId || ctx.sourceNodeId,
sensorType,
state,
severity,
duration,
});
}

public readonly sensorType: AlarmSensorType;
Expand Down Expand Up @@ -393,26 +421,30 @@ function testResponseForAlarmSensorGet(
}

// @publicAPI
export interface AlarmSensorCCGetOptions extends CCCommandOptions {
export interface AlarmSensorCCGetOptions {
sensorType?: AlarmSensorType;
}

@CCCommand(AlarmSensorCommand.Get)
@expectedCCResponse(AlarmSensorCCReport, testResponseForAlarmSensorGet)
export class AlarmSensorCCGet extends AlarmSensorCC {
public constructor(
options: CommandClassDeserializationOptions | AlarmSensorCCGetOptions,
options: WithAddress<AlarmSensorCCGetOptions>,
) {
super(options);
if (gotDeserializationOptions(options)) {
// TODO: Deserialize payload
throw new ZWaveError(
`${this.constructor.name}: deserialization not implemented`,
ZWaveErrorCodes.Deserialization_NotImplemented,
);
} else {
this.sensorType = options.sensorType ?? AlarmSensorType.Any;
}
this.sensorType = options.sensorType ?? AlarmSensorType.Any;
}

public static from(_raw: CCRaw, _ctx: CCParsingContext): AlarmSensorCCGet {
// TODO: Deserialize payload
throw new ZWaveError(
`${this.constructor.name}: deserialization not implemented`,

Check failure

Code scanning / CodeQL

Wrong use of 'this' for static method Error

Access to instance method
constructor
from static method
from
is not possible through this.
Access to instance method
constructor
from static method
from
is not possible through this.
Access to instance method
constructor
from static method
from
is not possible through this.
ZWaveErrorCodes.Deserialization_NotImplemented,
);

// return new AlarmSensorCCGet({
// nodeId: ctx.sourceNodeId,
// });
}

public sensorType: AlarmSensorType;
Expand All @@ -435,31 +467,47 @@ export class AlarmSensorCCGet extends AlarmSensorCC {
}
}

// @publicAPI
export interface AlarmSensorCCSupportedReportOptions {
supportedSensorTypes: AlarmSensorType[];
}

@CCCommand(AlarmSensorCommand.SupportedReport)
export class AlarmSensorCCSupportedReport extends AlarmSensorCC {
public constructor(
options: CommandClassDeserializationOptions,
options: WithAddress<AlarmSensorCCSupportedReportOptions>,
) {
super(options);
validatePayload(this.payload.length >= 1);
const bitMaskLength = this.payload[0];
validatePayload(this.payload.length >= 1 + bitMaskLength);
this._supportedSensorTypes = parseBitMask(
this.payload.subarray(1, 1 + bitMaskLength),

// TODO: Check implementation:
this.supportedSensorTypes = options.supportedSensorTypes;
}

public static from(
raw: CCRaw,
ctx: CCParsingContext,
): AlarmSensorCCSupportedReport {
validatePayload(raw.payload.length >= 1);
const bitMaskLength = raw.payload[0];
validatePayload(raw.payload.length >= 1 + bitMaskLength);
const supportedSensorTypes: AlarmSensorType[] = parseBitMask(
raw.payload.subarray(1, 1 + bitMaskLength),
AlarmSensorType["General Purpose"],
);

return new AlarmSensorCCSupportedReport({
nodeId: ctx.sourceNodeId,
supportedSensorTypes,
});
}

private _supportedSensorTypes: AlarmSensorType[];
@ccValue(AlarmSensorCCValues.supportedSensorTypes)
public get supportedSensorTypes(): readonly AlarmSensorType[] {
return this._supportedSensorTypes;
}
public supportedSensorTypes: AlarmSensorType[];

public persistValues(ctx: PersistValuesContext): boolean {
if (!super.persistValues(ctx)) return false;
// Create metadata for each sensor type
for (const type of this._supportedSensorTypes) {
for (const type of this.supportedSensorTypes) {
this.createMetadataForSensorType(ctx, type);
}
return true;
Expand All @@ -469,7 +517,7 @@ export class AlarmSensorCCSupportedReport extends AlarmSensorCC {
return {
...super.toLogEntry(ctx),
message: {
"supported sensor types": this._supportedSensorTypes
"supported sensor types": this.supportedSensorTypes
.map((t) => getEnumMemberName(AlarmSensorType, t))
.join(", "),
},
Expand Down
Loading

0 comments on commit 02fced2

Please sign in to comment.