]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/log
e-mobility-charging-stations-simulator.git
10 days agofix: improve OCPP 2.0.1 TransactionEvent spec compliance
Jérôme Benoit [Sun, 29 Mar 2026 00:45:59 +0000 (01:45 +0100)] 
fix: improve OCPP 2.0.1 TransactionEvent spec compliance

- Auto-derive chargingState in buildTransactionEvent when not explicitly
  provided (E02.FR.13: Started always has chargingState)
- Use idToken type Local instead of hardcoded Central in
  startTransactionOnConnector for ATG local tag consistency (E02.FR.01)
- Add TimeLimitReached mapping in mapStopReasonToOCPP20 for stations
  that need to report time-limited transaction stops

10 days agochore: release main (#1758) ocpp-server@v4.0.0 simulator@v4.0.0 webui@v4.0.0
Jérôme Benoit [Sat, 28 Mar 2026 23:31:23 +0000 (00:31 +0100)] 
chore: release main (#1758)

* chore: release main

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
11 days agochore(deps): lock file maintenance
Jérôme Benoit [Sat, 28 Mar 2026 23:20:46 +0000 (00:20 +0100)] 
chore(deps): lock file maintenance

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
11 days agorefactor: harmonize connector iteration to use iterateConnectors() across prod and...
Jérôme Benoit [Sat, 28 Mar 2026 23:18:42 +0000 (00:18 +0100)] 
refactor: harmonize connector iteration to use iterateConnectors() across prod and tests

Unify all connector/EVSE iteration patterns behind the iterateConnectors()
generator API, eliminating duplicated if(hasEvses)/else branching in both
production and test code.

Production:
- Refactor getNumberOfConnectors(), hasConnector(),
  getNumberOfReservationsOnConnectorZero() to use iterateConnectors()

Tests:
- Harmonize 10 mock methods in StationHelpers.ts to match production patterns
- Collapse OCPP20TestUtils resetConnectorTransactionState from dual-path to
  single iterateConnectors() loop
- Replace direct evseStatus.connectors map access with getConnectorStatus()
  and getConnectorIdByEvseId() helpers across 4 test files
- Remove redundant EVSE cleanup loops in standardCleanup and fullResetStation
- Align getReservationBy mock signature to production types (ReservationKey)
- Fix alphabetical ordering of mock methods

11 days agorefactor: encapsulate connector/EVSE iteration behind generator API
Jérôme Benoit [Sat, 28 Mar 2026 20:15:44 +0000 (21:15 +0100)] 
refactor: encapsulate connector/EVSE iteration behind generator API

Introduce iterateConnectors() and iterateEvses() generators on
ChargingStation, replacing ~30 duplicated if(hasEvses)/else iteration
patterns across the codebase. Make connectors and evses Maps private
to enforce usage of the new API.

- Add ConnectorEntry and EvseEntry as unified types for both iteration
  and serialization, removing duplicate ConnectorDataEntry/EvseDataEntry
- Add hasEvse() accessor method for EVSE existence checks
- Fix remoteStartId not propagated in TransactionEvent (F01.FR.25)
- Fix messagesInQueue in GetTransactionStatus to check actual queue
- Fix OCPP 1.6 changeAvailability losing SCHEDULED response
- Consolidate ad-hoc test mock factories to use createMockChargingStation
- Update UI wire format and components for unified types

BREAKING CHANGE: connectors and evses Maps are now private on
ChargingStation. Use iterateConnectors(), iterateEvses(), hasEvse(),
getEvseStatus(), getConnectorStatus() instead of direct Map access.

11 days agochore(webui): update screenshot
Jérôme Benoit [Sat, 28 Mar 2026 17:12:20 +0000 (18:12 +0100)] 
chore(webui): update screenshot

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
11 days agotest: isolate readCombinedLog tests from real logs directory
Jérôme Benoit [Sat, 28 Mar 2026 16:37:58 +0000 (17:37 +0100)] 
test: isolate readCombinedLog tests from real logs directory

Replace direct writes to the project's logs/ directory with temporary
directories and Configuration cache overrides, following the established
mkdtempSync/tmpdir pattern used in FileUtils and IdTagsCache tests.

11 days agotest: add symmetric Ended meter value tests for start/stop/zero-interval
Jérôme Benoit [Sat, 28 Mar 2026 16:15:58 +0000 (17:15 +0100)] 
test: add symmetric Ended meter value tests for start/stop/zero-interval

Cover startEndedMeterValues (timer + array init), stopEndedMeterValues
(timer clear), and interval=0 edge case (array init without timer).
Follows existing Updated test pattern for consistency.

11 days agofeat: implement TxEnded meter value accumulator per OCPP 2.0.1 spec §2.1
Jérôme Benoit [Sat, 28 Mar 2026 16:06:49 +0000 (17:06 +0100)] 
feat: implement TxEnded meter value accumulator per OCPP 2.0.1 spec §2.1

Per E06.FR.11 and J02.FR.10, TransactionEvent(Ended) must contain meter
values sampled every TxEndedInterval from transaction start, with the
final sample marked as Transaction.End context.

- Add TxEndedInterval to OCPP20RequiredVariableName enum
- Add start/stopEndedMeterValues and getTxEndedInterval
- Accumulate periodic samples during transaction, flush in Ended event
- Harmonize Updated/Ended naming across API, variables, and log messages
- Exclude transactionEndedMeterValues from serialized configuration
- Fix stopEndedMeterValues incorrectly called in startUpdatedMeterValues
- Restore JSDoc-free ConnectorStatus fields
- Fix DEFAULT_WS_PING_INTERVAL constant reference
- Update test fixtures and assertions for serialization coverage

11 days agostyle: apply prettier formatting to test files
Jérôme Benoit [Sat, 28 Mar 2026 14:45:15 +0000 (15:45 +0100)] 
style: apply prettier formatting to test files

Reformatting applied by lint-staged hooks during previous commits.
No behavioral changes.

11 days agofix: align ConnectionTimeOut semantics with OCPP spec
Jérôme Benoit [Sat, 28 Mar 2026 14:36:18 +0000 (15:36 +0100)] 
fix: align ConnectionTimeOut semantics with OCPP spec

ConnectionTimeOut/EVConnectionTimeOut is the EV cable insertion timeout
per OCPP 1.6 §9.1.6 and OCPP 2.0.1 TxCtrlr spec, not a WebSocket
timeout. Split DEFAULT_CONNECTION_TIMEOUT into three purpose-specific
constants:
- DEFAULT_EV_CONNECTION_TIMEOUT (180s): OCPP cable insertion timeout
- DEFAULT_WS_HANDSHAKE_TIMEOUT (30s): WebSocket handshake timeout
- DEFAULT_WS_RECONNECT_DELAY (30s): WebSocket reconnection delay
- DEFAULT_MESSAGE_TIMEOUT (30s): OCPP 2.0 MessageTimeout default

Dissociate WebSocket handshake/reconnect from OCPP ConnectionTimeOut
configuration. Initialize ConnectionTimeOut with 180s per spec.

11 days agorefactor: move ConfigurationValueSize, ReportingValueSize, ValueSize to OCPP20Optiona...
Jérôme Benoit [Sat, 28 Mar 2026 14:21:35 +0000 (15:21 +0100)] 
refactor: move ConfigurationValueSize, ReportingValueSize, ValueSize to OCPP20OptionalVariableName

Per OCPP 2.0.1 appendices CSV (dm_components_vars.csv), these three
DeviceDataCtrlr variables are required=no. Move them from
OCPP20RequiredVariableName to OCPP20OptionalVariableName to match
the spec classification.

11 days agorefactor: add SimulateSignatureVerificationFailure to OCPP20VendorVariableName enum
Jérôme Benoit [Sat, 28 Mar 2026 14:13:11 +0000 (15:13 +0100)] 
refactor: add SimulateSignatureVerificationFailure to OCPP20VendorVariableName enum

Replace string literal with enum reference in OCPP20VariableRegistry
and OCPP20IncomingRequestService for type safety and consistency with
CertificatePrivateKey and ConnectionUrl vendor variables.

11 days agorefactor: use OCPP20RequiredVariableName for measurands in OCPP20ServiceUtils
Jérôme Benoit [Sat, 28 Mar 2026 13:20:30 +0000 (14:20 +0100)] 
refactor: use OCPP20RequiredVariableName for measurands in OCPP20ServiceUtils

Replace StandardParametersKey with OCPP20RequiredVariableName for
TxStartedMeasurands and TxEndedMeasurands in buildTransaction*MeterValues.
Remove unused StandardParametersKey import.

11 days agorefactor: move debug param to last position in buildMeterValue
Jérôme Benoit [Sat, 28 Mar 2026 12:29:21 +0000 (13:29 +0100)] 
refactor: move debug param to last position in buildMeterValue

Reorder buildMeterValue signature so debug is the last optional
parameter, consistent with all other validate*MeasurandValue functions.
Removes need to pass explicit false to reach measurandsKey and context.

11 days agorefactor: replace Measurands string literal with OCPP20RequiredVariableName enum
Jérôme Benoit [Sat, 28 Mar 2026 12:05:34 +0000 (13:05 +0100)] 
refactor: replace Measurands string literal with OCPP20RequiredVariableName enum

Add Measurands to OCPP20RequiredVariableName enum (required per OCPP
2.0.1 Part 2 §2.7.1, AlignedDataCtrlr component) and replace all string
literals in ConfigurationKeyUtils mapping and OCPP20VariableRegistry.

11 days agofix: correct WebSocketPingInterval documentation and registry default
Jérôme Benoit [Sat, 28 Mar 2026 00:17:49 +0000 (01:17 +0100)] 
fix: correct WebSocketPingInterval documentation and registry default

- Remove vendor-specific ChargingStation.WebSocketPingInterval entry
  from README (already listed under OCPPCommCtrlr per OCPP 2.0.1 spec)
- Use Constants.DEFAULT_WEBSOCKET_PING_INTERVAL in OCPPCommCtrlr registry
  entry instead of hardcoded '30' for single source of truth

11 days agofix: map WebSocketPingInterval to OCPPCommCtrlr per OCPP 2.0.1 spec
Jérôme Benoit [Sat, 28 Mar 2026 00:09:34 +0000 (01:09 +0100)] 
fix: map WebSocketPingInterval to OCPPCommCtrlr per OCPP 2.0.1 spec

ChargingStation.WebSocketPingInterval is vendor-specific. The canonical
variable per OCPP 2.0.1 Part 4 §8.4 is OCPPCommCtrlr.WebSocketPingInterval.
Update both the 1.6→2.0 mapping and the keba-ocpp2 template.

11 days agotest: add whitespace-padded value coverage for convertToBoolean
Jérôme Benoit [Sat, 28 Mar 2026 00:02:08 +0000 (01:02 +0100)] 
test: add whitespace-padded value coverage for convertToBoolean

Align test coverage across root simulator and web UI components for
trim handling of ' true ', ' 1 ', ' false ', ' TRUE ', 'True', 'FALSE',
and numeric 2.

11 days agotest: restore and harmonize OCPP 2.0 transaction meter value assertions
Jérôme Benoit [Fri, 27 Mar 2026 23:50:22 +0000 (00:50 +0100)] 
test: restore and harmonize OCPP 2.0 transaction meter value assertions

- Add symmetric buildTransactionEndedMeterValues tests via requestStopTransaction
  (context=Transaction.End, measurand=Energy, no config key, no EVSE template)
- Strengthen buildTransactionStartedMeterValues assertions with strict
  context and measurand checks using OCPP enum constants
- Refactor Started tests: shared station setup in beforeEach, remove duplication
- Configure TxEndedMeasurands in deauthorization test for real coverage
- Remove comments that restate test names

12 days agofeat: support configurable measurands per transaction stage in OCPP 2.0
Jérôme Benoit [Fri, 27 Mar 2026 23:13:03 +0000 (00:13 +0100)] 
feat: support configurable measurands per transaction stage in OCPP 2.0

- Thread measurandsKey and context params through the meter value pipeline
  (getSampledValueTemplate, build*MeasurandValue, voltage helpers)
- buildTransactionStartedMeterValues uses SampledDataCtrlr.TxStartedMeasurands
  with Transaction.Begin context
- buildTransactionEndedMeterValues uses SampledDataCtrlr.TxEndedMeasurands
  with Transaction.End context
- Add OCPP 1.6→2.0 mappings for MeterValuesAlignedData,
  ClockAlignedDataInterval, StopTxnSampledData, StopTxnAlignedData
- Reorder getSampledValueTemplate params: measurandsKey before measurand
- Log warn (not debug) when meter value building fails in transaction events
- Add .trim() to convertToBoolean for whitespace-padded values

12 days agorefactor: extract normalized boolean variable in validation checks
Jérôme Benoit [Fri, 27 Mar 2026 21:50:32 +0000 (22:50 +0100)] 
refactor: extract normalized boolean variable in validation checks

12 days agofix: use case-insensitive boolean parsing for OCPP configuration values
Jérôme Benoit [Fri, 27 Mar 2026 21:40:34 +0000 (22:40 +0100)] 
fix: use case-insensitive boolean parsing for OCPP configuration values

- Replace strict string comparisons (=== 'true'/'false') with
  convertToBoolean() or .toLowerCase() across OCPP 1.6 and 2.0 stacks
- Add missing OCPP 1.6→2.0 key mappings for HeartbeatInterval,
  HeartBeatInterval, and WebSocketPingInterval
- Add missing readonly field to 4 chargex template configuration keys
- Add .trim() to convertToBoolean for whitespace-padded values

12 days agorefactor: harmonize errMsg → errorMsg for intra-file naming consistency
Jérôme Benoit [Fri, 27 Mar 2026 20:50:43 +0000 (21:50 +0100)] 
refactor: harmonize errMsg → errorMsg for intra-file naming consistency

OCPPServiceUtils.ts used both errMsg (4x) and errorMsg (1x) for the
same semantic. PerformanceStatistics.ts used errMsg (4x) while the
rest of the codebase uses errorMsg. Align to errorMsg everywhere.

12 days agochore: untrack accidentally committed test plan file
Jérôme Benoit [Fri, 27 Mar 2026 20:16:16 +0000 (21:16 +0100)] 
chore: untrack accidentally committed test plan file

12 days agorefactor: align variable naming and remove non-null assertions in tests and UI
Jérôme Benoit [Fri, 27 Mar 2026 20:15:44 +0000 (21:15 +0100)] 
refactor: align variable naming and remove non-null assertions in tests and UI

Apply the same naming conventions established in src/ to tests/ and ui/:
- connector → connectorStatus for ConnectorStatus values (48 instances)
- Stats → Statistics: heartbeatStats, authStats, rateLimitStats (3)
- Config → Configuration: atgConfig, originalConfig, updatedConfig,
  singleServerConfig, multiServerConfig (7)
- Remove 11 eslint-disable no-non-null-assertion suppressions in
  tests/ and ui/web/src/ with proper null guards

12 days agorefactor: align intermediate variable naming with codebase conventions
Jérôme Benoit [Fri, 27 Mar 2026 19:54:42 +0000 (20:54 +0100)] 
refactor: align intermediate variable naming with codebase conventions

Systematic rename of 24 variables to follow established patterns:
- connector → connectorStatus for ConnectorStatus values (8 decls + params)
- Stats → Statistics: workerScriptStats, strategyStats, cacheStats (5)
- Config → Configuration: newConfig, authConfig, logConfig, configData (7)
- entry → descriptive names: ipRateLimitEntry, authCacheEntry,
  rateLimitEntry, variableGroupEntry, pairedBoundsEntry (6)
- Fix variable shadowing in OCPP20ServiceUtils.startPeriodicMeterValues
  by naming outer setup variable initialConnectorStatus

12 days agodocs: align README OCPP 2.0.1 variable list with code registry
Jérôme Benoit [Fri, 27 Mar 2026 19:31:05 +0000 (20:31 +0100)] 
docs: align README OCPP 2.0.1 variable list with code registry

- Rename LocalAuthorizeOffline → LocalAuthorizationOffline and
  LocalPreAuthorize → LocalPreAuthorization to match code enum names
- Remove commented-out OrganizationName from ISO15118Ctrlr
- Add 7 implemented variables missing from documentation:
  AlignedDataCtrlr/TxEndedMeasurands, AuthCacheCtrlr/Storage,
  EVSE/Power, LocalAuthListCtrlr/Storage,
  SampledDataCtrlr/TxEndedMeasurands, SampledDataCtrlr/TxUpdatedMeasurands,
  SmartChargingCtrlr/RateUnit

12 days agochore: remove accidentally tracked test plan file
Jérôme Benoit [Fri, 27 Mar 2026 19:15:49 +0000 (20:15 +0100)] 
chore: remove accidentally tracked test plan file

12 days agorefactor: use Number.isNaN() instead of global isNaN()
Jérôme Benoit [Fri, 27 Mar 2026 19:13:36 +0000 (20:13 +0100)] 
refactor: use Number.isNaN() instead of global isNaN()

Replace global isNaN() with Number.isNaN() for stricter type checking.
Global isNaN() coerces the argument to a number first, which can produce
unexpected results for non-numeric strings.

12 days agofix: replace incorrect zero fallbacks for maximumPower and maximumAmperage
Jérôme Benoit [Fri, 27 Mar 2026 19:10:32 +0000 (20:10 +0100)] 
fix: replace incorrect zero fallbacks for maximumPower and maximumAmperage

Station physical properties (maximumPower, maximumAmperage) are validated
> 0 at startup. Using ?? 0 as fallback silently produces invalid states:
0W power limits blocking all charging, 0A amperage in config keys.

Replace with proper null guards that log errors and fail-open:
- getConnectorMaximumAvailablePower: return Infinity (no limit known)
- getMaximumAmperage: return undefined (caller already handles it)
- initializeOcppConfiguration: skip config key, log error
- getChargingStationChargingProfilesLimit: return limit uncapped
- getConnectorChargingProfilesLimit: return limit uncapped

12 days agorefactor: eliminate all non-null assertion suppressions with proper null guards
Jérôme Benoit [Fri, 27 Mar 2026 18:50:01 +0000 (19:50 +0100)] 
refactor: eliminate all non-null assertion suppressions with proper null guards

Remove 104 eslint-disable-next-line @typescript-eslint/no-non-null-assertion
suppressions across 23 files by replacing non-null assertions (!) with
semantically correct alternatives: local variable extraction with null
guards, optional chaining (?.), nullish coalescing (??) with proper
default constants, and error logging for invalid states.

Key improvements:
- Use existing DEFAULT_POOL_MAX_SIZE, DEFAULT_POOL_MIN_SIZE, and
  DEFAULT_ELEMENTS_PER_WORKER constants instead of hardcoded fallbacks
- Add proper error logging for powerDivider undefined/invalid states
  instead of silently computing wrong values
- Add connectorId null guard with throw in handleMeterValues instead of
  silently defaulting to connector 0
- Ensure .finally() always sends a response to prevent caller hangs
- Align variable naming with codebase conventions (connectorStatus,
  templateStatistics, commandStatisticsData, entryStatisticsData)

12 days agorefactor: deduplicate imports
Jérôme Benoit [Fri, 27 Mar 2026 16:44:08 +0000 (17:44 +0100)] 
refactor: deduplicate imports

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
12 days agorefactor: use `OCPP20MessageFormatEnumType` instead of string literal union
Jérôme Benoit [Fri, 27 Mar 2026 16:43:03 +0000 (17:43 +0100)] 
refactor: use `OCPP20MessageFormatEnumType` instead of string literal union

12 days agofix(deps): add security overrides for brace-expansion, picomatch, smol-toml
Jérôme Benoit [Fri, 27 Mar 2026 16:32:44 +0000 (17:32 +0100)] 
fix(deps): add security overrides for brace-expansion, picomatch, smol-toml

12 days agorefactor: make `eventType` required in `OCPP20TransactionEventOptions`
Jérôme Benoit [Fri, 27 Mar 2026 16:31:41 +0000 (17:31 +0100)] 
refactor: make `eventType` required in `OCPP20TransactionEventOptions`

12 days agochore: lock file maintenance
Jérôme Benoit [Fri, 27 Mar 2026 16:13:19 +0000 (17:13 +0100)] 
chore: lock file maintenance

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
12 days agorefactor: use `JsonObject` and generic `OCPPAuthAdapter<TVersionId>` in auth module
Jérôme Benoit [Fri, 27 Mar 2026 16:07:17 +0000 (17:07 +0100)] 
refactor: use `JsonObject` and generic `OCPPAuthAdapter<TVersionId>` in auth module

12 days agorefactor: type deprecated config key maps with `keyof` to eliminate 4 casts
Jérôme Benoit [Fri, 27 Mar 2026 15:28:26 +0000 (16:28 +0100)] 
refactor: type deprecated config key maps with `keyof` to eliminate 4 casts

12 days agorefactor: centralize handler type bridges in OCPP base classes
Jérôme Benoit [Fri, 27 Mar 2026 15:18:10 +0000 (16:18 +0100)] 
refactor: centralize handler type bridges in OCPP base classes

12 days agorefactor: eliminate explicit `any` from `once()` and `toHandler()` with proper generics
Jérôme Benoit [Fri, 27 Mar 2026 14:56:48 +0000 (15:56 +0100)] 
refactor: eliminate explicit `any` from `once()` and `toHandler()` with proper generics

12 days agorefactor: type buildTransactionEvent with OCPP20TransactionEventOptions
Jérôme Benoit [Fri, 27 Mar 2026 14:30:15 +0000 (15:30 +0100)] 
refactor: type buildTransactionEvent with OCPP20TransactionEventOptions

Replace the untyped Record<string, unknown> cast in buildTransactionEvent
with the proper OCPP20TransactionEventOptions parameter type. Add build
hint fields to the options interface. Remove 64 stale as-unknown-as casts
from transaction event tests.

12 days agorefactor: accept enum types in registry/manager signatures, remove 429 redundant...
Jérôme Benoit [Fri, 27 Mar 2026 13:39:32 +0000 (14:39 +0100)] 
refactor: accept enum types in registry/manager signatures, remove 429 redundant as-string casts

Widen buildRegistryKey, buildCaseInsensitiveCompositeKey, and
VariableMetadata.component to accept OCPP20ComponentName and VariableName
enums directly, eliminating 429 as-string casts across the registry and
variable manager. 8 casts retained on enum comparison lines required by
no-unsafe-enum-comparison lint rule.

12 days agofix(deps): update all non-major dependencies (#1759)
renovate[bot] [Fri, 27 Mar 2026 13:26:42 +0000 (14:26 +0100)] 
fix(deps): update all non-major dependencies (#1759)

* fix(deps): update all non-major dependencies

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
12 days agorefactor: consolidate enforceMessageLimits types with generic R and RejectionReason
Jérôme Benoit [Fri, 27 Mar 2026 13:23:14 +0000 (14:23 +0100)] 
refactor: consolidate enforceMessageLimits types with generic R and RejectionReason

- Add generic type parameter R to enforceMessageLimits and
  enforcePostCalculationBytesLimit, eliminating 4 'as typeof' casts
- Extract RejectionReason interface (additionalInfo + reasonCode) to
  replace inline anonymous types, aligned with StatusInfoType fields
- Remove all enum→string→enum round-trips in callers

12 days agofix: type buildRejected reasonCode as ReasonCodeEnumType instead of string
Jérôme Benoit [Fri, 27 Mar 2026 12:55:08 +0000 (13:55 +0100)] 
fix: type buildRejected reasonCode as ReasonCodeEnumType instead of string

Eliminates the pointless enum→string→enum round-trip: the callback
parameter is now typed as ReasonCodeEnumType directly, removing the
as-string casts at call sites and the reverse keyof-typeof reconversion
in callers.

12 days agofeat: enrich OCPP 2.0 template with OCPP 1.6 equivalent config keys
Jérôme Benoit [Fri, 27 Mar 2026 12:46:34 +0000 (13:46 +0100)] 
feat: enrich OCPP 2.0 template with OCPP 1.6 equivalent config keys

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

12 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Fri, 27 Mar 2026 11:34:09 +0000 (11:34 +0000)] 
[autofix.ci] apply automated fixes

12 days agofix: use Component.Variable[.Instance] key format for OCPP 2.0 variable persistence
Jérôme Benoit [Fri, 27 Mar 2026 11:31:06 +0000 (12:31 +0100)] 
fix: use Component.Variable[.Instance] key format for OCPP 2.0 variable persistence

The previous format used bare variable names (e.g., 'Enabled'), causing
collisions across components (9 components define Enabled, 8+ define
Available, etc.). Now uses the spec's (Component, Variable, Instance)
triplet as the persisted configuration key.

- Rewrite computeConfigurationKeyName to return Component.Variable format
- Remove shouldFlattenInstance (no longer needed)
- Extract buildConfigKey helper in ConfigurationKeyUtils, export via barrel
- Add MaxCertificateChainSize to OCPP20OptionalVariableName enum
- Update all direct config key lookups with component prefix
- Update keba-ocpp2 template keys to new format
- Update OCPP2_PARAMETER_KEY_MAP resolved values
- Replace all enum string literals in log messages with enum references
- Update all tests to use new key format

13 days agofeat: log once per key when OCPP 1.6 configuration key is remapped to OCPP 2.0 variable
Jérôme Benoit [Thu, 26 Mar 2026 23:09:30 +0000 (00:09 +0100)] 
feat: log once per key when OCPP 1.6 configuration key is remapped to OCPP 2.0 variable

13 days agofix: remove OrganizationName from ISO15118Ctrlr (spec says SecurityCtrlr only)
Jérôme Benoit [Thu, 26 Mar 2026 22:55:28 +0000 (23:55 +0100)] 
fix: remove OrganizationName from ISO15118Ctrlr (spec says SecurityCtrlr only)

Per OCPP 2.0.1 appendix section 3.1.15, OrganizationName belongs to
SecurityCtrlr only. The duplicate entry under ISO15118Ctrlr was not
in the spec and shadowed the SecurityCtrlr default value.

13 days agorefactor: add Enabled to OCPP20RequiredVariableName enum and fix LocalAuthListCtrlr...
Jérôme Benoit [Thu, 26 Mar 2026 22:25:20 +0000 (23:25 +0100)] 
refactor: add Enabled to OCPP20RequiredVariableName enum and fix LocalAuthListCtrlr.Enabled mapping

Replace all 'Enabled' string literals with OCPP20RequiredVariableName.Enabled
in the variable registry and auth adapter. Move LocalAuthListEnabled
default from AuthCtrlr to LocalAuthListCtrlr component per OCPP 2.0.1 spec.

13 days agofix: align OCPP 2.0 variable names to spec (LocalPreAuthorization, LocalAuthorization...
Jérôme Benoit [Thu, 26 Mar 2026 22:09:05 +0000 (23:09 +0100)] 
fix: align OCPP 2.0 variable names to spec (LocalPreAuthorization, LocalAuthorizationOffline)

Rename LocalPreAuthorize → LocalPreAuthorization and
LocalAuthorizeOffline → LocalAuthorizationOffline to match the OCPP
2.0.1 appendix. Add key resolution mapping for both. Replace string
literals with enum references in OCPP20AuthAdapter.

13 days agochore: remove test plan from git tracking
Jérôme Benoit [Thu, 26 Mar 2026 21:43:28 +0000 (22:43 +0100)] 
chore: remove test plan from git tracking

13 days agofeat: resolve OCPP 1.6 configuration keys to OCPP 2.0 equivalents transparently
Jérôme Benoit [Thu, 26 Mar 2026 21:42:53 +0000 (22:42 +0100)] 
feat: resolve OCPP 1.6 configuration keys to OCPP 2.0 equivalents transparently

Add resolveKey() inside ConfigurationKeyUtils that maps OCPP 1.6 key
names to their OCPP 2.0 equivalents (MeterValuesSampledData →
TxUpdatedMeasurands, ConnectionTimeOut → EVConnectionTimeOut, etc.)
transparently in get/add/set/delete operations. Remap keba-ocpp2
template keys and add missing OCPP 2.0 variables. Add exhaustive
tests for key resolution across all OCPP versions.

13 days agofix: use OCPP version-specific parameter keys for meter value measurands
Jérôme Benoit [Thu, 26 Mar 2026 20:35:50 +0000 (21:35 +0100)] 
fix: use OCPP version-specific parameter keys for meter value measurands

Remap keba-ocpp2 template configuration keys from OCPP 1.6 names to
OCPP 2.0 variable names (MeterValuesSampledData → TxUpdatedMeasurands,
MeterValueSampleInterval → TxUpdatedInterval, AuthorizeRemoteTxRequests
→ AuthorizeRemoteStart). Remove OCPP 1.6-only keys with no 2.0
equivalent. Use StandardParametersKey union to resolve the correct
key at runtime based on OCPP version.

13 days agofix(docs): use bold instead of italic for vendor-specific markers to avoid underscore...
Jérôme Benoit [Thu, 26 Mar 2026 20:03:00 +0000 (21:03 +0100)] 
fix(docs): use bold instead of italic for vendor-specific markers to avoid underscore conflicts

13 days agodocs: list OCPP 2.0 device model variables by component in README
Jérôme Benoit [Thu, 26 Mar 2026 20:00:03 +0000 (21:00 +0100)] 
docs: list OCPP 2.0 device model variables by component in README

Replace the incorrect GetVariables/SetVariables command listing with
141 device model variables across 19 components extracted from
OCPP20VariableRegistry.ts. Vendor-specific variables are marked.
Matches the structure used for OCPP 1.6 configuration keys.

13 days agochore: release main (#1751) ocpp-server@v3.4.0 simulator@v3.4.0 v3 v3.4 webui@v3.4.0
Jérôme Benoit [Thu, 26 Mar 2026 19:31:47 +0000 (20:31 +0100)] 
chore: release main (#1751)

* chore: release main

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
13 days agofeat(ocpp2): add configurable firmware signature verification simulation (L01.FR...
Jérôme Benoit [Thu, 26 Mar 2026 19:06:53 +0000 (20:06 +0100)] 
feat(ocpp2): add configurable firmware signature verification simulation (L01.FR.03/L01.FR.04) (#1757)

* feat(ocpp2): add FirmwareCtrlr component and signature verification variable

* test(ocpp2): add firmware signature verification failure tests

- Add test for InvalidSignature path when SimulateSignatureVerificationFailure is true
- Add test for normal path when SimulateSignatureVerificationFailure is false (TDD red phase)
- Tests verify lifecycle termination and SecurityEventNotification type correctness
- Evidence: typecheck & lint pass; tests fail as expected (awaiting Task 3 implementation)

* feat(ocpp2): implement configurable firmware signature verification simulation

* docs: update README firmware signature verification note

* fix(ocpp2): address PR review feedback on firmware signature verification

- Await sendSecurityEventNotification in failure path for consistency with success path
- Reorder test assertions: check array length before index access
- Add tick-forward verification that lifecycle stops after InvalidSignature
- Remove redundant standardCleanup() calls already handled by afterEach
- Remove firmware signature verification note from README

* fix(ocpp2): improve firmware update spec conformance and test quality

- L01.FR.07: Move EVSE unavailability check inside wait loop for continuous monitoring
- L01.FR.30: Honor retries/retryInterval with simulated download retry cycle
- Merge retry test into existing DownloadFailed test (DRY)
- Add EVSE continuous monitoring lifecycle test (L01.FR.07)
- Fix assertion ordering: length checks before index access
- Remove as-unknown-as-string cast in favor of String()
- Tighten assert.ok to assert.notStrictEqual where applicable

* fix(tests): type CapturedOCPPRequest.command as OCPP20RequestCommand

- Replace string type with OCPP20RequestCommand enum on CapturedOCPPRequest.command
- Remove 6 unnecessary 'as string' casts across UpdateFirmware and TransactionEvent tests
- Replace 'Unavailable' string literal with OCPP20ConnectorStatusEnumType.Unavailable
- Remove unnecessary optional chaining on non-nullable payload
- Complete @param JSDoc for retries/retryInterval on simulateFirmwareUpdateLifecycle

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* fix(tests): replace flaky sentRequests assertions with mock.method spy in EVSE monitoring test

Mock sendEvseStatusNotifications directly instead of asserting on fire-and-forget
side effects captured in sentRequests. The internal async chain (sendAndSetConnectorStatus
→ dynamic import → requestHandler) needed non-deterministic flushMicrotasks() stacking.
The spy makes assertions synchronous and CI-deterministic.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
13 days agofix: respect elementAddDelay by using sequential startup when configured
Jérôme Benoit [Thu, 26 Mar 2026 16:42:21 +0000 (17:42 +0100)] 
fix: respect elementAddDelay by using sequential startup when configured

The perf commit 218fd550 switched to parallel Promise.allSettled for
startup speed, but broke elementAddDelay since all stations launched
simultaneously. Now uses sequential await loop when elementAddDelay > 0
and parallel allSettled when 0 or unset. Removes redundant outer
try/catch — config errors propagate as fatal, station errors are
handled per-station in both paths.

13 days agofix(deps): update all non-major dependencies (#1755)
renovate[bot] [Thu, 26 Mar 2026 11:42:37 +0000 (12:42 +0100)] 
fix(deps): update all non-major dependencies (#1755)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
13 days agochore: remove test plan from git tracking
Jérôme Benoit [Thu, 26 Mar 2026 00:03:45 +0000 (01:03 +0100)] 
chore: remove test plan from git tracking

13 days agofix: cleanup connector after queued TransactionEvent.Ended replay
Jérôme Benoit [Thu, 26 Mar 2026 00:02:01 +0000 (01:02 +0100)] 
fix: cleanup connector after queued TransactionEvent.Ended replay

Extract cleanupEndedTransaction() in OCPP20ServiceUtils to consolidate
stopPeriodicMeterValues + resetConnectorStatus + unlock + Available
status. Called from both stopTransaction20 (normal flow) and
sendQueuedTransactionEvents (offline replay). Fixes stale Occupied
status and orphaned meter value interval after queued Ended events.

2 weeks agotest(webui): harmonize test stubs, remove AAA comments, add composable and StateButto...
Jérôme Benoit [Wed, 25 Mar 2026 22:41:36 +0000 (23:41 +0100)] 
test(webui): harmonize test stubs, remove AAA comments, add composable and StateButton tests

2 weeks agofeat: add server-side refresh notification over WebSocket
Jérôme Benoit [Wed, 25 Mar 2026 22:15:51 +0000 (23:15 +0100)] 
feat: add server-side refresh notification over WebSocket

Bootstrap calls scheduleClientNotification() after each cache mutation.
A 500ms debounce collapses rapid updates into a single broadcast of
ProtocolNotification [ServerNotification.REFRESH] to all connected WS
clients. setChargingStationData/deleteChargingStationData return boolean
so callers only notify on actual change. Frontend responseHandler uses
isServerNotification/isProtocolResponse guards to dispatch messages.

2 weeks agofix: post updated message on connectorStatusChanged to fix stale UI cache
Jérôme Benoit [Wed, 25 Mar 2026 21:20:43 +0000 (22:20 +0100)] 
fix: post updated message on connectorStatusChanged to fix stale UI cache

The .finally() in internalSendMessage emits the updated event BEFORE
sendAndSetConnectorStatus updates connectorStatus.status, causing the
UI cache to snapshot the old status. The connectorStatusChanged event
fires AFTER the update, so posting an updated message on it ensures
the cache receives the correct connector status.

2 weeks agorefactor(webui): extract useExecuteAction composable to eliminate duplication
Jérôme Benoit [Wed, 25 Mar 2026 20:55:02 +0000 (21:55 +0100)] 
refactor(webui): extract useExecuteAction composable to eliminate duplication

2 weeks agorefactor(webui): use useChargingStations composable in ChargingStationsView
Jérôme Benoit [Wed, 25 Mar 2026 20:36:53 +0000 (21:36 +0100)] 
refactor(webui): use useChargingStations composable in ChargingStationsView

2 weeks agorefactor(webui): add StateButton, centralize active style, fix refresh lifecycle
Jérôme Benoit [Wed, 25 Mar 2026 20:30:03 +0000 (21:30 +0100)] 
refactor(webui): add StateButton, centralize active style, fix refresh lifecycle

- StateButton: stateless toggle driven by server state (active prop)
- Button: owns .button--active CSS, ToggleButton/StateButton pass :active
- Conditional buttons: show Start/Stop, Open/Close, Lock/Unlock based on state
- refreshChargingStations(): shared composable eliminates 4x duplicated refresh
- executeAction() helper in CSData/CSConnector: DRY action/toast/refresh
- Fix: trigger getChargingStations() on need-refresh event
- Fix: refresh after AddChargingStations, StartTransaction, SetSupervisionUrl
- table-layout: fixed on connectors table to prevent overflow

2 weeks agochore(deps): update dependency typescript to v6 (#1750)
renovate[bot] [Wed, 25 Mar 2026 18:59:13 +0000 (19:59 +0100)] 
chore(deps): update dependency typescript to v6 (#1750)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 weeks agofix(webui): add table-layout fixed to connectors table to prevent overflow on action...
Jérôme Benoit [Wed, 25 Mar 2026 18:56:25 +0000 (19:56 +0100)] 
fix(webui): add table-layout fixed to connectors table to prevent overflow on action panel open

2 weeks agofeat: add connector cable retention lock/unlock simulation (#1754)
Jérôme Benoit [Wed, 25 Mar 2026 18:20:36 +0000 (19:20 +0100)] 
feat: add connector cable retention lock/unlock simulation (#1754)

* feat(types): add connector lock simulation types and UI protocol enums

* feat(core): add connector lock/unlock simulation methods

* feat(ocpp): set lock state on UnlockConnector command for both OCPP stacks

* feat(ui-server): add lock/unlock broadcast channel commands and MCP schemas

* feat(ui): add lock/unlock buttons to web UI

* test: add connector lock/unlock simulation tests

* docs: add connector lock/unlock simulation documentation

* feat(ocpp): auto-lock connector on transaction start, auto-unlock on normal termination

* fix(ui): reorder Locked column before Transaction in connector table

* fix: guard lock unlock on accepted stop, emit updated event, add null status warnings, fix resetConnectorStatus test

* [autofix.ci] apply automated fixes

* fix(ocpp): only auto-lock connector on accepted transaction start in OCPP 2.0.1

* fix(core): emit connectorStatusChanged instead of updated on lock state change

* test: add idempotency tests for lockConnector and unlockConnector

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2 weeks agofix(deps): update all non-major dependencies (#1753)
renovate[bot] [Wed, 25 Mar 2026 13:51:33 +0000 (14:51 +0100)] 
fix(deps): update all non-major dependencies (#1753)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 weeks agofeat(ocpp-server): enhance OCPP 2.0.1 mock server for comprehensive E2E testing ...
Jérôme Benoit [Wed, 25 Mar 2026 11:38:55 +0000 (12:38 +0100)] 
feat(ocpp-server): enhance OCPP 2.0.1 mock server for comprehensive E2E testing (#1752)

* feat(ocpp-server): add boot status sequence for Pending→Accepted transition testing

* feat(ocpp-server): add configurable TriggerMessage type

* feat(ocpp-server): add configurable Reset type and ChangeAvailability status

* feat(ocpp-server): add command sequencing for multi-step CSMS workflows

* feat(ocpp-server): add groupIdToken and cacheExpiry to Authorize response

* feat(ocpp-server): add multi-variable SetVariables/GetVariables CLI support

* fix(ocpp-server): share boot_index across reconnections so sequence advances correctly

* fix(ocpp-server): schedule send_commands as background task so it runs after cp.start()

* fix(ocpp-server): correct CLI help text casing and validate boot-status-sequence

* chore: remove E2E test plan from PR branch

* refactor(ocpp-server): fix misplaced tests and harmonize style

* docs(ocpp-server): document all new CLI flags and transaction tracking

* fix(ocpp-server): harden input validation and connection cleanup

* fix(ocpp-server): clean up auth-mode choices and CLI edge cases

* fix(ocpp-server): validate cache-expiry, skip trailing commas, document yield semantics

* fix(ocpp-server): harmonize empty-entry handling and wrap CLI parsers in error handler

2 weeks agorefactor: use try/catch instead of .catch() for stopMessageSequence error handling
Jérôme Benoit [Tue, 24 Mar 2026 21:21:21 +0000 (22:21 +0100)] 
refactor: use try/catch instead of .catch() for stopMessageSequence error handling

2 weeks agofix: add OCPP 2.0 DataTransfer outgoing support and B03 boot retry in Rejected state
Jérôme Benoit [Tue, 24 Mar 2026 19:40:46 +0000 (20:40 +0100)] 
fix: add OCPP 2.0 DataTransfer outgoing support and B03 boot retry in Rejected state

Add DATA_TRANSFER to OCPP20RequestCommand enum, buildRequestPayload
passthrough, schema registration, and response handler (P02.FR.02).

Allow BootNotification retry when station is in Rejected state by
adding inRejectedState() to the internalSendMessage gate (B03.FR.06).

Fix mock server: use OCPPCommCtrlr component for HeartbeatInterval
in SetVariables, add customerIdentifier in CustomerInformation.

Add unit tests for promiseWithTimeout and DataTransfer outgoing.

2 weeks agofix: prevent shutdown timeout with promiseWithTimeout helper
Jérôme Benoit [Tue, 24 Mar 2026 18:08:34 +0000 (19:08 +0100)] 
fix: prevent shutdown timeout with promiseWithTimeout helper

Extract a shared promiseWithTimeout<T>() utility from 3 divergent
Promise.race+timeout implementations. Use it in ChargingStation.stop()
to cap stopMessageSequence at 30s so the stopped event is always
emitted even when the OCPP server is unreachable, preventing the
60s Bootstrap shutdown timeout.

Also fix mock server SecurityEventNotification handler parameter name
and add OCPP 2.0.1 E2E test plan.

2 weeks agodocs: fix inaccurate @returns in getChargingProfilesLimit
Jérôme Benoit [Tue, 24 Mar 2026 17:01:10 +0000 (18:01 +0100)] 
docs: fix inaccurate @returns in getChargingProfilesLimit

Return type can be undefined but JSDoc only documented the non-null case.

2 weeks agodocs: harmonize JSDoc @param/@returns across entire codebase
Jérôme Benoit [Tue, 24 Mar 2026 16:54:43 +0000 (17:54 +0100)] 
docs: harmonize JSDoc @param/@returns across entire codebase

- Add missing dash separator to @param tags in OCPP20VariableRegistry
  and OCPPServiceUtils (35 occurrences)
- Fill empty @param descriptions in Helpers, IdTagsCache,
  OCPPRequestService, and Worker modules (23 occurrences)
- Replace type-only @returns with meaningful descriptions

2 weeks agodocs: add JSDoc to ChargingStation public API methods
Jérôme Benoit [Tue, 24 Mar 2026 16:45:25 +0000 (17:45 +0100)] 
docs: add JSDoc to ChargingStation public API methods

Add concise JSDoc with @param and @returns tags to 29 public methods
following the project's established pattern (param - description).
Resolves all 45 jsdoc/require-param and jsdoc/require-returns warnings.

2 weeks agorefactor: eliminate remaining non-null assertions in ChargingStation and OCPP16Servic...
Jérôme Benoit [Tue, 24 Mar 2026 16:26:31 +0000 (17:26 +0100)] 
refactor: eliminate remaining non-null assertions in ChargingStation and OCPP16ServiceUtils

- ChargingStation: replace 8 non-null assertions with optional chaining,
  nullish coalescing defaults, and null guards
- OCPP16ServiceUtils: add type narrowing guards for composite charging
  schedules, extract connector status variables with null checks,
  replace all 14 non-null assertions with safe access patterns

Zero non-null assertions remain across the entire src/ directory.

2 weeks agorefactor: eliminate non-null assertions in 8 files (31 of 53 total)
Jérôme Benoit [Tue, 24 Mar 2026 16:19:37 +0000 (17:19 +0100)] 
refactor: eliminate non-null assertions in 8 files (31 of 53 total)

- AutomaticTransactionGenerator: extract connector status variables with
  null guards, use optional chaining for stationInfo and ATG config
- ChargingStation: replace stationInfo! with null guards and ?./?? defaults
- Helpers: replace stationInfo!.maximumPower! with ?./?? defaults
- IdTagsCache: replace stationInfo!.hashId with optional chaining
- ChargingStationWorkerBroadcastChannel: replace stationInfo!.hashId
- OCPP16IncomingRequestService: wrap firmware/diagnostics status in
  null guards, replace resetTime!/configurationKey! with safe access
- UIServerFactory: replace options!.host! with optional chaining
- Configuration: replace getConfigurationData()! with null guards

2 weeks agorefactor: audit-driven improvements — use BaseError, fix barrel bypass, remove redund...
Jérôme Benoit [Tue, 24 Mar 2026 14:51:21 +0000 (15:51 +0100)] 
refactor: audit-driven improvements — use BaseError, fix barrel bypass, remove redundant console

- Replace throw new Error with BaseError in OCPP20CertificateManager and
  OCPP20VariableManager (3 occurrences)
- Move getMessageTypeString from OCPPServiceUtils to Helpers, resolving
  the barrel bypass in ErrorUtils — consumers import via charging-station
  barrel, tests moved to Helpers.test.ts
- Remove redundant console.warn in Helpers (logger.warn already called)
- Remove unused chalk import from Helpers

2 weeks agorefactor: audit-driven quick wins — fix assert.ok misuse, naming conventions, and...
Jérôme Benoit [Tue, 24 Mar 2026 13:46:17 +0000 (14:46 +0100)] 
refactor: audit-driven quick wins — fix assert.ok misuse, naming conventions, and hardcoded defaults

- Replace 71 improper assert.ok(comparison) with assert.strictEqual/notStrictEqual
  in 10 test files per TEST_STYLE_GUIDE (assert.ok for boolean/existence only)
- Rename lastUpdated → lastUpdatedDate in auth interfaces and strategies
  to follow Date suffix naming convention
- Extract hardcoded 'ws://localhost' to OCPP20Constants.DEFAULT_CONNECTION_URL
- Replace setTimeout(resolve, 50) with proper httpServer.close() await
  in UIMCPServer integration test

2 weeks agofix(webui): remove deprecated baseUrl for TypeScript 6 compatibility
Jérôme Benoit [Tue, 24 Mar 2026 11:47:53 +0000 (12:47 +0100)] 
fix(webui): remove deprecated baseUrl for TypeScript 6 compatibility

TS6 deprecated baseUrl (TS5101). Since paths entries already use './'
prefix, they work standalone without baseUrl. Vite resolve.alias handles
runtime resolution independently.

2 weeks agorefactor(ocpp-server): audit-driven test improvements
Jérôme Benoit [Tue, 24 Mar 2026 11:20:08 +0000 (12:20 +0100)] 
refactor(ocpp-server): audit-driven test improvements

- Extract mock_valid_ws and rate_limit_charge_point fixtures (DRY)
- Replace inline blacklist AuthConfig with existing fixture
- Strengthen log assertions using caplog.records with level checks
- Add edge case tests: empty token in whitelist/blacklist modes
- Add test for unknown transaction event type (case _ branch)
Coverage: 90.18% → 90.98%, 159 → 162 tests.

2 weeks agorefactor(ocpp-server): harmonize shutdown tests with project conventions
Jérôme Benoit [Tue, 24 Mar 2026 11:07:03 +0000 (12:07 +0100)] 
refactor(ocpp-server): harmonize shutdown tests with project conventions

Move fixture near other fixtures, replace _patch_main tuple return with
contextmanager, parametrize SIGINT/SIGTERM, remove dead code helpers,
merge two test classes into one.

2 weeks agotest(ocpp-server): add graceful shutdown tests covering signal handling
Jérôme Benoit [Tue, 24 Mar 2026 11:03:17 +0000 (12:03 +0100)] 
test(ocpp-server): add graceful shutdown tests covering signal handling

Cover SIGINT/SIGTERM handlers, double-signal force quit, Windows
fallback via call_soon_threadsafe, and shutdown timeout warning.
Coverage: 80% → 90%.

2 weeks agofeat(ocpp-server): add graceful shutdown with signal handling
Jérôme Benoit [Tue, 24 Mar 2026 10:44:28 +0000 (11:44 +0100)] 
feat(ocpp-server): add graceful shutdown with signal handling

Handle SIGINT/SIGTERM for clean WebSocket close (code 1001) to all
connected charge points. Includes shutdown drain timeout, double-signal
force quit, and cross-platform Windows fallback via call_soon_threadsafe.

2 weeks agotest(mcp): add OCPP schema mapping consistency tests
Jérôme Benoit [Tue, 24 Mar 2026 01:21:55 +0000 (02:21 +0100)] 
test(mcp): add OCPP schema mapping consistency tests

Verify that every broadcast channel OCPP command has a corresponding
MCP tool schema and OCPP schema mapping entry. Ensures schema coverage
stays in sync with supported commands as a single source of truth guard.

2 weeks agochore: release main (#1748) ocpp-server@v3.3.0 simulator@v3.3.0 v3.3 webui@v3.3.0
Jérôme Benoit [Tue, 24 Mar 2026 01:09:52 +0000 (02:09 +0100)] 
chore: release main (#1748)

* chore: release main

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2 weeks agofix(deps): update all non-major dependencies (#1749)
renovate[bot] [Tue, 24 Mar 2026 01:05:31 +0000 (02:05 +0100)] 
fix(deps): update all non-major dependencies (#1749)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 weeks agorefactor(mcp): split MCP definitions by spec concern — Resources, Tools, ToolSchemas
Jérôme Benoit [Tue, 24 Mar 2026 00:58:12 +0000 (01:58 +0100)] 
refactor(mcp): split MCP definitions by spec concern — Resources, Tools, ToolSchemas

Rename MCPResourceHandlers.ts to MCPResources.ts (contains only MCP
Resources: station-list, station-by-id, template-list, OCPP schemas).

Extract log tools (readCombinedLog, readErrorLog) and their helpers
(getLogFilePath, tailFile) into MCPTools.ts (MCP Tools per spec).

MCPToolSchemas.ts unchanged (tool schema definitions).

Directory now reflects MCP spec separation of concerns:
- MCPResources.ts  — MCP Resources (spec §Resources)
- MCPTools.ts      — MCP Tools with custom logic (spec §Tools)
- MCPToolSchemas.ts — Tool schema definitions for OCPP commands

2 weeks agorefactor(broadcast-channel): extract response status registries from switch/case
Jérôme Benoit [Tue, 24 Mar 2026 00:43:04 +0000 (01:43 +0100)] 
refactor(broadcast-channel): extract response status registries from switch/case

Replace 11 repetitive switch/case blocks in commandResponseToResponseStatus
with two static registries:
- emptyResponseCommands: Set of commands where empty response = success
- acceptedStatusCommands: Map of commands to status check predicates

Custom cases (AUTHORIZE, START/STOP_TRANSACTION, HEARTBEAT,
TRANSACTION_EVENT) remain in switch for version-specific logic.

Adding a new command now requires 1 line in the registry instead of
a 5-line case block.

2 weeks agorefactor(broadcast-channel): extract passthrough() factory, remove 20 identical handlers
Jérôme Benoit [Tue, 24 Mar 2026 00:39:40 +0000 (01:39 +0100)] 
refactor(broadcast-channel): extract passthrough() factory, remove 20 identical handlers

Replace 20 identical BroadcastChannel handler methods (handleAuthorize,
handleDataTransfer, handleHeartbeat, handleStatusNotification, etc.)
with a single passthrough(RequestCommand) factory method that creates
a CommandHandler forwarding the request payload to requestHandler.

Retain only handlers with custom logic: handleBootNotification (merges
bootNotificationRequest), handleMeterValues (version-specific meter
value building), handleStopTransaction (adds meterStop).

Net reduction: -208 lines.

2 weeks agorefactor(ocpp): extract buildEmptyMeterValue helper for empty MeterValue construction
Jérôme Benoit [Tue, 24 Mar 2026 00:16:06 +0000 (01:16 +0100)] 
refactor(ocpp): extract buildEmptyMeterValue helper for empty MeterValue construction

Replace 4 inline { sampledValue: [], timestamp: new Date() } constructions
with buildEmptyMeterValue() helper in OCPPServiceUtils and OCPP16ServiceUtils.

2 weeks agofeat(ui-server): add MCP transport and deprecate HTTP (#1746)
Jérôme Benoit [Mon, 23 Mar 2026 23:59:59 +0000 (00:59 +0100)] 
feat(ui-server): add MCP transport and deprecate HTTP (#1746)

* feat(types): add MCP application protocol type

* chore(deps): add @modelcontextprotocol/sdk and zod

* refactor(ui-server): deprecate UIHttpServer in favor of MCP transport

* feat(ui-server): add MCP tool zod schemas for all procedures

* feat(ui-server): add UIMCPServer transport and MCP resource handlers

* feat(ui-server): integrate UIMCPServer into UIServerFactory

* test(ui-server): add MCP factory integration tests

* test(ui-server): add UIMCPServer unit tests

* test(ui-server): add MCP endpoint integration tests and fix lint

* refactor(ui-server): extract HttpMethod enum to shared UIServerUtils

* docs: restructure UI Protocol section — MCP first, remove redundant API listing

* fix(ui-server): address PR review — security hardening and schema corrections

- Send 404 + destroy for non-/mcp requests instead of hanging socket
- Add rate limiting (429) matching UIHttpServer behavior
- Add body size limit (createBodySizeLimiter) to prevent DoS
- Register res.on('close') before handleRequest to prevent race
- Return 400 for JSON parse errors, 500 for server errors
- Fix setSupervisionUrl schema: supervisionUrl → url
- Fix stopTransaction schema: transactionIds string[] → transactionId number

* refactor(tests): use HttpMethod enum instead of string literals

* docs: remove redundant MCP discoverability statement

* docs: restructure MCP section for agent config, update uiServer type in config table

* feat(ui-server): log deprecation warning when HTTP transport is used

* fix(ui-server): harmonize log levels and message patterns with existing transports

* test(ui-server): align MCP tests with style guide

- Use createLoggerMocks() for log assertions instead of trivial checks
- Add server.stop() to factory fallback tests to prevent resource leaks
- Replace fragile globalThis.clearTimeout override with t.mock.method

* docs: restore PDU acronym definition in WebSocket Protocol section

* fix(ui-server): use UI protocol version for MCP server version

* fix(ui-server): derive log file paths from Configuration instead of hardcoding

* perf(ui-server): tail-read log files instead of loading entire file into memory

* refactor(ui-server): convert log resources to tools with tail parameter

Follow MCP best practices: resources are for static snapshots,
tools are for parameterized on-demand data.

- Replace log://combined and log://error resources with
  readCombinedLog and readErrorLog tools
- Add tail parameter (default 200, max 5000) following the
  filesystem server head/tail pattern
- Add readOnlyHint annotation per MCP tool spec
- Return totalLines metadata in response for context
- Keep station and template resources (small, static data)

* feat(ui-server): expose OCPP JSON schemas as MCP resource templates

Add schema://ocpp/{version} listing and schema://ocpp/{version}/{command}
resource templates that serve the existing JSON schema files from
src/assets/json-schemas/ocpp/ on demand.

LLM agents can discover available OCPP commands per version and read
the full JSON schema for any command without bloating tools/list.
Schemas are read from disk on demand — no memory duplication with
the AJV validators already loaded by OCPP services.

* feat(ui-server): add version-aware OCPP tool schemas with JSON Schema injection

- Add ocppSchemaMapping linking ProcedureName to JSON Schema files
- 16 OCPP tools now have ocpp16Payload/ocpp20Payload fields with
  version affinity in descriptions
- Intercept tools/list to inject full JSON Schemas from disk
  into inputSchema.properties (no Zod duplication, DRY)
- Add payload flattening: extract versioned payload, spread flat
- Add mutual exclusivity: reject if both payloads provided
- Add pre-flight version check: clear error on version mismatch
- Runtime guard: graceful fallback if SDK internal API changes

* refactor(ui-server): use OCPPVersion enum instead of string literals

* refactor(ui-server): improve MCP code elegance and project rule compliance

- Replace as-unknown-as cast with Reflect.get() for SDK handler access
- Replace generic Error with BaseError in stop/readRequestBody
- Extract createToolErrorResponse/createToolResponse static helpers (DRY)
- Extract closeTransportSafely for duplicate transport cleanup
- Extract registerLogReadTool factory for duplicate log tools (DRY)
- Remove unnecessary String() conversions in resource handlers

* fix(ui-server): fix body size double-counting and schema resource path

- readRequestBody: pass chunk.length (not accumulated total) to
  createBodySizeLimiter — matches UIHttpServer pattern
- getSchemaBaseDir: use correct relative path instead of broken
  resolve().includes('assets') condition (always true)

* refactor(ui-server): consolidate version fallback guards and clarify README version docs

* fix(ui-server): harden MCP schema resources against path traversal and fix transport leak

- Add path traversal guard in schema resource handlers to validate resolved
  paths stay within the OCPP schema base directory
- Close MCP transport on 405 Method Not Allowed responses to prevent leak
- Fix log file path to use direct path when rotation is disabled
- Fix misleading Heartbeat tool description that referenced unsupported
  ocpp payload fields

* fix(ui-server): address remaining review findings and lint warnings

- Reorder stop() to delete-before-reject preventing re-entrant issues
- Use ephemeral port in integration test instead of fixed port 18999
- Document synchronous authenticate() assumption and SDK pattern deviation
- Add 'modelcontextprotocol' to cspell wordlist
- Convert JSDoc to inline comment to fix jsdoc lint warnings

* fix(ui-server): exact MCP path match, pin SDK version, log schema injection

- Use exact pathname match (url.pathname !== '/mcp') instead of
  startsWith to prevent unintended paths from reaching the MCP handler
- Pin @modelcontextprotocol/sdk to ~1.27.1 (patch-only) since the
  OCPP schema injection relies on SDK internals pinned to 1.27.x
- Add startup log confirming successful OCPP JSON schema injection

* test(ui-server): add comprehensive UIMCPServer unit tests

Cover private method logic missing from initial test suite:
- invokeProcedure: service null, dual payload, version mismatch,
  direct response, service error, 30s timeout (mock timers)
- checkVersionCompatibility: OCPP 1.6/2.0/2.0.1 matching, cross-version
  errors, hashIds filtering, all-stations fallback
- readRequestBody: valid JSON, payload too large, invalid JSON, stream error
- loadOcppSchemas: disk loading, cache entry validation

Refactor for elegance and guide compliance:
- Centralize Reflect.get wrappers in TestableUIMCPServer class
- Extract assertToolError() helper to eliminate repeated triplet
- Move createMockChargingStationDataWithVersion() to UIServerTestUtils
- Replace magic number with DEFAULT_MAX_PAYLOAD_SIZE import

* fix(ui-server): per-request McpServer factory, async iterator body reader, transport leak

- Create a new McpServer per HTTP request via createMcpServer() factory
  to fix concurrency bug: McpServer.connect() overwrites a single
  internal _transport field, causing cross-talk under concurrent requests.
  OCPP schema cache and UI service are pre-loaded at startup; tool
  registration is ~12µs per request (negligible).

- Replace event-listener readRequestBody with for-await-of async
  iterator pattern. Single settlement guaranteed by language semantics,
  eliminating the double-reject bug on payload-too-large.

- Close transport on mcpServer.connect() failure to prevent resource leak.

- Clean up both transport and McpServer on response close via unified
  cleanup callback.

* fix: align StatusNotification field name with OCPP 2.0.1 schema, fix asset paths

- Use 'connectorStatus' (OCPP 2.0.1 schema field name) instead of
  'status' (non-standard) in OCPP20RequestService.buildRequestPayload
  and both call sites in OCPP20IncomingRequestService

- Fix esbuild-relative asset paths for OCPP JSON schemas and MCP
  resource handlers — import.meta.url resolves to dist/start.js,
  not the original source tree depth

* refactor(ocpp): align payload builders to object-param API, fix StatusNotification field names

- Refactor buildStatusNotificationRequest from positional params to
  object-based StatusNotificationRequest input. Builder accepts both
  'status' (internal/OCPP 1.6) and 'connectorStatus' (OCPP 2.0.1)
  with consistent fallback order (connectorStatus ?? status).

- Refactor buildTransactionEvent from 3 overloads to single
  object-based OCPP20TransactionEventRequest input, removing
  positional param complexity and non-null assertions.

- Refactor sendAndSetConnectorStatus from positional params to
  object-based StatusNotificationRequest input. Callers pass
  version-correct field names: 'status' in OCPP 1.6 code,
  'connectorStatus' in OCPP 2.0 code, either in common code.

- Extract getSchemaBaseDir() as protected method on UIMCPServer
  for test overridability, replacing fragile symlink hack.

- Migrate all call sites (28 sendAndSetConnectorStatus, 60+
  buildTransactionEvent) and their corresponding tests.

* build: externalize @modelcontextprotocol/sdk and zod from esbuild bundle

Add @modelcontextprotocol/* and zod to esbuild external list for
consistency with all other production dependencies (ajv, ws, winston,
etc.) which are already externalized. Reduces bundle size and avoids
bundling the SDK's transitive dependency tree.

* fix(ui-server): fix SonarCloud quality gate — localeCompare sort, reduce cognitive complexity

- Add localeCompare to schema file sort in MCPResourceHandlers to
  ensure locale-aware alphabetical ordering (SonarCloud bug)

- Extract sendErrorResponse helper from handleMcpRequest to reduce
  cognitive complexity from 16 to below threshold (SonarCloud smell)

* chore: update .serena/project.yml with new default config fields

* refactor(ocpp): buildMeterValue resolves connectorId/evseId from transactionId

- Refactor buildMeterValue signature from (station, connectorId,
  transactionId, interval) to (station, transactionId, interval).
  connectorId and evseId are resolved internally from the unique
  transactionId via getConnectorIdByTransactionId/getEvseIdByTransactionId.

- Refactor getSampledValueTemplate to accept optional evseId. When
  provided, EVSE-level MeterValues templates take priority; falls back
  to merging connector-level templates from all EVSE connectors.

- Add MeterValues field to EvseStatus and EvseTemplate types to support
  EVSE-level meter value template definitions.

- Refactor handleMeterValues in BroadcastChannel from if/else to
  switch/case on OCPP version with explicit default error.

- Simplify OCPP20IncomingRequestService fallback MeterValues (no
  transaction = static empty meter value, skip buildMeterValue).

- Add buildMeterValue unit tests covering OCPP 1.6/2.0 resolution,
  unknown transactionId throw, EVSE-level template priority, and
  connector template merge fallback.

- Add TEST_TRANSACTION_ID_STRING constant for OCPP 2.0 tests, fix
  BroadcastChannel test to use string transactionId for OCPP 2.0.1.

* docs: add MCP Protocol to ToC, fix HTTP Protocol link, document EVSE-level MeterValues

- Add MCP Protocol entry to table of contents
- Fix HTTP Protocol anchor to match 'deprecated' heading
- Document EVSE-level MeterValues templates in Evses section examples
  with priority explanation (EVSE-level overrides connector-level)

* fix(ocpp): version-aware Authorize response mapping, complete cross-version types

- Fix commandResponseToResponseStatus for Authorize: use switch/case
  on OCPP version. OCPP 1.6 checks idTagInfo.status, OCPP 2.0 checks
  idTokenInfo.status. Previously only checked idTagInfo (1.6 field),
  causing MCP to report 'failure' for successful OCPP 2.0 authorizations.

- Make AuthorizeResponse and AuthorizeRequest cross-version union types
  (OCPP16 | OCPP20) consistent with AuthorizationStatus pattern.

- Make isIdTagRemoteAuthorized version-aware: sends idTag for OCPP 1.6,
  idToken for OCPP 2.0, and checks the version-correct response field.

- Use version-specific casts (OCPP16AuthorizeResponse,
  OCPP20AuthorizeResponse) in version-switched code paths instead of
  agnostic union type.

* refactor(ocpp): harmonize version switch/case in auth functions

- Refactor isIdTagAuthorizedUnified and isIdTagAuthorized from if/else
  on OCPP version to switch/case with explicit VERSION_16, VERSION_20/201
  cases and separate default fallback.

- Remove unnecessary optional chaining on stationInfo inside
  version-switched cases where stationInfo is guaranteed non-null.

- Separate VERSION_16 from default in all auth switch/case blocks
  for spec-compliant version handling.

* fix(ocpp): buildMeterValue from transactionId, log date locale fix, MeterValues EVSE templates

- Refactor buildMeterValue to resolve connectorId/evseId from
  transactionId internally. Returns empty MeterValue when transactionId
  is undefined (MCP pass-through with provided meterValue).

- Fix log file date resolution: use local date instead of UTC to match
  Winston DailyRotateFile naming convention.

- Add optional date parameter to readCombinedLog/readErrorLog MCP tools
  for accessing rotated log files by date (YYYY-MM-DD).

- Add MeterValues field to EvseStatus/EvseTemplate types for EVSE-level
  meter value template definitions. getSampledValueTemplate checks
  EVSE-level templates first, falls back to merging connector templates.

- Refactor handleMeterValues to switch/case on OCPP version with
  explicit default error.

- Make AuthorizeRequest/AuthorizeResponse cross-version union types.
  Fix commandResponseToResponseStatus and isIdTagRemoteAuthorized with
  version-aware switch/case for OCPP 1.6 (idTagInfo) vs 2.0 (idTokenInfo).

- Add integration tests for readCombinedLog tool (default date, explicit
  date, missing file error). Add buildMeterValue unit tests.

2 weeks agochore(deps): lock file maintenance (#1747)
renovate[bot] [Mon, 23 Mar 2026 12:04:34 +0000 (13:04 +0100)] 
chore(deps): lock file maintenance (#1747)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 weeks agochore(deps): update dependency vite to v8 (#1717)
renovate[bot] [Mon, 23 Mar 2026 11:15:16 +0000 (12:15 +0100)] 
chore(deps): update dependency vite to v8 (#1717)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>