]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
feat: enrich OCPP 2.0 template with OCPP 1.6 equivalent config keys
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Mar 2026 12:46:34 +0000 (13:46 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Mar 2026 12:47:08 +0000 (13:47 +0100)
Add LocalAuthListCtrlr.Enabled, ReservationCtrlr.Enabled/NonEvseSpecific,
TxCtrlr.EVConnectionTimeOut, AuthCtrlr.LocalAuthorizationOffline to
keba-ocpp2 template — previously impossible due to key collisions.

- Add LocalAuthListEnabled → LocalAuthListCtrlr.Enabled key remapping
- Add ReserveConnectorZeroSupported → ReservationCtrlr.NonEvseSpecific remapping
- Remove AuthCtrlr.Enabled from template (no application consumer)
- Add NonEvseSpecific, MaxEnergyOnInvalidId to OCPP20OptionalVariableName enum
- Replace string literals with enum refs in registry and service utils
- Add tests for the 2 new key remappings

src/assets/station-templates/keba-ocpp2.station-template.json
src/charging-station/ConfigurationKeyUtils.ts
src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts
src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts
src/types/ocpp/2.0/Variables.ts
tests/charging-station/ConfigurationKeyUtils.test.ts

index 351d611725a159f1a71335710666f28bddd99321..5ff0561a43b443aaf0f7d81aa3a4f376e1d7eb55 100644 (file)
   "Configuration": {
     "configurationKey": [
       {
-        "key": "SampledDataCtrlr.TxUpdatedMeasurands",
+        "key": "AuthCtrlr.AuthorizeRemoteStart",
         "readonly": false,
-        "value": "Energy.Active.Import.Register,Power.Active.Import,Current.Import,Voltage"
+        "value": "false"
       },
       {
-        "key": "SampledDataCtrlr.TxUpdatedInterval",
+        "key": "AuthCtrlr.LocalAuthorizationOffline",
         "readonly": false,
-        "value": "30"
+        "value": "true"
       },
       {
-        "key": "AuthCtrlr.AuthorizeRemoteStart",
+        "key": "AuthCtrlr.LocalPreAuthorization",
         "readonly": false,
         "value": "false"
       },
       {
-        "key": "AuthCtrlr.LocalPreAuthorization",
+        "key": "AuthCtrlr.OfflineTxForUnknownIdEnabled",
         "readonly": false,
         "value": "false"
       },
       {
-        "key": "AuthCtrlr.OfflineTxForUnknownIdEnabled",
+        "key": "ChargingStation.WebSocketPingInterval",
+        "readonly": false,
+        "value": "60"
+      },
+      {
+        "key": "LocalAuthListCtrlr.Enabled",
         "readonly": false,
         "value": "false"
       },
+      {
+        "key": "OCPPCommCtrlr.MessageAttemptInterval.TransactionEvent",
+        "readonly": false,
+        "value": "20"
+      },
       {
         "key": "OCPPCommCtrlr.MessageAttempts.TransactionEvent",
         "readonly": false,
         "value": "3"
       },
       {
-        "key": "OCPPCommCtrlr.MessageAttemptInterval.TransactionEvent",
+        "key": "ReservationCtrlr.Enabled",
         "readonly": false,
-        "value": "20"
+        "value": "false"
       },
       {
-        "key": "ChargingStation.WebSocketPingInterval",
+        "key": "ReservationCtrlr.NonEvseSpecific",
         "readonly": false,
-        "value": "60"
+        "value": "false"
+      },
+      {
+        "key": "SampledDataCtrlr.TxUpdatedInterval",
+        "readonly": false,
+        "value": "30"
+      },
+      {
+        "key": "SampledDataCtrlr.TxUpdatedMeasurands",
+        "readonly": false,
+        "value": "Energy.Active.Import.Register,Power.Active.Import,Current.Import,Voltage"
+      },
+      {
+        "key": "TxCtrlr.EVConnectionTimeOut",
+        "readonly": false,
+        "value": "180"
       }
     ]
   },
index 891255a2aa872cb546409ebaacdf6c89f633d937..79b57ec49759134ca270fbc030a52c1e0b395389 100644 (file)
@@ -38,6 +38,10 @@ const OCPP2_PARAMETER_KEY_MAP = new Map<
               StandardParametersKey.LocalAuthorizationOffline
             ),
           ],
+          [
+            StandardParametersKey.LocalAuthListEnabled,
+            buildConfigKey(OCPP20ComponentName.LocalAuthListCtrlr, StandardParametersKey.Enabled),
+          ],
           [
             StandardParametersKey.LocalPreAuthorize,
             buildConfigKey(OCPP20ComponentName.AuthCtrlr, StandardParametersKey.LocalPreAuthorization),
@@ -56,6 +60,10 @@ const OCPP2_PARAMETER_KEY_MAP = new Map<
               StandardParametersKey.TxUpdatedMeasurands
             ),
           ],
+          [
+            StandardParametersKey.ReserveConnectorZeroSupported,
+            buildConfigKey(OCPP20ComponentName.ReservationCtrlr, StandardParametersKey.NonEvseSpecific),
+          ],
         ] as [ConfigurationKeyType, ConfigurationKeyType][]
       ).map(([from, to]) => [
         from,
index 7cd6adc7138cf2df27bcfaaaba245df384dac744..aa778e4377ddbb2fb972c401fd7797cba57b1956 100644 (file)
@@ -12,6 +12,7 @@ import {
   OCPP20IncomingRequestCommand,
   OCPP20MeasurandEnumType,
   type OCPP20MeterValue,
+  OCPP20OptionalVariableName,
   OCPP20ReadingContextEnumType,
   OCPP20ReasonEnumType,
   OCPP20RequestCommand,
@@ -24,6 +25,7 @@ import {
   type OCPP20TransactionType,
   OCPP20TriggerReasonEnumType,
   OCPPVersion,
+  ReasonCodeEnumType,
   type UUIDv4,
 } from '../../../types/index.js'
 import {
@@ -195,7 +197,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
       const results = data.map(d =>
         buildRejected(d, {
           info: `ItemsPerMessage limit ${itemsLimit.toString()} exceeded (${data.length.toString()} requested)`,
-          reasonCode: 'TooManyElements',
+          reasonCode: ReasonCodeEnumType.TooManyElements as string,
         })
       )
       logger.debug(
@@ -209,7 +211,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
         const results = data.map(d =>
           buildRejected(d, {
             info: `BytesPerMessage limit ${bytesLimit.toString()} exceeded (estimated ${estimatedSize.toString()} bytes)`,
-            reasonCode: 'TooLargeElement',
+            reasonCode: ReasonCodeEnumType.TooLargeElement as string,
           })
         )
         logger.debug(
@@ -240,7 +242,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
           const results = originalData.map(d =>
             buildRejected(d, {
               info: `BytesPerMessage limit ${bytesLimit.toString()} exceeded (actual ${actualSize.toString()} bytes)`,
-              reasonCode: 'TooLargeElement',
+              reasonCode: ReasonCodeEnumType.TooLargeElement as string,
             })
           )
           logger.debug(
@@ -351,7 +353,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
     const maxEnergyOnInvalidId = OCPP20ServiceUtils.readVariableAsInteger(
       chargingStation,
       OCPP20ComponentName.TxCtrlr,
-      'MaxEnergyOnInvalidId',
+      OCPP20OptionalVariableName.MaxEnergyOnInvalidId,
       0
     )
 
@@ -589,7 +591,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
           const maxEnergy = OCPP20ServiceUtils.readVariableAsInteger(
             chargingStation,
             OCPP20ComponentName.TxCtrlr,
-            'MaxEnergyOnInvalidId',
+            OCPP20OptionalVariableName.MaxEnergyOnInvalidId,
             0
           )
           const currentEnergy = connectorStatus.transactionEnergyActiveImportRegisterValue ?? 0
index 9a19930d051ec53ab8393c78168f6f2ca9089ac3..3a70bb4ef14c9518543039cc250a01afa0cc19a3 100644 (file)
@@ -9,9 +9,11 @@ import {
   OCPP20ChargingRateUnitEnumType,
   OCPP20ComponentName,
   OCPP20DeviceInfoVariableName,
+  OCPP20IncomingRequestCommand,
   OCPP20MeasurandEnumType,
   OCPP20OperationalStatusEnumType,
   OCPP20OptionalVariableName,
+  OCPP20RequestCommand,
   OCPP20RequiredVariableName,
   OCPP20UnitEnumType,
   OCPP20VendorVariableName,
@@ -121,20 +123,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'Available',
   },
-  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'Interval')]: {
-    component: OCPP20ComponentName.AlignedDataCtrlr as string,
-    dataType: DataEnumType.integer,
-    defaultValue: '900',
-    description:
-      'Size (in seconds) of the clock-aligned data interval, intended to be transmitted in the MeterValuesRequest message.',
-    min: 1,
-    mutability: MutabilityEnumType.ReadWrite,
-    persistence: PersistenceEnumType.Persistent,
-    required: true,
-    supportedAttributes: [AttributeEnumType.Actual],
-    unit: OCPP20UnitEnumType.SECONDS,
-    variable: 'Interval',
-  },
   [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'Measurands')]: {
     component: OCPP20ComponentName.AlignedDataCtrlr as string,
     dataType: DataEnumType.MemberList,
@@ -205,7 +193,40 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     unit: OCPP20UnitEnumType.SECONDS,
     variable: 'TxEndedInterval',
   },
-  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'TxEndedMeasurands')]: {
+  [buildRegistryKey(
+    OCPP20ComponentName.AlignedDataCtrlr as string,
+    OCPP20RequiredVariableName.AlignedDataInterval as string
+  )]: {
+    component: OCPP20ComponentName.AlignedDataCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '900',
+    description:
+      'Size (in seconds) of the clock-aligned data interval, intended to be transmitted in the MeterValuesRequest message.',
+    min: 1,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    required: true,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: OCPP20UnitEnumType.SECONDS,
+    variable: OCPP20RequiredVariableName.AlignedDataInterval as string,
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.AlignedDataCtrlr as string,
+    OCPP20RequiredVariableName.Enabled as string
+  )]: {
+    component: OCPP20ComponentName.AlignedDataCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'If this variable reports a value of true, Clock-Aligned Data is enabled',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: OCPP20RequiredVariableName.Enabled as string,
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.AlignedDataCtrlr as string,
+    OCPP20RequiredVariableName.TxEndedMeasurands as string
+  )]: {
     component: OCPP20ComponentName.AlignedDataCtrlr as string,
     dataType: DataEnumType.MemberList,
     defaultValue: `${OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER},${OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL},${OCPP20MeasurandEnumType.VOLTAGE}`,
@@ -239,19 +260,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: OCPP20RequiredVariableName.TxEndedMeasurands,
   },
-  [buildRegistryKey(
-    OCPP20ComponentName.AlignedDataCtrlr as string,
-    OCPP20RequiredVariableName.Enabled as string
-  )]: {
-    component: OCPP20ComponentName.AlignedDataCtrlr as string,
-    dataType: DataEnumType.boolean,
-    defaultValue: 'false',
-    description: 'If this variable reports a value of true, Clock-Aligned Data is enabled',
-    mutability: MutabilityEnumType.ReadWrite,
-    persistence: PersistenceEnumType.Persistent,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: OCPP20RequiredVariableName.Enabled as string,
-  },
 
   // AuthCacheCtrlr Component
   [buildRegistryKey(OCPP20ComponentName.AuthCacheCtrlr as string, 'Available')]: {
@@ -532,16 +540,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'Available',
   },
-  [buildRegistryKey(OCPP20ComponentName.ChargingStation as string, 'Model')]: {
-    component: OCPP20ComponentName.ChargingStation as string,
-    dataType: DataEnumType.string,
-    description: 'Charging station model as reported in BootNotification.',
-    maxLength: 50,
-    mutability: MutabilityEnumType.ReadOnly,
-    persistence: PersistenceEnumType.Persistent,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: OCPP20DeviceInfoVariableName.Model,
-  },
   [buildRegistryKey(OCPP20ComponentName.ChargingStation as string, 'SupplyPhases')]: {
     component: OCPP20ComponentName.ChargingStation as string,
     dataType: DataEnumType.integer,
@@ -555,16 +553,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'SupplyPhases',
   },
-  [buildRegistryKey(OCPP20ComponentName.ChargingStation as string, 'VendorName')]: {
-    component: OCPP20ComponentName.ChargingStation as string,
-    dataType: DataEnumType.string,
-    description: 'Charging station vendor name as reported in BootNotification.',
-    maxLength: 50,
-    mutability: MutabilityEnumType.ReadOnly,
-    persistence: PersistenceEnumType.Persistent,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: OCPP20DeviceInfoVariableName.VendorName,
-  },
   [buildRegistryKey(
     OCPP20ComponentName.ChargingStation as string,
     OCPP20DeviceInfoVariableName.AvailabilityState
@@ -583,6 +571,32 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: OCPP20DeviceInfoVariableName.AvailabilityState as string,
   },
+  [buildRegistryKey(
+    OCPP20ComponentName.ChargingStation as string,
+    OCPP20DeviceInfoVariableName.Model as string
+  )]: {
+    component: OCPP20ComponentName.ChargingStation as string,
+    dataType: DataEnumType.string,
+    description: 'Charging station model as reported in BootNotification.',
+    maxLength: 50,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: OCPP20DeviceInfoVariableName.Model,
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.ChargingStation as string,
+    OCPP20DeviceInfoVariableName.VendorName as string
+  )]: {
+    component: OCPP20ComponentName.ChargingStation as string,
+    dataType: DataEnumType.string,
+    description: 'Charging station vendor name as reported in BootNotification.',
+    maxLength: 50,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: OCPP20DeviceInfoVariableName.VendorName,
+  },
   [buildRegistryKey(
     OCPP20ComponentName.ChargingStation as string,
     OCPP20OptionalVariableName.WebSocketPingInterval
@@ -756,13 +770,13 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   [buildRegistryKey(
     OCPP20ComponentName.DeviceDataCtrlr as string,
     OCPP20RequiredVariableName.BytesPerMessage,
-    'GetVariables'
+    OCPP20IncomingRequestCommand.GET_VARIABLES as string
   )]: {
     component: OCPP20ComponentName.DeviceDataCtrlr as string,
     dataType: DataEnumType.integer,
     defaultValue: '8192',
     description: 'Maximum number of bytes in a GetVariables message.',
-    instance: 'GetVariables',
+    instance: OCPP20IncomingRequestCommand.GET_VARIABLES as string,
     max: 65535,
     min: 1,
     mutability: MutabilityEnumType.ReadOnly,
@@ -775,13 +789,13 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   [buildRegistryKey(
     OCPP20ComponentName.DeviceDataCtrlr as string,
     OCPP20RequiredVariableName.BytesPerMessage,
-    'SetVariables'
+    OCPP20IncomingRequestCommand.SET_VARIABLES as string
   )]: {
     component: OCPP20ComponentName.DeviceDataCtrlr as string,
     dataType: DataEnumType.integer,
     defaultValue: '8192',
     description: 'Maximum number of bytes in a SetVariables message.',
-    instance: 'SetVariables',
+    instance: OCPP20IncomingRequestCommand.SET_VARIABLES as string,
     max: 65535,
     min: 1,
     mutability: MutabilityEnumType.ReadOnly,
@@ -832,13 +846,13 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   [buildRegistryKey(
     OCPP20ComponentName.DeviceDataCtrlr as string,
     OCPP20RequiredVariableName.ItemsPerMessage,
-    'GetVariables'
+    OCPP20IncomingRequestCommand.GET_VARIABLES as string
   )]: {
     component: OCPP20ComponentName.DeviceDataCtrlr as string,
     dataType: DataEnumType.integer,
     defaultValue: '32',
     description: 'Maximum ComponentVariable entries in a GetVariables message.',
-    instance: 'GetVariables',
+    instance: OCPP20IncomingRequestCommand.GET_VARIABLES as string,
     max: 256,
     min: 1,
     mutability: MutabilityEnumType.ReadOnly,
@@ -851,13 +865,13 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   [buildRegistryKey(
     OCPP20ComponentName.DeviceDataCtrlr as string,
     OCPP20RequiredVariableName.ItemsPerMessage,
-    'SetVariables'
+    OCPP20IncomingRequestCommand.SET_VARIABLES as string
   )]: {
     component: OCPP20ComponentName.DeviceDataCtrlr as string,
     dataType: DataEnumType.integer,
     defaultValue: '32',
     description: 'Maximum ComponentVariable entries in a SetVariables message.',
-    instance: 'SetVariables',
+    instance: OCPP20IncomingRequestCommand.SET_VARIABLES as string,
     max: 256,
     min: 1,
     mutability: MutabilityEnumType.ReadOnly,
@@ -915,21 +929,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'AllowReset',
   },
-  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'AvailabilityState')]: {
-    component: OCPP20ComponentName.EVSE as string,
-    dataType: DataEnumType.OptionList,
-    defaultValue: OCPP20OperationalStatusEnumType.Operative,
-    description: 'This variable reports current availability state for the EVSE',
-    enumeration: [
-      OCPP20OperationalStatusEnumType.Operative,
-      OCPP20OperationalStatusEnumType.Inoperative,
-    ],
-    mutability: MutabilityEnumType.ReadOnly,
-    persistence: PersistenceEnumType.Volatile,
-    required: true,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: OCPP20DeviceInfoVariableName.AvailabilityState,
-  },
   [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'Available')]: {
     component: OCPP20ComponentName.EVSE as string,
     dataType: DataEnumType.boolean,
@@ -992,6 +991,24 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'SupplyPhases',
   },
+  [buildRegistryKey(
+    OCPP20ComponentName.EVSE as string,
+    OCPP20DeviceInfoVariableName.AvailabilityState as string
+  )]: {
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.OptionList,
+    defaultValue: OCPP20OperationalStatusEnumType.Operative,
+    description: 'This variable reports current availability state for the EVSE',
+    enumeration: [
+      OCPP20OperationalStatusEnumType.Operative,
+      OCPP20OperationalStatusEnumType.Inoperative,
+    ],
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    required: true,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: OCPP20DeviceInfoVariableName.AvailabilityState,
+  },
 
   // FirmwareCtrlr Component
   [buildRegistryKey(
@@ -1149,18 +1166,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'Available',
   },
-  [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'BytesPerMessage')]: {
-    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
-    dataType: DataEnumType.integer,
-    defaultValue: '8192',
-    description: 'Maximum number of bytes in a SendLocalList message.',
-    min: 1,
-    mutability: MutabilityEnumType.ReadOnly,
-    persistence: PersistenceEnumType.Persistent,
-    required: true,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: OCPP20RequiredVariableName.BytesPerMessage,
-  },
   [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'DisablePostAuthorize')]: {
     component: OCPP20ComponentName.LocalAuthListCtrlr as string,
     dataType: DataEnumType.boolean,
@@ -1184,18 +1189,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'Entries',
   },
-  [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'ItemsPerMessage')]: {
-    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
-    dataType: DataEnumType.integer,
-    defaultValue: '100',
-    description: 'Maximum number of records in SendLocalList',
-    min: 1,
-    mutability: MutabilityEnumType.ReadOnly,
-    persistence: PersistenceEnumType.Persistent,
-    required: true,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: OCPP20RequiredVariableName.ItemsPerMessage,
-  },
   [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'Storage')]: {
     characteristics: {
       maxLimit: 1048576, // 1MB default
@@ -1212,6 +1205,21 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     unit: OCPP20UnitEnumType.BYTES,
     variable: 'Storage',
   },
+  [buildRegistryKey(
+    OCPP20ComponentName.LocalAuthListCtrlr as string,
+    OCPP20RequiredVariableName.BytesPerMessage as string
+  )]: {
+    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '8192',
+    description: 'Maximum number of bytes in a SendLocalList message.',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    required: true,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: OCPP20RequiredVariableName.BytesPerMessage,
+  },
   [buildRegistryKey(
     OCPP20ComponentName.LocalAuthListCtrlr as string,
     OCPP20RequiredVariableName.Enabled as string
@@ -1226,6 +1234,21 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: OCPP20RequiredVariableName.Enabled as string,
   },
+  [buildRegistryKey(
+    OCPP20ComponentName.LocalAuthListCtrlr as string,
+    OCPP20RequiredVariableName.ItemsPerMessage as string
+  )]: {
+    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '100',
+    description: 'Maximum number of records in SendLocalList',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    required: true,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: OCPP20RequiredVariableName.ItemsPerMessage,
+  },
 
   // MonitoringCtrlr Component
   [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'ActiveMonitoringBase')]: {
@@ -1263,9 +1286,45 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'Available',
   },
+  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'MonitoringBase')]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.OptionList,
+    defaultValue: 'All',
+    description: 'Currently used monitoring base (readonly)',
+    enumeration: ['All', 'FactoryDefault', 'HardwiredOnly'],
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'MonitoringBase',
+  },
+  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'MonitoringLevel')]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '9',
+    description: 'Currently used monitoring level (readonly)',
+    max: 9,
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'MonitoringLevel',
+  },
+  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'OfflineQueuingSeverity')]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '5',
+    description:
+      'When set and the Charging Station is offline, the Charging Station shall queue any notifyEventRequest messages triggered by a monitor with a severity number equal to or lower than the severity configured here.',
+    max: 9,
+    min: 0,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'OfflineQueuingSeverity',
+  },
   [buildRegistryKey(
     OCPP20ComponentName.MonitoringCtrlr as string,
-    'BytesPerMessage',
+    OCPP20RequiredVariableName.BytesPerMessage as string,
     'ClearVariableMonitoring'
   )]: {
     component: OCPP20ComponentName.MonitoringCtrlr as string,
@@ -1281,7 +1340,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   },
   [buildRegistryKey(
     OCPP20ComponentName.MonitoringCtrlr as string,
-    'BytesPerMessage',
+    OCPP20RequiredVariableName.BytesPerMessage as string,
     'SetVariableMonitoring'
   )]: {
     component: OCPP20ComponentName.MonitoringCtrlr as string,
@@ -1298,7 +1357,20 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   },
   [buildRegistryKey(
     OCPP20ComponentName.MonitoringCtrlr as string,
-    'ItemsPerMessage',
+    OCPP20RequiredVariableName.Enabled as string
+  )]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Whether monitoring is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: OCPP20RequiredVariableName.Enabled as string,
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.MonitoringCtrlr as string,
+    OCPP20RequiredVariableName.ItemsPerMessage as string,
     'ClearVariableMonitoring'
   )]: {
     component: OCPP20ComponentName.MonitoringCtrlr as string,
@@ -1314,7 +1386,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   },
   [buildRegistryKey(
     OCPP20ComponentName.MonitoringCtrlr as string,
-    'ItemsPerMessage',
+    OCPP20RequiredVariableName.ItemsPerMessage as string,
     'SetVariableMonitoring'
   )]: {
     component: OCPP20ComponentName.MonitoringCtrlr as string,
@@ -1330,55 +1402,6 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: OCPP20RequiredVariableName.ItemsPerMessage,
   },
-  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'MonitoringBase')]: {
-    component: OCPP20ComponentName.MonitoringCtrlr as string,
-    dataType: DataEnumType.OptionList,
-    defaultValue: 'All',
-    description: 'Currently used monitoring base (readonly)',
-    enumeration: ['All', 'FactoryDefault', 'HardwiredOnly'],
-    mutability: MutabilityEnumType.ReadOnly,
-    persistence: PersistenceEnumType.Volatile,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: 'MonitoringBase',
-  },
-  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'MonitoringLevel')]: {
-    component: OCPP20ComponentName.MonitoringCtrlr as string,
-    dataType: DataEnumType.integer,
-    defaultValue: '9',
-    description: 'Currently used monitoring level (readonly)',
-    max: 9,
-    min: 0,
-    mutability: MutabilityEnumType.ReadOnly,
-    persistence: PersistenceEnumType.Volatile,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: 'MonitoringLevel',
-  },
-  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'OfflineQueuingSeverity')]: {
-    component: OCPP20ComponentName.MonitoringCtrlr as string,
-    dataType: DataEnumType.integer,
-    defaultValue: '5',
-    description:
-      'When set and the Charging Station is offline, the Charging Station shall queue any notifyEventRequest messages triggered by a monitor with a severity number equal to or lower than the severity configured here.',
-    max: 9,
-    min: 0,
-    mutability: MutabilityEnumType.ReadWrite,
-    persistence: PersistenceEnumType.Persistent,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: 'OfflineQueuingSeverity',
-  },
-  [buildRegistryKey(
-    OCPP20ComponentName.MonitoringCtrlr as string,
-    OCPP20RequiredVariableName.Enabled as string
-  )]: {
-    component: OCPP20ComponentName.MonitoringCtrlr as string,
-    dataType: DataEnumType.boolean,
-    defaultValue: 'true',
-    description: 'Whether monitoring is enabled.',
-    mutability: MutabilityEnumType.ReadWrite,
-    persistence: PersistenceEnumType.Persistent,
-    supportedAttributes: [AttributeEnumType.Actual],
-    variable: OCPP20RequiredVariableName.Enabled as string,
-  },
 
   // OCPPCommCtrlr Component
   [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'ActiveNetworkProfile')]: {
@@ -1506,13 +1529,13 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   [buildRegistryKey(
     OCPP20ComponentName.OCPPCommCtrlr as string,
     OCPP20RequiredVariableName.MessageAttemptInterval,
-    'TransactionEvent'
+    OCPP20RequestCommand.TRANSACTION_EVENT as string
   )]: {
     component: OCPP20ComponentName.OCPPCommCtrlr as string,
     dataType: DataEnumType.integer,
     defaultValue: '5',
     description: 'Interval (seconds) between retry attempts for TransactionEvent messages.',
-    instance: 'TransactionEvent',
+    instance: OCPP20RequestCommand.TRANSACTION_EVENT as string,
     max: 3600,
     min: 1,
     mutability: MutabilityEnumType.ReadWrite,
@@ -1526,13 +1549,13 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
   [buildRegistryKey(
     OCPP20ComponentName.OCPPCommCtrlr as string,
     OCPP20RequiredVariableName.MessageAttempts,
-    'TransactionEvent'
+    OCPP20RequestCommand.TRANSACTION_EVENT as string
   )]: {
     component: OCPP20ComponentName.OCPPCommCtrlr as string,
     dataType: DataEnumType.integer,
     defaultValue: '3',
     description: 'Maximum number of TransactionEvent message attempts after initial send.',
-    instance: 'TransactionEvent',
+    instance: OCPP20RequestCommand.TRANSACTION_EVENT as string,
     max: 10,
     min: 1,
     mutability: MutabilityEnumType.ReadWrite,
@@ -1654,7 +1677,10 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'Available',
   },
-  [buildRegistryKey(OCPP20ComponentName.ReservationCtrlr as string, 'NonEvseSpecific')]: {
+  [buildRegistryKey(
+    OCPP20ComponentName.ReservationCtrlr as string,
+    OCPP20OptionalVariableName.NonEvseSpecific as string
+  )]: {
     component: OCPP20ComponentName.ReservationCtrlr as string,
     dataType: DataEnumType.boolean,
     defaultValue: 'false',
@@ -1663,7 +1689,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     mutability: MutabilityEnumType.ReadOnly,
     persistence: PersistenceEnumType.Persistent,
     supportedAttributes: [AttributeEnumType.Actual],
-    variable: 'NonEvseSpecific',
+    variable: OCPP20OptionalVariableName.NonEvseSpecific as string,
   },
   [buildRegistryKey(
     OCPP20ComponentName.ReservationCtrlr as string,
@@ -2299,28 +2325,31 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     unit: OCPP20UnitEnumType.SECONDS,
     variable: 'ChargingTime',
   },
-  [buildRegistryKey(OCPP20ComponentName.TxCtrlr as string, 'MaxEnergyOnInvalidId')]: {
+  [buildRegistryKey(OCPP20ComponentName.TxCtrlr as string, 'TxBeforeAcceptedEnabled')]: {
     component: OCPP20ComponentName.TxCtrlr as string,
-    dataType: DataEnumType.integer,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
     description:
-      'Maximum amount of energy in Wh delivered when an identifier is deauthorized by the CSMS after start of a transaction.',
-    min: 0,
+      'Allow charging before having received a BootNotificationResponse with RegistrationStatus: Accepted.',
     mutability: MutabilityEnumType.ReadWrite,
     persistence: PersistenceEnumType.Persistent,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: OCPP20UnitEnumType.WATT_HOUR,
-    variable: 'MaxEnergyOnInvalidId',
+    variable: 'TxBeforeAcceptedEnabled',
   },
-  [buildRegistryKey(OCPP20ComponentName.TxCtrlr as string, 'TxBeforeAcceptedEnabled')]: {
+  [buildRegistryKey(
+    OCPP20ComponentName.TxCtrlr as string,
+    OCPP20OptionalVariableName.MaxEnergyOnInvalidId as string
+  )]: {
     component: OCPP20ComponentName.TxCtrlr as string,
-    dataType: DataEnumType.boolean,
-    defaultValue: 'false',
+    dataType: DataEnumType.integer,
     description:
-      'Allow charging before having received a BootNotificationResponse with RegistrationStatus: Accepted.',
+      'Maximum amount of energy in Wh delivered when an identifier is deauthorized by the CSMS after start of a transaction.',
+    min: 0,
     mutability: MutabilityEnumType.ReadWrite,
     persistence: PersistenceEnumType.Persistent,
     supportedAttributes: [AttributeEnumType.Actual],
-    variable: 'TxBeforeAcceptedEnabled',
+    unit: OCPP20UnitEnumType.WATT_HOUR,
+    variable: OCPP20OptionalVariableName.MaxEnergyOnInvalidId as string,
   },
   [buildRegistryKey(
     OCPP20ComponentName.TxCtrlr as string,
index ee419459a32fecd6ac061364ae1aa99bdeaf2857..a7e0651c66353de5a901ee1fe1464ec2e833a013 100644 (file)
@@ -35,6 +35,8 @@ export enum OCPP20DeviceInfoVariableName {
 export enum OCPP20OptionalVariableName {
   HeartbeatInterval = 'HeartbeatInterval',
   MaxCertificateChainSize = 'MaxCertificateChainSize',
+  MaxEnergyOnInvalidId = 'MaxEnergyOnInvalidId',
+  NonEvseSpecific = 'NonEvseSpecific',
   WebSocketPingInterval = 'WebSocketPingInterval',
 }
 
index e9aca27eca5ca77d5e891bd1f7c919a1071669b7..7e61aff0b3c8cfb7db751928ad41202624b65283 100644 (file)
@@ -424,6 +424,52 @@ await describe('ConfigurationKeyUtils', async () => {
       )
       assert.strictEqual(k.value, '60')
     })
+
+    await it('should resolve LocalAuthListEnabled to LocalAuthListCtrlr.Enabled on OCPP 2.0.1 station', () => {
+      // Arrange
+      const cs = createStationForVersion(OCPPVersion.VERSION_201)
+
+      // Act
+      addConfigurationKey(cs, StandardParametersKey.LocalAuthListEnabled, 'true', undefined, {
+        save: false,
+      })
+
+      // Assert
+      const k = getConfigurationKey(cs, StandardParametersKey.LocalAuthListEnabled)
+      if (k == null) {
+        assert.fail('Expected configuration key to be found')
+      }
+      assert.strictEqual(
+        k.key,
+        buildConfigKey(OCPP20ComponentName.LocalAuthListCtrlr, StandardParametersKey.Enabled)
+      )
+      assert.strictEqual(k.value, 'true')
+    })
+
+    await it('should resolve ReserveConnectorZeroSupported to ReservationCtrlr.NonEvseSpecific on OCPP 2.0.1 station', () => {
+      // Arrange
+      const cs = createStationForVersion(OCPPVersion.VERSION_201)
+
+      // Act
+      addConfigurationKey(
+        cs,
+        StandardParametersKey.ReserveConnectorZeroSupported,
+        'false',
+        undefined,
+        { save: false }
+      )
+
+      // Assert
+      const k = getConfigurationKey(cs, StandardParametersKey.ReserveConnectorZeroSupported)
+      if (k == null) {
+        assert.fail('Expected configuration key to be found')
+      }
+      assert.strictEqual(
+        k.key,
+        buildConfigKey(OCPP20ComponentName.ReservationCtrlr, StandardParametersKey.NonEvseSpecific)
+      )
+      assert.strictEqual(k.value, 'false')
+    })
   })
 
   await describe('SetConfigurationKeyValue', async () => {