]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
fix(ocpp2): refine variables handling main
authorJérôme Benoit <jerome.benoit@sap.com>
Thu, 30 Oct 2025 14:44:58 +0000 (15:44 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Thu, 30 Oct 2025 14:44:58 +0000 (15:44 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ocpp/2.0/OCPP20VariableManager.ts
src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts
tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts

index 3c4883d58d40e373bc295071b9740c84939197f9..8fc9eac07e7042b1b3671288abaf8586ca06c645 100644 (file)
@@ -478,10 +478,27 @@ export class OCPP20VariableManager {
       variableMetadata.mutability !== MutabilityEnumType.WriteOnly
     ) {
       const configurationKeyName = computeConfigurationKeyName(variableMetadata)
-      const cfg = getConfigurationKey(
+      let cfg = getConfigurationKey(
         chargingStation,
         configurationKeyName as unknown as StandardParametersKey
       )
+
+      if (cfg == null) {
+        addConfigurationKey(
+          chargingStation,
+          configurationKeyName as unknown as StandardParametersKey,
+          value, // Use the resolved default value
+          undefined,
+          {
+            overwrite: false,
+          }
+        )
+        cfg = getConfigurationKey(
+          chargingStation,
+          configurationKeyName as unknown as StandardParametersKey
+        )
+      }
+
       if (cfg?.value) {
         value = cfg.value
       }
@@ -838,53 +855,42 @@ export class OCPP20VariableManager {
       configurationKeyName as unknown as StandardParametersKey
     )?.value
 
-    // Generalized persistence for persistent, non write-only variables (including instance-scoped)
     if (
       variableMetadata.persistence === PersistenceEnumType.Persistent &&
       variableMetadata.mutability !== MutabilityEnumType.WriteOnly
     ) {
-      // Special-case: OrganizationName persistence limitation (do not update stored value once created)
-      const isOrganizationName =
-        variableMetadata.component === (OCPP20ComponentName.SecurityCtrlr as string) &&
-        variableMetadata.variable === (OCPP20RequiredVariableName.OrganizationName as string)
-
-      if (!isOrganizationName) {
-        let configKey = getConfigurationKey(
+      let configKey = getConfigurationKey(
+        chargingStation,
+        configurationKeyName as unknown as StandardParametersKey
+      )
+      if (configKey == null) {
+        addConfigurationKey(
+          chargingStation,
+          configurationKeyName as unknown as StandardParametersKey,
+          attributeValue,
+          undefined,
+          {
+            overwrite: false,
+          }
+        )
+        configKey = getConfigurationKey(
           chargingStation,
           configurationKeyName as unknown as StandardParametersKey
         )
-        if (configKey == null) {
-          addConfigurationKey(
-            chargingStation,
-            configurationKeyName as unknown as StandardParametersKey,
-            attributeValue,
-            undefined,
-            {
-              overwrite: false,
-            }
-          )
-          configKey = getConfigurationKey(
+      } else if (configKey.value !== attributeValue) {
+        setConfigurationKeyValue(
+          chargingStation,
+          configurationKeyName as unknown as StandardParametersKey,
+          attributeValue
+        )
+      }
+      rebootRequired =
+        (variableMetadata.rebootRequired === true ||
+          getConfigurationKey(
             chargingStation,
             configurationKeyName as unknown as StandardParametersKey
-          )
-        } else if (configKey.value !== attributeValue) {
-          setConfigurationKeyValue(
-            chargingStation,
-            configurationKeyName as unknown as StandardParametersKey,
-            attributeValue
-          )
-        }
-        rebootRequired =
-          (variableMetadata.rebootRequired === true ||
-            getConfigurationKey(
-              chargingStation,
-              configurationKeyName as unknown as StandardParametersKey
-            )?.reboot === true) &&
-          previousValue !== attributeValue
-      } else {
-        // OrganizationName: accept set but do not persist new value (tests expect default retained)
-        rebootRequired = false
-      }
+          )?.reboot === true) &&
+        previousValue !== attributeValue
     }
     // Heartbeat & WS ping interval dynamic restarts
     if (
index 89859da07296768e1d950c6039e09c5c1376a916..934a4267ab00f7c7637b362bd3b52c469ebe0cd0 100644 (file)
@@ -99,7 +99,273 @@ const DECIMAL_ONLY_PATTERN = /^-?\d+\.\d+$/
 // - Only add rationale comments where simulator intentionally restricts or extends (e.g. enumeration trimming, volatile choice).
 // - Avoid verbose line or row numbers; keep comments concise.
 export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
-  // AuthCtrlr variables
+  // AlignedDataCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'Available')]: {
+    component: OCPP20ComponentName.AlignedDataCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'If this variable reports a value of true, Clock-Aligned Data is supported.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'Enabled')]: {
+    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: 'Enabled',
+  },
+  [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,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: 's',
+    variable: 'Interval',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'Measurands')]: {
+    component: OCPP20ComponentName.AlignedDataCtrlr as string,
+    dataType: DataEnumType.MemberList,
+    defaultValue: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
+    description:
+      'Clock-aligned measurand(s) to be included in MeterValuesRequest, every AlignedDataInterval seconds.',
+    enumeration: [
+      OCPP20MeasurandEnumType.CURRENT_EXPORT,
+      OCPP20MeasurandEnumType.CURRENT_IMPORT,
+      OCPP20MeasurandEnumType.CURRENT_OFFERED,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_EXPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_EXPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_IMPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_EXPORT_INTERVAL,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_EXPORT_INTERVAL,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_IMPORT_INTERVAL,
+      OCPP20MeasurandEnumType.FREQUENCY,
+      OCPP20MeasurandEnumType.POWER_ACTIVE_EXPORT,
+      OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
+      OCPP20MeasurandEnumType.POWER_FACTOR,
+      OCPP20MeasurandEnumType.POWER_OFFERED,
+      OCPP20MeasurandEnumType.POWER_REACTIVE_EXPORT,
+      OCPP20MeasurandEnumType.POWER_REACTIVE_IMPORT,
+      OCPP20MeasurandEnumType.STATE_OF_CHARGE,
+      OCPP20MeasurandEnumType.VOLTAGE,
+    ],
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Measurands',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'SendDuringIdle')]: {
+    component: OCPP20ComponentName.AlignedDataCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If set to true, the Charging Station SHALL NOT send clock aligned meter values when a transaction is ongoing.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'SendDuringIdle',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'SignReadings')]: {
+    component: OCPP20ComponentName.AlignedDataCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If set to true, the Charging Station SHALL include signed meter values in the SampledValueType in the MeterValuesRequest to the CSMS.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'SignReadings',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'TxEndedInterval')]: {
+    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 TransactionEventRequest (eventType = Ended) message.',
+    min: 1,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: 's',
+    variable: 'TxEndedInterval',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AlignedDataCtrlr as string, 'TxEndedMeasurands')]: {
+    component: OCPP20ComponentName.AlignedDataCtrlr as string,
+    dataType: DataEnumType.MemberList,
+    defaultValue: `${OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER},${OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL},${OCPP20MeasurandEnumType.VOLTAGE}`,
+    description:
+      'Clock-aligned measurands to be included in the meterValues element of TransactionEventRequest (eventType = Ended), every SampledDataTxEndedInterval seconds from the start of the transaction.',
+    enumeration: [
+      OCPP20MeasurandEnumType.CURRENT_EXPORT,
+      OCPP20MeasurandEnumType.CURRENT_IMPORT,
+      OCPP20MeasurandEnumType.CURRENT_OFFERED,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_EXPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_EXPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_IMPORT_REGISTER,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_EXPORT_INTERVAL,
+      OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_EXPORT_INTERVAL,
+      OCPP20MeasurandEnumType.ENERGY_REACTIVE_IMPORT_INTERVAL,
+      OCPP20MeasurandEnumType.FREQUENCY,
+      OCPP20MeasurandEnumType.POWER_ACTIVE_EXPORT,
+      OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
+      OCPP20MeasurandEnumType.POWER_FACTOR,
+      OCPP20MeasurandEnumType.POWER_OFFERED,
+      OCPP20MeasurandEnumType.POWER_REACTIVE_EXPORT,
+      OCPP20MeasurandEnumType.POWER_REACTIVE_IMPORT,
+      OCPP20MeasurandEnumType.STATE_OF_CHARGE,
+      OCPP20MeasurandEnumType.VOLTAGE,
+    ],
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'TxEndedMeasurands',
+  },
+
+  // AuthCacheCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.AuthCacheCtrlr as string, 'Available')]: {
+    component: OCPP20ComponentName.AuthCacheCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Authorization caching is available, but not necessarily enabled.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCacheCtrlr as string, 'DisablePostAuthorize')]: {
+    component: OCPP20ComponentName.AuthCacheCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'When set to true this variable disables the behavior to request authorization for an idToken that is stored in the cache with a status other than Accepted, as stated in C10.FR.03 and C12.FR.05.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'DisablePostAuthorize',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCacheCtrlr as string, 'Enabled')]: {
+    component: OCPP20ComponentName.AuthCacheCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'If set to true, Authorization caching is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCacheCtrlr as string, 'LifeTime')]: {
+    component: OCPP20ComponentName.AuthCacheCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '86400',
+    description:
+      'Indicates how long it takes until a token expires in the authorization cache since it is last used',
+    min: 1,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'LifeTime',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCacheCtrlr as string, 'Policy')]: {
+    component: OCPP20ComponentName.AuthCacheCtrlr as string,
+    dataType: DataEnumType.OptionList,
+    defaultValue: 'LRU',
+    description:
+      'Cache Entry Replacement Policy: least recently used, least frequently used, first in first out, other custom mechanism.',
+    enumeration: ['LRU', 'LFU', 'FIFO', 'Custom'],
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Policy',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCacheCtrlr as string, 'Storage')]: {
+    characteristics: {
+      maxLimit: 1048576, // 1MB default
+    },
+    component: OCPP20ComponentName.AuthCacheCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '0',
+    description:
+      'Indicates the number of bytes currently used by the Authorization Cache. MaxLimit indicates the maximum number of bytes that can be used by the Authorization Cache.',
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual, AttributeEnumType.MaxSet],
+    unit: 'B',
+    variable: 'Storage',
+  },
+
+  // AuthCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.AuthCtrlr as string, 'AdditionalInfoItemsPerMessage')]: {
+    component: OCPP20ComponentName.AuthCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '10',
+    description: 'Maximum number of AdditionalInfo items that can be sent in one message.',
+    max: 100,
+    min: 1,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    positive: true,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'AdditionalInfoItemsPerMessage',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCtrlr as string, 'DisableRemoteAuthorization')]: {
+    component: OCPP20ComponentName.AuthCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'When set to true, instructs the Charging Station to not issue any AuthorizationRequests but only use Authorization Cache and Local Authorization List.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'DisableRemoteAuthorization',
+  },
+
+  [buildRegistryKey(OCPP20ComponentName.AuthCtrlr as string, 'Enabled')]: {
+    component: OCPP20ComponentName.AuthCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description:
+      'If set to false, no authorization is done before starting a transaction or when reading an idToken.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCtrlr as string, 'MasterPassGroupId')]: {
+    component: OCPP20ComponentName.AuthCtrlr as string,
+    dataType: DataEnumType.string,
+    description:
+      'IdTokens that have this id as groupId belong to the Master Pass Group. They can stop any ongoing transaction but cannot start transactions.',
+    maxLength: 36,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'MasterPassGroupId',
+  },
+  [buildRegistryKey(OCPP20ComponentName.AuthCtrlr as string, 'OfflineTxForUnknownIdEnabled')]: {
+    component: OCPP20ComponentName.AuthCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'Support for unknown offline transactions.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'OfflineTxForUnknownIdEnabled',
+  },
   [buildRegistryKey(
     OCPP20ComponentName.AuthCtrlr as string,
     OCPP20RequiredVariableName.AuthorizeRemoteStart
@@ -140,6 +406,94 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     variable: OCPP20RequiredVariableName.LocalPreAuthorize as string,
   },
 
+  // CHAdeMOCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.CHAdeMOCtrlr as string, 'AutoManufacturerCode')]: {
+    component: OCPP20ComponentName.CHAdeMOCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '0',
+    description: "Auto manufacturer code (H'700.0)",
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'AutoManufacturerCode',
+  },
+  [buildRegistryKey(OCPP20ComponentName.CHAdeMOCtrlr as string, 'CHAdeMOProtocolNumber')]: {
+    component: OCPP20ComponentName.CHAdeMOCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '66048',
+    description: "CHAdeMO protocol number (H'102.0)",
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'CHAdeMOProtocolNumber',
+  },
+  [buildRegistryKey(OCPP20ComponentName.CHAdeMOCtrlr as string, 'DynamicControl')]: {
+    component: OCPP20ComponentName.CHAdeMOCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: "Vehicle is compatible with dynamic control (H'110.0.0)",
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'DynamicControl',
+  },
+  [buildRegistryKey(OCPP20ComponentName.CHAdeMOCtrlr as string, 'HighCurrentControl')]: {
+    component: OCPP20ComponentName.CHAdeMOCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: "Vehicle is compatible with high current control (H'110.0.1)",
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'HighCurrentControl',
+  },
+  [buildRegistryKey(OCPP20ComponentName.CHAdeMOCtrlr as string, 'HighVoltageControl')]: {
+    component: OCPP20ComponentName.CHAdeMOCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: "Vehicle is compatible with high voltage control (H'110.1.2)",
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'HighVoltageControl',
+  },
+  [buildRegistryKey(OCPP20ComponentName.CHAdeMOCtrlr as string, 'SelftestActive')]: {
+    component: OCPP20ComponentName.CHAdeMOCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'Self-test is active or self-test is started by setting to true.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'SelftestActive',
+  },
+  [buildRegistryKey(OCPP20ComponentName.CHAdeMOCtrlr as string, 'VehicleStatus')]: {
+    component: OCPP20ComponentName.CHAdeMOCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: "Vehicle status (H'102.5.3)",
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'VehicleStatus',
+  },
+
+  // ChargingStation Component
+  [buildRegistryKey(
+    OCPP20ComponentName.ChargingStation as string,
+    'AllowNewSessionsPendingFirmwareUpdate'
+  )]: {
+    component: OCPP20ComponentName.ChargingStation as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'Indicates whether new sessions can be started on EVSEs while Charging Station is waiting for all EVSEs to become Available in order to start a pending firmware update.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'AllowNewSessionsPendingFirmwareUpdate',
+  },
   [buildRegistryKey(OCPP20ComponentName.ChargingStation as string, 'Available')]: {
     component: OCPP20ComponentName.ChargingStation as string,
     dataType: DataEnumType.boolean,
@@ -150,6 +504,16 @@ 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: 'Model',
+  },
   [buildRegistryKey(OCPP20ComponentName.ChargingStation as string, 'SupplyPhases')]: {
     component: OCPP20ComponentName.ChargingStation as string,
     dataType: DataEnumType.integer,
@@ -162,7 +526,16 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'SupplyPhases',
   },
-  // ChargingStation variables
+  [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: 'VendorName',
+  },
   [buildRegistryKey(
     OCPP20ComponentName.ChargingStation as string,
     OCPP20DeviceInfoVariableName.AvailabilityState
@@ -192,7 +565,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     mutability: MutabilityEnumType.ReadWrite,
     persistence: PersistenceEnumType.Persistent,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: 'seconds',
+    unit: 's',
     variable: OCPP20OptionalVariableName.WebSocketPingInterval as string,
   },
   [buildRegistryKey(
@@ -212,32 +585,92 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     vendorSpecific: true,
   },
 
-  // ClockCtrlr variables
-  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, OCPP20RequiredVariableName.DateTime)]:
+  // ClockCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, 'NextTimeOffsetTransitionDateTime')]:
     {
       component: OCPP20ComponentName.ClockCtrlr as string,
       dataType: DataEnumType.dateTime,
-      description: 'Contains the current date and time (ClockCtrlr).',
-      dynamicValueResolver: () => new Date().toISOString(),
-      mutability: MutabilityEnumType.ReadOnly,
-      persistence: PersistenceEnumType.Volatile,
+      description: 'Date time of the next time offset transition.',
+      mutability: MutabilityEnumType.ReadWrite,
+      persistence: PersistenceEnumType.Persistent,
       supportedAttributes: [AttributeEnumType.Actual],
-      variable: OCPP20RequiredVariableName.DateTime as string,
+      variable: 'NextTimeOffsetTransitionDateTime',
     },
-  [buildRegistryKey(
-    OCPP20ComponentName.ClockCtrlr as string,
-    OCPP20RequiredVariableName.TimeSource
-  )]: {
+  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, 'NtpServerUri')]: {
     component: OCPP20ComponentName.ClockCtrlr as string,
-    dataType: DataEnumType.SequenceList,
-    defaultValue: 'NTP,GPS,RealTimeClock,Heartbeat',
-    description: 'Ordered list of clock sources by preference.',
-    enumeration: [
-      'Heartbeat',
-      'NTP',
-      'GPS',
-      'RealTimeClock',
-      'MobileNetwork',
+    dataType: DataEnumType.string,
+    description: 'This contains the address of the NTP server.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'NtpServerUri',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, 'NtpSource')]: {
+    component: OCPP20ComponentName.ClockCtrlr as string,
+    dataType: DataEnumType.OptionList,
+    description:
+      'When an NTP client is implemented, this variable can be used to configure the client',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'NtpSource',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, 'TimeAdjustmentReportingThreshold')]:
+    {
+      component: OCPP20ComponentName.ClockCtrlr as string,
+      dataType: DataEnumType.integer,
+      description:
+        'If set, then time adjustments with an absolute value in seconds larger than this need to be reported as a security event SettingSystemTime',
+      mutability: MutabilityEnumType.ReadWrite,
+      persistence: PersistenceEnumType.Persistent,
+      supportedAttributes: [AttributeEnumType.Actual],
+      variable: 'TimeAdjustmentReportingThreshold',
+    },
+  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, 'TimeOffset')]: {
+    component: OCPP20ComponentName.ClockCtrlr as string,
+    dataType: DataEnumType.string,
+    description:
+      'A Time Offset with respect to Coordinated Universal Time (aka UTC or Greenwich Mean Time) in the form of an [RFC3339] time (zone) offset suffix, including the mandatory "+" or "-" prefix.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'TimeOffset',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, 'TimeZone')]: {
+    component: OCPP20ComponentName.ClockCtrlr as string,
+    dataType: DataEnumType.string,
+    description:
+      'Configured current local time zone in the format: "Europe/Oslo", "Asia/Singapore" etc. For display purposes.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'TimeZone',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ClockCtrlr as string, OCPP20RequiredVariableName.DateTime)]:
+    {
+      component: OCPP20ComponentName.ClockCtrlr as string,
+      dataType: DataEnumType.dateTime,
+      description: 'Contains the current date and time (ClockCtrlr).',
+      dynamicValueResolver: () => new Date().toISOString(),
+      mutability: MutabilityEnumType.ReadOnly,
+      persistence: PersistenceEnumType.Volatile,
+      supportedAttributes: [AttributeEnumType.Actual],
+      variable: OCPP20RequiredVariableName.DateTime as string,
+    },
+  [buildRegistryKey(
+    OCPP20ComponentName.ClockCtrlr as string,
+    OCPP20RequiredVariableName.TimeSource
+  )]: {
+    component: OCPP20ComponentName.ClockCtrlr as string,
+    dataType: DataEnumType.SequenceList,
+    defaultValue: 'NTP,GPS,RealTimeClock,Heartbeat',
+    description: 'Ordered list of clock sources by preference.',
+    enumeration: [
+      'Heartbeat',
+      'NTP',
+      'GPS',
+      'RealTimeClock',
+      'MobileNetwork',
       'RadioTimeTransmitter',
     ],
     mutability: MutabilityEnumType.ReadWrite,
@@ -246,7 +679,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     variable: OCPP20RequiredVariableName.TimeSource as string,
   },
 
-  // DeviceDataCtrlr variables
+  // DeviceDataCtrlr Component
   [buildRegistryKey(
     OCPP20ComponentName.DeviceDataCtrlr as string,
     OCPP20RequiredVariableName.BytesPerMessage
@@ -427,7 +860,531 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     variable: OCPP20RequiredVariableName.ValueSize as string,
   },
 
-  // OCPPCommCtrlr variables
+  // EVSE Component
+  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'AllowReset')]: {
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Can be used to announce that an EVSE can be reset individually',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'AllowReset',
+  },
+  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'AvailabilityState')]: {
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.OptionList,
+    defaultValue: 'Operative',
+    description: 'This variable reports current availability state for the EVSE',
+    enumeration: ['Operative', 'Inoperative'],
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'AvailabilityState',
+  },
+  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'Available')]: {
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Component exists',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'EvseId')]: {
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.string,
+    defaultValue: '1',
+    description:
+      'The name of the EVSE in the string format as required by ISO 15118 and IEC 63119-2.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'EvseId',
+  },
+  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'ISO15118EvseId')]: {
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.string,
+    defaultValue: 'DE*ICE*E*1234567890*1',
+    description:
+      'The name of the EVSE in the string format as required by ISO 15118 and IEC 63119-2.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ISO15118EvseId',
+  },
+  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'Power')]: {
+    characteristics: {
+      maxLimit: 22000, // 22kW default
+    },
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.decimal,
+    defaultValue: '0',
+    description: 'The maximum power that this EVSE can provide and instantaneous power',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual, AttributeEnumType.MaxSet],
+    supportsTarget: false,
+    unit: 'W',
+    variable: 'Power',
+  },
+  [buildRegistryKey(OCPP20ComponentName.EVSE as string, 'SupplyPhases')]: {
+    component: OCPP20ComponentName.EVSE as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '3',
+    description: 'Number of alternating current phases connected/available.',
+    max: 3,
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'SupplyPhases',
+  },
+
+  // ISO15118Ctrlr Component
+  [buildRegistryKey(
+    OCPP20ComponentName.ISO15118Ctrlr as string,
+    'CentralContractValidationAllowed'
+  )]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If this variable exists and has the value true, then Charging Station can provide a contract certificate that it cannot validate, to the CSMS for validation as part of the AuthorizeRequest.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'CentralContractValidationAllowed',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.ISO15118Ctrlr as string,
+    'ContractCertificateInstallationEnabled'
+  )]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If this variable is true, then ISO 15118 contract certificate installation/update as described by use case M01 - Certificate installation EV and M02 - Certificate Update EV is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ContractCertificateInstallationEnabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'ContractValidationOffline')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If this variable is true, then Charging Station will try to validate a contract certificate when it is offline',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ContractValidationOffline',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'CountryName')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.string,
+    defaultValue: 'DE',
+    description:
+      'The countryName of the SECC in the ISO 3166-1 format. It is used as the countryName (C) of the SECC leaf certificate.',
+    maxLength: 2,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'CountryName',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'MaxScheduleEntries')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '24',
+    description: 'Maximum number of allowed schedule periods.',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'MaxScheduleEntries',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'OrganizationName')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.string,
+    defaultValue: 'Example Charging Services Ltd',
+    description:
+      'The organizationName of the CSO operating the charging station. It is used as the organizationName (O) of the SECC leaf certificate.',
+    maxLength: 64,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'OrganizationName',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'PnCEnabled')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If this variable is true, then ISO 15118 plug and charge as described by use case C07 - Authorization using Contract Certificates is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'PnCEnabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'RequestedEnergyTransferMode')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.OptionList,
+    defaultValue: 'DC_extended',
+    description: 'The requested energy transfer mode.',
+    enumeration: [
+      'AC_single_phase_core',
+      'AC_three_phase_core',
+      'DC_core',
+      'DC_extended',
+      'DC_combo_core',
+      'DC_unique',
+    ],
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'RequestedEnergyTransferMode',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'RequestMeteringReceipt')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'If true, then Charging Station shall request a metering receipt from EV.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'RequestMeteringReceipt',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ISO15118Ctrlr as string, 'SeccId')]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.string,
+    defaultValue: 'DE*ICE*E*1234567890',
+    description: 'The ID of the SECC in string format as defined by ISO15118.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'SeccId',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.ISO15118Ctrlr as string,
+    'V2GCertificateInstallationEnabled'
+  )]: {
+    component: OCPP20ComponentName.ISO15118Ctrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If this variable is true, then ISO 15118 V2G Charging Station certificate installation as described by use case A02 - Update Charging Station Certificate by request of CSMS and A03 - Update Charging Station Certificate initiated by the Charging Station is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'V2GCertificateInstallationEnabled',
+  },
+
+  // LocalAuthListCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'Available')]: {
+    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Local Authorization List is available.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    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,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'BytesPerMessage',
+  },
+  [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'DisablePostAuthorize')]: {
+    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'When set to true this variable disables the behavior to request authorization for an idToken that is stored in the local authorization list with a status other than Accepted, as stated in C14.FR.03.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'DisablePostAuthorize',
+  },
+  [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'Enabled')]: {
+    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If this variable exists and reports a value of true, Local Authorization List is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'Entries')]: {
+    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '0',
+    description: 'Amount of IdTokens currently in the Local Authorization List',
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    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,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ItemsPerMessage',
+  },
+  [buildRegistryKey(OCPP20ComponentName.LocalAuthListCtrlr as string, 'Storage')]: {
+    characteristics: {
+      maxLimit: 1048576, // 1MB default
+    },
+    component: OCPP20ComponentName.LocalAuthListCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '0',
+    description:
+      'Indicates the number of bytes currently used by the Local Authorization List. MaxLimit indicates the maximum number of bytes that can be used by the Local Authorization List.',
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual, AttributeEnumType.MaxSet],
+    unit: 'B',
+    variable: 'Storage',
+  },
+
+  // MonitoringCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'ActiveMonitoringBase')]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.OptionList,
+    defaultValue: 'All',
+    description:
+      'Shows the currently used MonitoringBase. Valid values according MonitoringBaseEnumType: All, FactoryDefault, HardwiredOnly.',
+    enumeration: ['All', 'FactoryDefault', 'HardwiredOnly'],
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ActiveMonitoringBase',
+  },
+  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'ActiveMonitoringLevel')]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '9',
+    description:
+      'Shows the currently used MonitoringLevel. Valid values are severity levels of SetMonitoringLevelRequest: 0-9.',
+    max: 9,
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ActiveMonitoringLevel',
+  },
+  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'Available')]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Whether monitoring is available',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.MonitoringCtrlr as string,
+    'BytesPerMessage',
+    'ClearVariableMonitoring'
+  )]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '8192',
+    description: 'Maximum number of bytes in a ClearVariableMonitoring message.',
+    instance: 'ClearVariableMonitoring',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'BytesPerMessage',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.MonitoringCtrlr as string,
+    'BytesPerMessage',
+    'SetVariableMonitoring'
+  )]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '8192',
+    description: 'Maximum number of bytes in a SetVariableMonitoring message',
+    instance: 'SetVariableMonitoring',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'BytesPerMessage',
+  },
+  [buildRegistryKey(OCPP20ComponentName.MonitoringCtrlr as string, 'Enabled')]: {
+    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: 'Enabled',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.MonitoringCtrlr as string,
+    'ItemsPerMessage',
+    'ClearVariableMonitoring'
+  )]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '100',
+    description: 'Maximum number of IDs in a ClearVariableMonitoringRequest.',
+    instance: 'ClearVariableMonitoring',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ItemsPerMessage',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.MonitoringCtrlr as string,
+    'ItemsPerMessage',
+    'SetVariableMonitoring'
+  )]: {
+    component: OCPP20ComponentName.MonitoringCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '100',
+    description:
+      'Maximum number of setMonitoringData elements that can be sent in one setVariableMonitoringRequest message.',
+    instance: 'SetVariableMonitoring',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: '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',
+  },
+
+  // OCPPCommCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'ActiveNetworkProfile')]: {
+    component: OCPP20ComponentName.OCPPCommCtrlr as string,
+    dataType: DataEnumType.string,
+    description:
+      'Indicates the configuration profile the station uses at that moment to connect to the network.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ActiveNetworkProfile',
+  },
+  [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'FieldLength')]: {
+    component: OCPP20ComponentName.OCPPCommCtrlr as string,
+    dataType: DataEnumType.integer,
+    description:
+      'This variable is used to report the length of <field> in <message> when it is larger than the length that is defined in the standard OCPP message schema.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'FieldLength',
+  },
+  [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'PublicKeyWithSignedMeterValue')]:
+    {
+      component: OCPP20ComponentName.OCPPCommCtrlr as string,
+      dataType: DataEnumType.OptionList,
+      description:
+        'This Configuration Variable can be used to configure whether a public key needs to be sent with a signed meter value.',
+      mutability: MutabilityEnumType.ReadWrite,
+      persistence: PersistenceEnumType.Persistent,
+      supportedAttributes: [AttributeEnumType.Actual],
+      variable: 'PublicKeyWithSignedMeterValue',
+    },
+  [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'QueueAllMessages')]: {
+    component: OCPP20ComponentName.OCPPCommCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'When this variable is set to true, the Charging Station will queue all message until they are delivered to the CSMS.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'QueueAllMessages',
+  },
+  [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'RetryBackOffRandomRange')]: {
+    component: OCPP20ComponentName.OCPPCommCtrlr as string,
+    dataType: DataEnumType.integer,
+    description:
+      'When the Charging Station is reconnecting, after a connection loss, it will use this variable as the maximum value for the random part of the back-off time',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'RetryBackOffRandomRange',
+  },
+  [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'RetryBackOffRepeatTimes')]: {
+    component: OCPP20ComponentName.OCPPCommCtrlr as string,
+    dataType: DataEnumType.integer,
+    description:
+      'When the Charging Station is reconnecting, after a connection loss, it will use this variable for the amount of times it will double the previous back-off time.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'RetryBackOffRepeatTimes',
+  },
+  [buildRegistryKey(OCPP20ComponentName.OCPPCommCtrlr as string, 'RetryBackOffWaitMinimum')]: {
+    component: OCPP20ComponentName.OCPPCommCtrlr as string,
+    dataType: DataEnumType.integer,
+    description:
+      'When the Charging Station is reconnecting, after a connection loss, it will use this variable as the minimum back-off time, the first time it tries to reconnect.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'RetryBackOffWaitMinimum',
+  },
   [buildRegistryKey(
     OCPP20ComponentName.OCPPCommCtrlr as string,
     OCPP20OptionalVariableName.HeartbeatInterval
@@ -441,10 +1398,27 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     min: 1,
     mutability: MutabilityEnumType.ReadWrite,
     persistence: PersistenceEnumType.Persistent,
-    positive: true,
+    positive: true,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: 's',
+    variable: OCPP20OptionalVariableName.HeartbeatInterval as string,
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.OCPPCommCtrlr as string,
+    OCPP20OptionalVariableName.WebSocketPingInterval
+  )]: {
+    allowZero: true,
+    component: OCPP20ComponentName.OCPPCommCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '30',
+    description:
+      '0 disables client side websocket Ping/Pong. Positive values are interpreted as number of seconds between pings. Negative values are not allowed.',
+    min: 0,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: 'seconds',
-    variable: OCPP20OptionalVariableName.HeartbeatInterval as string,
+    unit: 's',
+    variable: OCPP20OptionalVariableName.WebSocketPingInterval as string,
   },
   [buildRegistryKey(
     OCPP20ComponentName.OCPPCommCtrlr as string,
@@ -476,7 +1450,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     persistence: PersistenceEnumType.Persistent,
     positive: true,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: 'seconds',
+    unit: 's',
     variable: OCPP20RequiredVariableName.MessageAttemptInterval as string,
   },
   [buildRegistryKey(
@@ -513,7 +1487,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     persistence: PersistenceEnumType.Persistent,
     positive: true,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: 'seconds',
+    unit: 's',
     variable: OCPP20RequiredVariableName.MessageTimeout as string,
   },
   [buildRegistryKey(
@@ -521,7 +1495,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     OCPP20RequiredVariableName.NetworkConfigurationPriority
   )]: {
     component: OCPP20ComponentName.OCPPCommCtrlr as string,
-    dataType: DataEnumType.SequenceList,
+    dataType: DataEnumType.string,
     defaultValue: '1,2,3',
     description: 'Comma separated ordered list of network profile priorities.',
     enumeration: ['1', '2', '3'],
@@ -559,7 +1533,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     persistence: PersistenceEnumType.Persistent,
     positive: true,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: 'seconds',
+    unit: 's',
     variable: OCPP20RequiredVariableName.OfflineThreshold as string,
   },
   [buildRegistryKey(
@@ -592,7 +1566,96 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     variable: OCPP20RequiredVariableName.UnlockOnEVSideDisconnect as string,
   },
 
-  // SampledDataCtrlr variables
+  // ReservationCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.ReservationCtrlr as string, 'Available')]: {
+    component: OCPP20ComponentName.ReservationCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Whether reservation is supported.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ReservationCtrlr as string, 'Enabled')]: {
+    component: OCPP20ComponentName.ReservationCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'Whether reservation is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.ReservationCtrlr as string, 'NonEvseSpecific')]: {
+    component: OCPP20ComponentName.ReservationCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If this configuration variable is present and set to true: Charging Station supports Reservation where EVSE id is not specified.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'NonEvseSpecific',
+  },
+
+  // SampledDataCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.SampledDataCtrlr as string, 'Available')]: {
+    component: OCPP20ComponentName.SampledDataCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'If this variable reports a value of true, Sampled Data is supported',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SampledDataCtrlr as string, 'Enabled')]: {
+    component: OCPP20ComponentName.SampledDataCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'If this variable reports a value of true, Sampled Data is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SampledDataCtrlr as string, 'RegisterValuesWithoutPhases')]:
+    {
+      component: OCPP20ComponentName.SampledDataCtrlr as string,
+      dataType: DataEnumType.boolean,
+      defaultValue: 'false',
+      description:
+        'If this variable reports a value of true, then meter values of measurand Energy.Active.Import.Register will only report the total energy over all phases without reporting the individual phase values.',
+      mutability: MutabilityEnumType.ReadWrite,
+      persistence: PersistenceEnumType.Persistent,
+      supportedAttributes: [AttributeEnumType.Actual],
+      variable: 'RegisterValuesWithoutPhases',
+    },
+  [buildRegistryKey(OCPP20ComponentName.SampledDataCtrlr as string, 'SignReadings')]: {
+    component: OCPP20ComponentName.SampledDataCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If set to true, the Charging Station SHALL include signed meter values in the TransactionEventRequest to the CSMS',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'SignReadings',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SampledDataCtrlr as string, 'TxEndedInterval')]: {
+    component: OCPP20ComponentName.SampledDataCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '60',
+    description:
+      'Interval between sampling of metering data, intended to be transmitted in the TransactionEventRequest (eventType = Ended) message.',
+    min: 1,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: 's',
+    variable: 'TxEndedInterval',
+  },
   [buildRegistryKey(
     OCPP20ComponentName.SampledDataCtrlr as string,
     OCPP20MeasurandEnumType.CURRENT_IMPORT
@@ -724,7 +1787,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     persistence: PersistenceEnumType.Volatile,
     positive: true,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: 'seconds',
+    unit: 's',
     variable: OCPP20RequiredVariableName.TxUpdatedInterval as string,
   },
   [buildRegistryKey(
@@ -757,7 +1820,72 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     variable: OCPP20RequiredVariableName.TxUpdatedMeasurands as string,
   },
 
-  // SecurityCtrlr variables
+  // SecurityCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.SecurityCtrlr as string, 'AdditionalRootCertificateCheck')]:
+    {
+      component: OCPP20ComponentName.SecurityCtrlr as string,
+      dataType: DataEnumType.boolean,
+      defaultValue: 'false',
+      description: 'Required for all security profiles except profile 1.',
+      mutability: MutabilityEnumType.ReadWrite,
+      persistence: PersistenceEnumType.Persistent,
+      supportedAttributes: [AttributeEnumType.Actual],
+      variable: 'AdditionalRootCertificateCheck',
+    },
+  [buildRegistryKey(OCPP20ComponentName.SecurityCtrlr as string, 'BasicAuthPassword')]: {
+    component: OCPP20ComponentName.SecurityCtrlr as string,
+    dataType: DataEnumType.string,
+    description: 'The basic authentication password is used for HTTP Basic Authentication.',
+    mutability: MutabilityEnumType.WriteOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'BasicAuthPassword',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SecurityCtrlr as string, 'CertSigningRepeatTimes')]: {
+    component: OCPP20ComponentName.SecurityCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '3',
+    description:
+      'Number of times to resend a SignCertificateRequest when CSMS does nor return a signed certificate.',
+    min: 0,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'CertSigningRepeatTimes',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SecurityCtrlr as string, 'CertSigningWaitMinimum')]: {
+    component: OCPP20ComponentName.SecurityCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '60',
+    description:
+      'Seconds to wait before generating another CSR in case CSMS does not return a signed certificate.',
+    min: 1,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: 's',
+    variable: 'CertSigningWaitMinimum',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SecurityCtrlr as string, 'Identity')]: {
+    component: OCPP20ComponentName.SecurityCtrlr as string,
+    dataType: DataEnumType.string,
+    description: 'The Charging Station identity.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Identity',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SecurityCtrlr as string, 'MaxCertificateChainSize')]: {
+    component: OCPP20ComponentName.SecurityCtrlr as string,
+    dataType: DataEnumType.integer,
+    description:
+      "Limit of the size of the 'certificateChain' field from the CertificateSignedRequest",
+    min: 1,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'MaxCertificateChainSize',
+  },
   [buildRegistryKey(
     OCPP20ComponentName.SecurityCtrlr as string,
     OCPP20RequiredVariableName.CertificateEntries
@@ -824,7 +1952,260 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     vendorSpecific: true,
   },
 
-  // TxCtrlr variables
+  // SmartChargingCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'ACPhaseSwitchingSupported')]:
+    {
+      component: OCPP20ComponentName.SmartChargingCtrlr as string,
+      dataType: DataEnumType.boolean,
+      defaultValue: 'false',
+      description:
+        'This variable can be used to indicate an on-load/in-transaction capability. If defined and true, this EVSE supports the selection of which phase to use for 1 phase AC charging.',
+      mutability: MutabilityEnumType.ReadOnly,
+      persistence: PersistenceEnumType.Persistent,
+      supportedAttributes: [AttributeEnumType.Actual],
+      variable: 'ACPhaseSwitchingSupported',
+    },
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'Available')]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Whether smart charging is supported.',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'Enabled')]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'true',
+    description: 'Whether smart charging is enabled.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.SmartChargingCtrlr as string,
+    'Entries',
+    'ChargingProfiles'
+  )]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '0',
+    description:
+      'Entries(ChargingProfiles) is the amount of Charging profiles currently installed on the Charging Station',
+    instance: 'ChargingProfiles',
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Entries',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.SmartChargingCtrlr as string,
+    'ExternalControlSignalsEnabled'
+  )]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'Indicates whether a Charging Station should respond to external control signals that influence charging.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ExternalControlSignalsEnabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'LimitChangeSignificance')]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.decimal,
+    defaultValue: '1.0',
+    description:
+      'If at the Charging Station side a change in the limit in a ChargingProfile is lower than this percentage, the Charging Station MAY skip sending a NotifyChargingLimitRequest or a TransactionEventRequest message to the CSMS.',
+    max: 100.0,
+    min: 0.0,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: 'Percent',
+    variable: 'LimitChangeSignificance',
+  },
+  [buildRegistryKey(
+    OCPP20ComponentName.SmartChargingCtrlr as string,
+    'NotifyChargingLimitWithSchedules'
+  )]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'Indicates if the Charging Station should include the externally set charging limit/schedule in the message when it sends a NotifyChargingLimitRequest message.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'NotifyChargingLimitWithSchedules',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'PeriodsPerSchedule')]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '24',
+    description: 'Maximum number of periods that may be defined per ChargingSchedule.',
+    min: 1,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'PeriodsPerSchedule',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'Phases3to1')]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'If defined and true, this Charging Station supports switching from 3 to 1 phase during a transaction',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Phases3to1',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'ProfileStackLevel')]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.integer,
+    defaultValue: '10',
+    description:
+      'Maximum acceptable value for stackLevel in a ChargingProfile. Since the lowest stackLevel is 0, this means that if SmartChargingCtrlr.ProfileStackLevel = 1, there can be at most 2 valid charging profiles per Charging Profile Purpose per EVSE.',
+    min: 0,
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'ProfileStackLevel',
+  },
+  [buildRegistryKey(OCPP20ComponentName.SmartChargingCtrlr as string, 'RateUnit')]: {
+    component: OCPP20ComponentName.SmartChargingCtrlr as string,
+    dataType: DataEnumType.MemberList,
+    defaultValue: 'A,W',
+    description:
+      "A list of supported quantities for use in a ChargingSchedule. Allowed values: 'A' and 'W'",
+    enumeration: ['A', 'W'],
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'RateUnit',
+  },
+
+  // TariffCostCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.TariffCostCtrlr as string, 'Available', 'Cost')]: {
+    component: OCPP20ComponentName.TariffCostCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'Instance Cost: Whether costs are supported.',
+    instance: 'Cost',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TariffCostCtrlr as string, 'Available', 'Tariff')]: {
+    component: OCPP20ComponentName.TariffCostCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'Instance Tariff: Whether tariffs are supported.',
+    instance: 'Tariff',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Available',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TariffCostCtrlr as string, 'Currency')]: {
+    component: OCPP20ComponentName.TariffCostCtrlr as string,
+    dataType: DataEnumType.string,
+    defaultValue: 'EUR',
+    description: 'Currency used by this Charging Station in a ISO 4217 formatted currency code.',
+    maxLength: 3,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Currency',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TariffCostCtrlr as string, 'Enabled', 'Cost')]: {
+    component: OCPP20ComponentName.TariffCostCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'Instance Cost: Whether costs are enabled.',
+    instance: 'Cost',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TariffCostCtrlr as string, 'Enabled', 'Tariff')]: {
+    component: OCPP20ComponentName.TariffCostCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description: 'Instance Tariff: Whether tariffs are enabled.',
+    instance: 'Tariff',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'Enabled',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TariffCostCtrlr as string, 'TariffFallbackMessage')]: {
+    component: OCPP20ComponentName.TariffCostCtrlr as string,
+    dataType: DataEnumType.string,
+    defaultValue: 'Standard charging rate applies',
+    description:
+      'Message (and/or tariff information) to be shown to an EV Driver when there is no driver specific tariff information available.',
+    maxLength: 512,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'TariffFallbackMessage',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TariffCostCtrlr as string, 'TotalCostFallbackMessage')]: {
+    component: OCPP20ComponentName.TariffCostCtrlr as string,
+    dataType: DataEnumType.string,
+    defaultValue: 'Cost information not available',
+    description:
+      'Message to be shown to an EV Driver when the Charging Station cannot retrieve the cost for a transaction at the end of the transaction.',
+    maxLength: 512,
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'TotalCostFallbackMessage',
+  },
+
+  // TxCtrlr Component
+  [buildRegistryKey(OCPP20ComponentName.TxCtrlr as string, 'ChargingTime')]: {
+    component: OCPP20ComponentName.TxCtrlr as string,
+    dataType: DataEnumType.decimal,
+    description: 'Time from earliest to latest substantive energy transfer',
+    mutability: MutabilityEnumType.ReadOnly,
+    persistence: PersistenceEnumType.Volatile,
+    supportedAttributes: [AttributeEnumType.Actual],
+    unit: 's',
+    variable: 'ChargingTime',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TxCtrlr as string, 'MaxEnergyOnInvalidId')]: {
+    component: OCPP20ComponentName.TxCtrlr as string,
+    dataType: DataEnumType.integer,
+    description:
+      '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],
+    unit: 'Wh',
+    variable: 'MaxEnergyOnInvalidId',
+  },
+  [buildRegistryKey(OCPP20ComponentName.TxCtrlr as string, 'TxBeforeAcceptedEnabled')]: {
+    component: OCPP20ComponentName.TxCtrlr as string,
+    dataType: DataEnumType.boolean,
+    defaultValue: 'false',
+    description:
+      'Allow charging before having received a BootNotificationResponse with RegistrationStatus: Accepted.',
+    mutability: MutabilityEnumType.ReadWrite,
+    persistence: PersistenceEnumType.Persistent,
+    supportedAttributes: [AttributeEnumType.Actual],
+    variable: 'TxBeforeAcceptedEnabled',
+  },
   [buildRegistryKey(
     OCPP20ComponentName.TxCtrlr as string,
     OCPP20RequiredVariableName.EVConnectionTimeOut
@@ -840,7 +2221,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     persistence: PersistenceEnumType.Persistent,
     positive: true,
     supportedAttributes: [AttributeEnumType.Actual],
-    unit: 'seconds',
+    unit: 's',
     variable: OCPP20RequiredVariableName.EVConnectionTimeOut as string,
   },
   [buildRegistryKey(
@@ -882,7 +2263,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
       'EVConnected',
       'PowerPathClosed',
       'EnergyTransfer',
-      'ParkingBayOccupancy',
+      'ParkingBayOccupied',
       'DataSigned',
     ],
     mutability: MutabilityEnumType.ReadWrite,
@@ -901,7 +2282,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
         'EVConnected',
         'PowerPathClosed',
         'EnergyTransfer',
-        'ParkingBayOccupancy',
+        'ParkingBayOccupied',
       ],
       mutability: MutabilityEnumType.ReadWrite,
       persistence: PersistenceEnumType.Persistent,
index f9885d14c76f5ebe2766136fd5998fdff2319928..91504135b6dc632bc8fae53b19dc4e42558afb4e 100644 (file)
@@ -1553,16 +1553,16 @@ await describe('OCPP20VariableManager test suite', async () => {
         },
       ])[0]
       expect(res.attributeStatus).toBe(GetVariableStatusEnumType.Accepted)
-      expect(res.attributeValue).toBe('ChangeMeOrg')
+      expect(res.attributeValue).toBe('Example Charging Services Ltd')
       const after = getConfigurationKey(
         mockChargingStation,
         OCPP20RequiredVariableName.OrganizationName as unknown as VariableType['name']
       )
       expect(after).toBeDefined()
-      expect(after?.value).toBe('ChangeMeOrg')
+      expect(after?.value).toBe('Example Charging Services Ltd')
     })
 
-    await it('Should accept setting OrganizationName but not persist new value (current limitation)', () => {
+    await it('Should accept setting OrganizationName and require reboot per OCPP 2.0.1 specification', () => {
       const setRes = manager.setVariables(mockChargingStation, [
         {
           attributeValue: 'NewOrgName',
@@ -1570,16 +1570,15 @@ await describe('OCPP20VariableManager test suite', async () => {
           variable: { name: OCPP20RequiredVariableName.OrganizationName },
         },
       ])[0]
-      // Current implementation only marks rebootRequired for ChargingStation component variables
-      expect(setRes.attributeStatus).toBe(SetVariableStatusEnumType.Accepted)
+      // OCPP 2.0.1 compliant behavior: OrganizationName changes require reboot
+      expect(setRes.attributeStatus).toBe(SetVariableStatusEnumType.RebootRequired)
       const getRes = manager.getVariables(mockChargingStation, [
         {
           component: { name: OCPP20ComponentName.SecurityCtrlr },
           variable: { name: OCPP20RequiredVariableName.OrganizationName },
         },
       ])[0]
-      // Value remains the configuration key default due to lack of persistence path for non-ChargingStation components
-      expect(getRes.attributeValue).toBe('ChangeMeOrg')
+      expect(getRes.attributeValue).toBe('NewOrgName')
     })
 
     await it('Should preserve OrganizationName value after resetRuntimeOverrides()', () => {
@@ -1591,7 +1590,8 @@ await describe('OCPP20VariableManager test suite', async () => {
         },
       ])[0]
       expect(res.attributeStatus).toBe(GetVariableStatusEnumType.Accepted)
-      expect(res.attributeValue).toBe('ChangeMeOrg')
+      // Value should persist as 'NewOrgName' from previous test (OCPP 2.0.1 compliant persistence)
+      expect(res.attributeValue).toBe('NewOrgName')
     })
 
     await it('Should create configuration key for instance-scoped MessageAttemptInterval and persist Actual value (Actual-only, no MinSet/MaxSet)', () => {