From a2fc93879a836c5a6a27789b6c372fcb9f34e6e1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 27 Mar 2026 13:46:34 +0100 Subject: [PATCH] feat: enrich OCPP 2.0 template with OCPP 1.6 equivalent config keys MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 --- .../keba-ocpp2.station-template.json | 47 ++- src/charging-station/ConfigurationKeyUtils.ts | 8 + .../ocpp/2.0/OCPP20ServiceUtils.ts | 12 +- .../ocpp/2.0/OCPP20VariableRegistry.ts | 359 ++++++++++-------- src/types/ocpp/2.0/Variables.ts | 2 + .../ConfigurationKeyUtils.test.ts | 46 +++ 6 files changed, 293 insertions(+), 181 deletions(-) diff --git a/src/assets/station-templates/keba-ocpp2.station-template.json b/src/assets/station-templates/keba-ocpp2.station-template.json index 351d6117..5ff0561a 100644 --- a/src/assets/station-templates/keba-ocpp2.station-template.json +++ b/src/assets/station-templates/keba-ocpp2.station-template.json @@ -17,44 +17,69 @@ "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" } ] }, diff --git a/src/charging-station/ConfigurationKeyUtils.ts b/src/charging-station/ConfigurationKeyUtils.ts index 891255a2..79b57ec4 100644 --- a/src/charging-station/ConfigurationKeyUtils.ts +++ b/src/charging-station/ConfigurationKeyUtils.ts @@ -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, diff --git a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts index 7cd6adc7..aa778e43 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts @@ -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 diff --git a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts index 9a19930d..3a70bb4e 100644 --- a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts +++ b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts @@ -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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { [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 = { [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 = { [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 = { [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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { }, [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 = { }, [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 = { }, [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 = { 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 = { [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 = { [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 = { 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 = { 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 = { 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, diff --git a/src/types/ocpp/2.0/Variables.ts b/src/types/ocpp/2.0/Variables.ts index ee419459..a7e0651c 100644 --- a/src/types/ocpp/2.0/Variables.ts +++ b/src/types/ocpp/2.0/Variables.ts @@ -35,6 +35,8 @@ export enum OCPP20DeviceInfoVariableName { export enum OCPP20OptionalVariableName { HeartbeatInterval = 'HeartbeatInterval', MaxCertificateChainSize = 'MaxCertificateChainSize', + MaxEnergyOnInvalidId = 'MaxEnergyOnInvalidId', + NonEvseSpecific = 'NonEvseSpecific', WebSocketPingInterval = 'WebSocketPingInterval', } diff --git a/tests/charging-station/ConfigurationKeyUtils.test.ts b/tests/charging-station/ConfigurationKeyUtils.test.ts index e9aca27e..7e61aff0 100644 --- a/tests/charging-station/ConfigurationKeyUtils.test.ts +++ b/tests/charging-station/ConfigurationKeyUtils.test.ts @@ -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 () => { -- 2.43.0