Skip to content

Commit

Permalink
feat: add mock implementation for Meter CC
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Jun 12, 2024
1 parent 35b4920 commit ea92e38
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/testing/src/CCSpecificCapabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,28 @@ export interface NotificationCCCapabilities {
notificationTypesAndEvents: Record<number, number[]>;
}

export interface MeterCCCapabilities {
meterType: number;
supportedScales: number[];
supportedRateTypes: number[];
supportsReset: boolean;
getValue?: (
scale: number,
rateType: number,
) => number | {
value: number;
deltaTime: number;
prevValue?: number;
} | undefined;
onReset?: (
options?: {
scale: number;
rateType: number;
targetValue: number;
},
) => void;
}

export interface MultilevelSensorCCCapabilities {
sensors: Record<number, {
supportedScales: number[];
Expand Down Expand Up @@ -123,6 +145,7 @@ export type CCSpecificCapabilities = {
[67 /* Thermostat Setpoint */]: ThermostatSetpointCCCapabilities;
[99 /* User Code */]: UserCodeCCCapabilities;
[78 /* Schedule Entry Lock */]: ScheduleEntryLockCCCapabilities;
[CommandClasses.Meter]: MeterCCCapabilities;
};

export type CCIdToCapabilities<T extends CommandClasses = CommandClasses> =
Expand Down
2 changes: 2 additions & 0 deletions packages/zwave-js/src/lib/node/MockNodeBehaviors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { BasicCCBehaviors } from "./mockCCBehaviors/Basic";
import { ConfigurationCCBehaviors } from "./mockCCBehaviors/Configuration";
import { EnergyProductionCCBehaviors } from "./mockCCBehaviors/EnergyProduction";
import { ManufacturerSpecificCCBehaviors } from "./mockCCBehaviors/ManufacturerSpecific";
import { MeterCCBehaviors } from "./mockCCBehaviors/Meter";
import { MultilevelSensorCCBehaviors } from "./mockCCBehaviors/MultilevelSensor";
import { NotificationCCBehaviors } from "./mockCCBehaviors/Notification";
import { ScheduleEntryLockCCBehaviors } from "./mockCCBehaviors/ScheduleEntryLock";
Expand Down Expand Up @@ -294,6 +295,7 @@ export function createDefaultBehaviors(): MockNodeBehavior[] {
...ConfigurationCCBehaviors,
...EnergyProductionCCBehaviors,
...ManufacturerSpecificCCBehaviors,
...MeterCCBehaviors,
...MultilevelSensorCCBehaviors,
...NotificationCCBehaviors,
...ScheduleEntryLockCCBehaviors,
Expand Down
143 changes: 143 additions & 0 deletions packages/zwave-js/src/lib/node/mockCCBehaviors/Meter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {
MeterCCGet,
MeterCCReport,
MeterCCReset,
MeterCCSupportedGet,
MeterCCSupportedReport,
RateType,
} from "@zwave-js/cc";
import { CommandClasses } from "@zwave-js/core";
import {
type MeterCCCapabilities,
type MockNodeBehavior,
MockZWaveFrameType,
createMockZWaveRequestFrame,
} from "@zwave-js/testing";

export const defaultCapabilities: MeterCCCapabilities = {
meterType: 0x01, // Electric
supportedScales: [0x00], // kWh
supportedRateTypes: [RateType.Consumed],
supportsReset: true,
};

const respondToMeterSupportedGet: MockNodeBehavior = {
async onControllerFrame(controller, self, frame) {
if (
frame.type === MockZWaveFrameType.Request
&& frame.payload instanceof MeterCCSupportedGet
) {
const capabilities = {
...defaultCapabilities,
...self.getCCCapabilities(
CommandClasses.Meter,
frame.payload.endpointIndex,
),
};
const cc = new MeterCCSupportedReport(self.host, {
nodeId: controller.host.ownNodeId,
type: capabilities.meterType,
supportedScales: capabilities.supportedScales,
supportedRateTypes: capabilities.supportedRateTypes,
supportsReset: capabilities.supportsReset,
});
await self.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
return true;
}
return false;
},
};

const respondToMeterGet: MockNodeBehavior = {
async onControllerFrame(controller, self, frame) {
if (
frame.type === MockZWaveFrameType.Request
&& frame.payload instanceof MeterCCGet
) {
const capabilities = {
...defaultCapabilities,
...self.getCCCapabilities(
CommandClasses.Meter,
frame.payload.endpointIndex,
),
};
const scale = frame.payload.scale
?? capabilities.supportedScales[0];
const rateType = frame.payload.rateType
?? capabilities.supportedRateTypes[0]
?? RateType.Consumed;

const value = capabilities.getValue?.(scale, rateType) ?? {
value: 0,
deltaTime: 0,
};
const normalizedValue = typeof value === "number"
? {
value,
deltaTime: 0,
}
: value;

const cc = new MeterCCReport(self.host, {
nodeId: controller.host.ownNodeId,
type: capabilities.meterType,
scale,
rateType,
...normalizedValue,
});
await self.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
return true;
}
return false;
},
};

const respondToMeterReset: MockNodeBehavior = {
onControllerFrame(controller, self, frame) {
if (
frame.type === MockZWaveFrameType.Request
&& frame.payload instanceof MeterCCReset
) {
const capabilities = {
...defaultCapabilities,
...self.getCCCapabilities(
CommandClasses.Meter,
frame.payload.endpointIndex,
),
};

const cc = frame.payload;
if (
cc.type != undefined
&& cc.scale != undefined
&& cc.rateType != undefined
&& cc.targetValue != undefined
) {
capabilities.onReset?.({
scale: cc.scale,
rateType: cc.rateType,
targetValue: cc.targetValue,
});
} else {
capabilities.onReset?.();
}

return true;
}
return false;
},
};

export const MeterCCBehaviors = [
respondToMeterSupportedGet,
respondToMeterGet,
respondToMeterReset,
];

0 comments on commit ea92e38

Please sign in to comment.