From 8e89879f009ea191ef060e961aec6046394124f1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 7 Apr 2026 20:39:26 +0200 Subject: [PATCH] feat(ocpp): add EncodingMethodEnumType enum for signed meter value encoding methods - Add EncodingMethodEnumType enum with OCMF and EDL values based on OCA Application Note and OCPP 2.0.1/2.1 specs - Use enum in SignedMeterData interface and wire types (with string union for extensibility) - Replace hardcoded 'OCMF' string constant with enum value - Update all tests to use EncodingMethodEnumType consistently --- .../ocpp/OCPPSignedMeterDataGenerator.ts | 7 ++++--- src/types/index.ts | 1 + src/types/ocpp/1.6/MeterValues.ts | 4 ++-- src/types/ocpp/2.0/MeterValues.ts | 4 ++-- src/types/ocpp/Configuration.ts | 5 +++++ .../ocpp/1.6/OCPP16SignedMeterValues.test.ts | 15 ++++++++------- .../ocpp/2.0/OCPP20SignedMeterValues.test.ts | 8 ++++++-- .../ocpp/OCPPSignedMeterDataGenerator.test.ts | 3 ++- tests/types/ocpp/1.6/MeterValues.test.ts | 5 +++-- 9 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/charging-station/ocpp/OCPPSignedMeterDataGenerator.ts b/src/charging-station/ocpp/OCPPSignedMeterDataGenerator.ts index 6474a25f..bb09b3df 100644 --- a/src/charging-station/ocpp/OCPPSignedMeterDataGenerator.ts +++ b/src/charging-station/ocpp/OCPPSignedMeterDataGenerator.ts @@ -1,6 +1,7 @@ import { createHash } from 'node:crypto' import { + EncodingMethodEnumType, type JsonObject, MeterValueContext, MeterValueUnit, @@ -9,7 +10,7 @@ import { import { roundTo } from '../../utils/index.js' export interface SignedMeterData extends JsonObject { - encodingMethod: string + encodingMethod: EncodingMethodEnumType publicKey: string signedMeterData: string signingMethod: SigningMethodEnumType @@ -25,7 +26,7 @@ export interface SignedMeterDataParams { } const DEFAULT_SIGNING_METHOD = SigningMethodEnumType.ECDSA_secp256r1_SHA256 -const ENCODING_METHOD = 'OCMF' +const DEFAULT_ENCODING_METHOD = EncodingMethodEnumType.OCMF const contextToTxCode = (context: MeterValueContext): string => { switch (context) { @@ -78,7 +79,7 @@ export const generateSignedMeterData = ( const ocmfString = `OCMF|${JSON.stringify(ocmfPayload)}|{"SA":"${resolvedSigningMethod}","SD":"${simulatedSignature}"}` return { - encodingMethod: ENCODING_METHOD, + encodingMethod: DEFAULT_ENCODING_METHOD, publicKey: publicKeyHex != null ? buildPublicKeyValue(publicKeyHex) : '', signedMeterData: Buffer.from(ocmfString).toString('base64'), signingMethod: resolvedSigningMethod, diff --git a/src/types/index.ts b/src/types/index.ts index f39ba634..4a7a17ec 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -339,6 +339,7 @@ export { type GenericResponse, GenericStatus, RegistrationStatusEnumType } from export { type ConfigurationKeyType, ConnectorPhaseRotation, + EncodingMethodEnumType, type OCPPConfigurationKey, PublicKeyWithSignedMeterValueEnumType, SigningMethodEnumType, diff --git a/src/types/ocpp/1.6/MeterValues.ts b/src/types/ocpp/1.6/MeterValues.ts index 021f9764..6bcd4b7f 100644 --- a/src/types/ocpp/1.6/MeterValues.ts +++ b/src/types/ocpp/1.6/MeterValues.ts @@ -1,7 +1,7 @@ import type { EmptyObject } from '../../EmptyObject.js' import type { JsonObject } from '../../JsonType.js' -import { type SigningMethodEnumType } from '../Configuration.js' +import { type EncodingMethodEnumType, type SigningMethodEnumType } from '../Configuration.js' export enum OCPP16MeterValueContext { INTERRUPTION_BEGIN = 'Interruption.Begin', @@ -108,7 +108,7 @@ export interface OCPP16SampledValue extends JsonObject { } export interface OCPP16SignedMeterValue extends JsonObject { - encodingMethod: string + encodingMethod: EncodingMethodEnumType | string publicKey: string signedMeterData: string signingMethod: '' | SigningMethodEnumType diff --git a/src/types/ocpp/2.0/MeterValues.ts b/src/types/ocpp/2.0/MeterValues.ts index fc34a546..33788545 100644 --- a/src/types/ocpp/2.0/MeterValues.ts +++ b/src/types/ocpp/2.0/MeterValues.ts @@ -2,7 +2,7 @@ import type { EmptyObject } from '../../EmptyObject.js' import type { JsonObject } from '../../JsonType.js' import type { CustomDataType, OCPP20UnitEnumType } from './Common.js' -import { type SigningMethodEnumType } from '../Configuration.js' +import { type EncodingMethodEnumType, type SigningMethodEnumType } from '../Configuration.js' export enum OCPP20LocationEnumType { Body = 'Body', @@ -91,7 +91,7 @@ export interface OCPP20SampledValue extends JsonObject { export interface OCPP20SignedMeterValue extends JsonObject { customData?: CustomDataType - encodingMethod: string // maxLength: 50 + encodingMethod: EncodingMethodEnumType | string // maxLength: 50 publicKey: string // Base64 encoded, maxLength: 2500 signedMeterData: string // Base64 encoded, maxLength: 2500 signingMethod: '' | SigningMethodEnumType // maxLength: 50 diff --git a/src/types/ocpp/Configuration.ts b/src/types/ocpp/Configuration.ts index 991e1fb0..202c9a72 100644 --- a/src/types/ocpp/Configuration.ts +++ b/src/types/ocpp/Configuration.ts @@ -22,6 +22,11 @@ export enum ConnectorPhaseRotation { Unknown = 'Unknown', } +export enum EncodingMethodEnumType { + EDL = 'EDL', + OCMF = 'OCMF', +} + export enum PublicKeyWithSignedMeterValueEnumType { EveryMeterValue = 'EveryMeterValue', Never = 'Never', diff --git a/tests/charging-station/ocpp/1.6/OCPP16SignedMeterValues.test.ts b/tests/charging-station/ocpp/1.6/OCPP16SignedMeterValues.test.ts index d58bf8aa..88a11438 100644 --- a/tests/charging-station/ocpp/1.6/OCPP16SignedMeterValues.test.ts +++ b/tests/charging-station/ocpp/1.6/OCPP16SignedMeterValues.test.ts @@ -13,6 +13,7 @@ import type { ChargingStation } from '../../../../src/charging-station/index.js' import { buildSignedOCPP16SampledValue } from '../../../../src/charging-station/ocpp/1.6/OCPP16RequestBuilders.js' import { OCPP16ServiceUtils } from '../../../../src/charging-station/ocpp/1.6/OCPP16ServiceUtils.js' import { + EncodingMethodEnumType, type OCPP16MeterValue, OCPP16MeterValueContext, OCPP16MeterValueFormat, @@ -39,7 +40,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => { await describe('buildSignedOCPP16SampledValue', async () => { await it('should return SampledValue with format=SignedData', () => { const signedData: OCPP16SignedMeterValue = { - encodingMethod: 'OCMF', + encodingMethod: EncodingMethodEnumType.OCMF, publicKey: '', signedMeterData: 'dGVzdA==', signingMethod: '', @@ -55,7 +56,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => { await it('should set measurand to Energy.Active.Import.Register', () => { const signedData: OCPP16SignedMeterValue = { - encodingMethod: 'OCMF', + encodingMethod: EncodingMethodEnumType.OCMF, publicKey: '', signedMeterData: 'dGVzdA==', signingMethod: '', @@ -71,7 +72,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => { await it('should set location to Outlet', () => { const signedData: OCPP16SignedMeterValue = { - encodingMethod: 'OCMF', + encodingMethod: EncodingMethodEnumType.OCMF, publicKey: '', signedMeterData: 'dGVzdA==', signingMethod: '', @@ -87,7 +88,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => { await it('should set value to JSON-serialized SignedMeterValue', () => { const signedData: OCPP16SignedMeterValue = { - encodingMethod: 'OCMF', + encodingMethod: EncodingMethodEnumType.OCMF, publicKey: 'abc123', signedMeterData: 'dGVzdA==', signingMethod: '', @@ -99,7 +100,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => { ) const parsed = JSON.parse(result.value) as OCPP16SignedMeterValue - assert.strictEqual(parsed.encodingMethod, 'OCMF') + assert.strictEqual(parsed.encodingMethod, EncodingMethodEnumType.OCMF) assert.strictEqual(parsed.signingMethod, '') assert.strictEqual(parsed.signedMeterData, 'dGVzdA==') assert.strictEqual(parsed.publicKey, 'abc123') @@ -107,7 +108,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => { await it('should use the provided context', () => { const signedData: OCPP16SignedMeterValue = { - encodingMethod: 'OCMF', + encodingMethod: EncodingMethodEnumType.OCMF, publicKey: '', signedMeterData: 'dGVzdA==', signingMethod: '', @@ -296,7 +297,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => { assert.strictEqual(typeof parsed.signingMethod, 'string') assert.strictEqual(typeof parsed.signedMeterData, 'string') assert.strictEqual(typeof parsed.publicKey, 'string') - assert.strictEqual(parsed.encodingMethod, 'OCMF') + assert.strictEqual(parsed.encodingMethod, EncodingMethodEnumType.OCMF) assert.strictEqual(parsed.signingMethod, SigningMethodEnumType.ECDSA_secp256r1_SHA256) }) }) diff --git a/tests/charging-station/ocpp/2.0/OCPP20SignedMeterValues.test.ts b/tests/charging-station/ocpp/2.0/OCPP20SignedMeterValues.test.ts index 7be581b7..0013ed6b 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20SignedMeterValues.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20SignedMeterValues.test.ts @@ -14,6 +14,7 @@ import { buildOCPP20SampledValue } from '../../../../src/charging-station/ocpp/2 import { buildMeterValue } from '../../../../src/charging-station/ocpp/OCPPServiceUtils.js' import { type SampledValueSigningConfig } from '../../../../src/charging-station/ocpp/OCPPSignedMeterValueUtils.js' import { + EncodingMethodEnumType, MeterValueMeasurand, OCPP20ComponentName, OCPP20ReadingContextEnumType, @@ -68,7 +69,7 @@ await describe('OCPP 2.0 Signed Meter Values', async () => { sampledValue.signedMeterValue.signingMethod, SigningMethodEnumType.ECDSA_secp256r1_SHA256 ) - assert.strictEqual(sampledValue.signedMeterValue.encodingMethod, 'OCMF') + assert.strictEqual(sampledValue.signedMeterValue.encodingMethod, EncodingMethodEnumType.OCMF) }) await it('should not add signedMeterValue when signing is disabled', () => { @@ -246,7 +247,10 @@ await describe('OCPP 2.0 Signed Meter Values', async () => { energySampledValue.signedMeterValue.signingMethod, SigningMethodEnumType.ECDSA_secp256r1_SHA256 ) - assert.strictEqual(energySampledValue.signedMeterValue.encodingMethod, 'OCMF') + assert.strictEqual( + energySampledValue.signedMeterValue.encodingMethod, + EncodingMethodEnumType.OCMF + ) }) await it('should not add signedMeterValue when SignReadings is not configured', () => { diff --git a/tests/charging-station/ocpp/OCPPSignedMeterDataGenerator.test.ts b/tests/charging-station/ocpp/OCPPSignedMeterDataGenerator.test.ts index aae72c44..d7c7852a 100644 --- a/tests/charging-station/ocpp/OCPPSignedMeterDataGenerator.test.ts +++ b/tests/charging-station/ocpp/OCPPSignedMeterDataGenerator.test.ts @@ -20,6 +20,7 @@ import { type SignedMeterDataParams, } from '../../../src/charging-station/ocpp/OCPPSignedMeterDataGenerator.js' import { + EncodingMethodEnumType, MeterValueContext, MeterValueUnit, SigningMethodEnumType, @@ -68,7 +69,7 @@ await describe('SignedMeterDataGenerator', async () => { await it('should set encodingMethod to OCMF', () => { const result = generateSignedMeterData(DEFAULT_PARAMS) - assert.strictEqual(result.encodingMethod, 'OCMF') + assert.strictEqual(result.encodingMethod, EncodingMethodEnumType.OCMF) }) await it('should return empty publicKey when no publicKeyHex provided', () => { diff --git a/tests/types/ocpp/1.6/MeterValues.test.ts b/tests/types/ocpp/1.6/MeterValues.test.ts index 34e3c70e..774ae39b 100644 --- a/tests/types/ocpp/1.6/MeterValues.test.ts +++ b/tests/types/ocpp/1.6/MeterValues.test.ts @@ -7,6 +7,7 @@ import assert from 'node:assert/strict' import { describe, it } from 'node:test' import { + EncodingMethodEnumType, OCPP16MeterValueFormat, type OCPP16SignedMeterValue, SigningMethodEnumType, @@ -26,12 +27,12 @@ await describe('OCPP 1.6 meter value types', async () => { await describe('OCPP16SignedMeterValue', async () => { await it('should compile as an interface with correct field names', () => { const signedMeterValue: OCPP16SignedMeterValue = { - encodingMethod: 'OCMF', + encodingMethod: EncodingMethodEnumType.OCMF, publicKey: 'b2NhOmJhc2UxNjphc24xOmZha2VrZXk=', // cspell:disable-line signedMeterData: 'T0NNRnx7fXxmYWtlc2lnbmF0dXJl', // cspell:disable-line signingMethod: SigningMethodEnumType.ECDSA_secp256r1_SHA256, } - assert.strictEqual(signedMeterValue.encodingMethod, 'OCMF') + assert.strictEqual(signedMeterValue.encodingMethod, EncodingMethodEnumType.OCMF) assert.strictEqual( signedMeterValue.signingMethod, SigningMethodEnumType.ECDSA_secp256r1_SHA256 -- 2.43.0