]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/log
e-mobility-charging-stations-simulator.git
39 hours agofix: use exception barrel instead of direct module imports
Jérôme Benoit [Thu, 19 Mar 2026 14:58:59 +0000 (15:58 +0100)] 
fix: use exception barrel instead of direct module imports

Redirect OCPPError imports from exception/OCPPError.js to the barrel
exception/index.js in auth services and OCPP20 test utils.

40 hours agofix(ocpp): keep StopTransactionRequest as 1.6 wire type, narrow reason via indexed...
Jérôme Benoit [Thu, 19 Mar 2026 14:53:24 +0000 (15:53 +0100)] 
fix(ocpp): keep StopTransactionRequest as 1.6 wire type, narrow reason via indexed access

Revert the Omit-based widening of StopTransactionRequest — it's a 1.6
wire protocol message and should not accept OCPP 2.0 reason values.
Use StopTransactionRequest['reason'] indexed access type for narrowing
in ChargingStation, avoiding any version-specific OCPP16* import.

40 hours agofix(ocpp): remove type re-export from services barrel
Jérôme Benoit [Thu, 19 Mar 2026 14:42:59 +0000 (15:42 +0100)] 
fix(ocpp): remove type re-export from services barrel

Move OCPP20TransactionEventEnumType and OCPP20TriggerReasonEnumType
imports in ChargingStation.ts from the services barrel (ocpp/index.ts)
to the types barrel (types/index.ts) where they belong.

40 hours agorefactor(ocpp): consolidate cross-stack types and harmonize barrel imports
Jérôme Benoit [Thu, 19 Mar 2026 14:24:42 +0000 (15:24 +0100)] 
refactor(ocpp): consolidate cross-stack types and harmonize barrel imports

Consolidate OCPP types with common semantics across 1.6 and 2.0 stacks
(AuthorizationStatus, StopTransactionReason, MessageTrigger, DataTransfer,
TriggerMessageStatus, UnlockStatus, AvailabilityStatus, HeartbeatRequest/
Response, StatusNotificationResponse, FirmwareStatusNotificationResponse).

Harmonize all imports across src/ and tests/ to use the barrel
(types/index.ts) instead of direct version-specific type paths, matching
the pattern already established by the OCPP 1.6 handlers.

42 hours agochore(ocpp-server): add .coverage to .gitignore
Jérôme Benoit [Thu, 19 Mar 2026 11:59:29 +0000 (12:59 +0100)] 
chore(ocpp-server): add .coverage to .gitignore

42 hours agotest(ocpp-server): add coverage threshold (fail_under=83%)
Jérôme Benoit [Thu, 19 Mar 2026 11:58:01 +0000 (12:58 +0100)] 
test(ocpp-server): add coverage threshold (fail_under=83%)

42 hours agotest(ui): raise coverage thresholds to match current levels
Jérôme Benoit [Thu, 19 Mar 2026 11:55:08 +0000 (12:55 +0100)] 
test(ui): raise coverage thresholds to match current levels

statements: 87→91, branches: 85→89, functions: 80→83, lines: 87→91

43 hours agotest(webui): add comprehensive unit test suite (#1738)
Jérôme Benoit [Thu, 19 Mar 2026 11:39:02 +0000 (12:39 +0100)] 
test(webui): add comprehensive unit test suite (#1738)

* test(webui): add test infrastructure and setup

* test(webui): add Utils composable tests

* test(webui): refactor and expand UIClient tests

* test(webui): add action modal component tests

* test(webui): fix JSDoc warnings in mount factory functions

* test(webui): add ChargingStationsView tests

* [autofix.ci] apply automated fixes

* test(webui): finalize coverage and verify quality gates

* test(webui): harmonize test infrastructure — extract ButtonStub, unify toast mock, DRY error tests

* test(webui): add missing coverage for timeout, server switching, authorize errors, WS states

* test(webui): raise coverage thresholds to match achieved 93%/91%/85%/93%

* test(webui): address PR review — unify mock cleanup, improve MockWebSocket fidelity, fix types

* test(webui): adapt tests for new OCPP Version column

* test(webui): remove MockWebSocket auto-open, robustify component lookups, add open assertion

* test(webui): init MockWebSocket readyState to CONNECTING per WebSocket spec

* fix(webui): use window.localStorage for Node 22+ jsdom compatibility

* fix(webui): disable Node 25+ native webstorage to prevent jsdom localStorage conflict

* test(webui): await router.isReady() in App mount test

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
43 hours agorefactor(ocpp2): harmonize and condense audit comments to match existing FR style
Jérôme Benoit [Thu, 19 Mar 2026 11:29:15 +0000 (12:29 +0100)] 
refactor(ocpp2): harmonize and condense audit comments to match existing FR style

43 hours agofix(ocpp2): audit TransactionEvent — state ownership, deauthorization, meter values
Jérôme Benoit [Thu, 19 Mar 2026 11:17:44 +0000 (12:17 +0100)] 
fix(ocpp2): audit TransactionEvent — state ownership, deauthorization, meter values

- Fix periodic TransactionEvent(Updated) to include meter values via buildMeterValue
- Replace non-UUID temp transactionId with generateUUID in OCPP20AuthAdapter
- Refactor state ownership: response handler is sole authority for transactionStarted,
  StatusNotification(Occupied), and TxUpdatedInterval start
- Add transactionPending flag to prevent duplicate RequestStartTransaction race conditions
- Add requestDeauthorizeTransaction per E05.FR.09/FR.10/E06.FR.04: sends
  Updated(Deauthorized, SuspendedEVSE) then Ended(Deauthorized, DeAuthorized)
- Fix rejection check to cover all non-Accepted idTokenInfo statuses
- Extract buildFinalMeterValues helper to eliminate DRY violation
- Skip Occupied/TxUpdatedInterval setup when idToken is rejected in same response
- Remove cleanup from Ended response handler (owned by caller)
- Add tests for getTxUpdatedInterval, requestDeauthorizeTransaction, Updated-failure path

45 hours agochore(deps): update dependency vue-tsc to v3 (#1740)
renovate[bot] [Thu, 19 Mar 2026 09:34:33 +0000 (10:34 +0100)] 
chore(deps): update dependency vue-tsc to v3 (#1740)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 days agofix(ocpp2): handle transaction lifecycle in TransactionEvent response
Jérôme Benoit [Thu, 19 Mar 2026 00:08:33 +0000 (01:08 +0100)] 
fix(ocpp2): handle transaction lifecycle in TransactionEvent response

Adapt the OCPP 1.6 transaction lifecycle pattern to OCPP 2.0: manage
connector state (transactionStarted, transactionId, StatusNotification,
meter intervals) in handleResponseTransactionEvent for both Started
and Ended events, matching handleResponseStartTransaction and
handleResponseStopTransaction in OCPP 1.6.

2 days agofeat(ui): add SAP Horizon theme from official theming-base-content
Jérôme Benoit [Wed, 18 Mar 2026 23:47:16 +0000 (00:47 +0100)] 
feat(ui): add SAP Horizon theme from official theming-base-content

Map SAP Horizon palette to semantic tokens using values from
SAP/theming-base-content sap_horizon css_variables.css.

2 days agofix(ui): use theme text color on toggle pressed state for readability
Jérôme Benoit [Wed, 18 Mar 2026 23:44:23 +0000 (00:44 +0100)] 
fix(ui): use theme text color on toggle pressed state for readability

White text on light Surface backgrounds was illegible in Catppuccin
Latte. Using --color-text adapts to both light and dark themes.

2 days agodocs(ui): document theme configuration and available themes
Jérôme Benoit [Wed, 18 Mar 2026 23:35:02 +0000 (00:35 +0100)] 
docs(ui): document theme configuration and available themes

2 days agofeat(ui): add Catppuccin Latte light theme
Jérôme Benoit [Wed, 18 Mar 2026 23:32:29 +0000 (00:32 +0100)] 
feat(ui): add Catppuccin Latte light theme

Map Catppuccin Latte palette to semantic tokens following the official
style guide: Base for background, Mantle/Crust for secondary panes,
Surface 0/1 for hover/active, Blue for buttons/links, Lavender for
accent, Base for text-on-button (On Accent per guide).

2 days agofix(ui): remove redundant text color selectors that override toast styles
Jérôme Benoit [Wed, 18 Mar 2026 23:27:55 +0000 (00:27 +0100)] 
fix(ui): remove redundant text color selectors that override toast styles

The h1-h3/p/li selectors were unnecessary (body color is inherited)
and caused vue-toast-notification text to appear grey instead of white.

2 days agofeat(ui): add configurable theme support
Jérôme Benoit [Wed, 18 Mar 2026 23:23:29 +0000 (00:23 +0100)] 
feat(ui): add configurable theme support

Add theme field to ConfigurationData. Theme CSS files are loaded
dynamically from assets/themes/ at startup. Falls back to
tokyo-night-storm when not configured or theme not found.

Move theme.css to assets/themes/tokyo-night-storm.css.

2 days agofeat(ui): apply Tokyo Night Storm theme with semantic CSS tokens
Jérôme Benoit [Wed, 18 Mar 2026 23:14:13 +0000 (00:14 +0100)] 
feat(ui): apply Tokyo Night Storm theme with semantic CSS tokens

Add theme.css with two-layer token system: primitive tokens from
the official Tokyo Night Storm palette, semantic tokens mapping UI
roles to primitives. All components use semantic tokens exclusively.

Replace all hardcoded colors across 13 Vue components. Theme native
HTML elements (button, input, select, a, headings) globally. Toggle
button pressed state uses palette-semantic active bg + accent border
+ inset shadow for clear visual distinction.

Remove dead code: simulatorButtonClass computed and associated CSS
classes that duplicated global button styles.

2 days agofeat(ui): add OCPP version column to charging stations table
Jérôme Benoit [Wed, 18 Mar 2026 22:25:38 +0000 (23:25 +0100)] 
feat(ui): add OCPP version column to charging stations table

Display ocppVersion from stationInfo between Registration Status and
Template columns.

2 days agofix: use truncateId consistently for all user identifiers in logs
Jérôme Benoit [Wed, 18 Mar 2026 22:21:10 +0000 (23:21 +0100)] 
fix: use truncateId consistently for all user identifiers in logs

Replace 16 bare idTag/identifier.value references and 4 manual
substring(0,8) truncations with truncateId() across OCPP 1.6
ResponseService, IncomingRequestService, auth adapters, strategies,
and helpers.

User identifiers (RFID tags, auth tokens) are now consistently
truncated in all log output to prevent sensitive data exposure.

2 days agofix: preserve EVSE and connector IDs in configuration persistence
Jérôme Benoit [Wed, 18 Mar 2026 22:00:00 +0000 (23:00 +0100)] 
fix: preserve EVSE and connector IDs in configuration persistence

Serialization (buildConnectorsStatus, buildEvsesStatus) now saves
[id, status] tuples instead of flat arrays, preserving EVSE and
connector IDs per OCPP 2.0.1 §7.

Deserialization detects both formats transparently: new tuple format
uses explicit IDs, legacy flat array format falls back to array index.

Add checkEvsesConfiguration template validation enforcing §7.2
connector numbering (EVSE 0: connector 0 only, EVSE ≥1: connectors
start at 1).

Add tests for ID preservation across serialization for both connectors
and EVSEs.

2 days agofix: prevent tests from polluting production log files
Jérôme Benoit [Wed, 18 Mar 2026 20:33:51 +0000 (21:33 +0100)] 
fix: prevent tests from polluting production log files

Set NODE_ENV=test via cross-env in all three test scripts (test,
test:debug, test:coverage). Logger checks NODE_ENV and sets silent
mode, preventing file writes to logs/combined.log and logs/error.log
during test runs.

2 days agotest: move truncateId tests from InMemoryAuthCache to Utils
Jérôme Benoit [Wed, 18 Mar 2026 20:07:13 +0000 (21:07 +0100)] 
test: move truncateId tests from InMemoryAuthCache to Utils

truncateId is a general utility function in src/utils/, not a cache
feature. Its tests belong in the utils test file.

2 days agofix(ocpp): replace this.constructor.name with moduleName in base class logs
Jérôme Benoit [Wed, 18 Mar 2026 19:53:19 +0000 (20:53 +0100)] 
fix(ocpp): replace this.constructor.name with moduleName in base class logs

Minification mangles class names, causing logs like 'ln.responseHandler'
instead of 'OCPP20ResponseService.responseHandler' in production builds.

Add abstract moduleName property to OCPPResponseService and
OCPPIncomingRequestService base classes, implemented by all four
subclasses using their file-level moduleName constant.

2 days agorefactor(test): remove duplicate certificate passthrough tests from broadcast channel
Jérôme Benoit [Wed, 18 Mar 2026 19:04:12 +0000 (20:04 +0100)] 
refactor(test): remove duplicate certificate passthrough tests from broadcast channel

These 3 tests duplicated coverage already provided by
OCPP20RequestService-ISO15118.test.ts which tests the same code path
through the full requestHandler pipeline.

Add JSDoc descriptions to buildTransactionEvent implementation to
satisfy jsdoc/require-param-description lint rule.

2 days agotest(ocpp): add call chain integration tests for both OCPP stacks
Jérôme Benoit [Wed, 18 Mar 2026 18:49:02 +0000 (19:49 +0100)] 
test(ocpp): add call chain integration tests for both OCPP stacks

Verify the single-path contract: requestHandler(minimal params) →
buildRequestPayload(constructs) → sendMessage(complete payload).

OCPP 2.0: StatusNotification, TransactionEvent (with default resolution
for triggerReason, transactionId, connectorId from evse), Heartbeat,
and rawPayload bypass.

OCPP 1.6: StatusNotification (errorCode added by builder),
StartTransaction (meterStart/timestamp enrichment), StopTransaction
(meterStop/timestamp enrichment), Heartbeat.

Remove deleted TransactionContextFixtures from TEST_STYLE_GUIDE.

2 days agorefactor(ocpp2): extract buildTransactionEvent as standalone function, remove dead...
Jérôme Benoit [Wed, 18 Mar 2026 18:32:25 +0000 (19:32 +0100)] 
refactor(ocpp2): extract buildTransactionEvent as standalone function, remove dead context code

Extract buildTransactionEvent from OCPP20ServiceUtils static method to
exported standalone function, consistent with buildStatusNotificationRequest
and buildMeterValue.

Remove unused context overload from buildTransactionEvent and
sendTransactionEvent — all production callers pass triggerReason directly.

Remove dead code: OCPP20TransactionContext interface (not in OCPP 2.0.1
specs), selectTriggerReason method, TransactionContextFixtures, and
associated tests.

2 days agorefactor(ocpp): add rawPayload bypass, resolve minimal params in buildRequestPayload...
Jérôme Benoit [Wed, 18 Mar 2026 16:59:56 +0000 (17:59 +0100)] 
refactor(ocpp): add rawPayload bypass, resolve minimal params in buildRequestPayload, fix test types

Add rawPayload option to RequestParams for offline queue replay to
explicitly bypass buildRequestPayload instead of heuristic detection.

Make buildRequestPayload resolve missing TransactionEvent fields from
station context (connectorId from evse, transactionId generation,
triggerReason defaults) so the broadcast channel passthrough works.

Remove pre-built payload guard from OCPP 1.6 STATUS_NOTIFICATION.

Replace Record<string, unknown> downgrades in tests with proper OCPP
types (Partial<OCPP20TransactionEventRequest>, RequestParams).

2 days agorefactor(ocpp): make buildRequestPayload the authoritative builder for all commands
Jérôme Benoit [Wed, 18 Mar 2026 16:13:49 +0000 (17:13 +0100)] 
refactor(ocpp): make buildRequestPayload the authoritative builder for all commands

buildRequestPayload now calls the centralized builders for
TRANSACTION_EVENT (via buildTransactionEvent) and STATUS_NOTIFICATION
(via buildStatusNotificationRequest) in both OCPP versions, matching
the existing pattern for START_TRANSACTION/STOP_TRANSACTION in 1.6.

Callers pass minimal params (eventType, connectorId, status, etc.)
and buildRequestPayload constructs the complete spec-compliant payload.

sendTransactionEvent's offline path builds directly for queueing since
the queue stores pre-built payloads sent as-is on reconnect.

2 days agorefactor(ocpp): use buildStatusNotificationRequest helper in TriggerMessage handlers
Jérôme Benoit [Wed, 18 Mar 2026 15:03:31 +0000 (16:03 +0100)] 
refactor(ocpp): use buildStatusNotificationRequest helper in TriggerMessage handlers

Replace inline StatusNotification payload construction in both OCPP 1.6
and 2.0 TriggerMessage handlers with the centralized
buildStatusNotificationRequest helper. Export the helper from
OCPPServiceUtils to make it available.

This eliminates 5 inline duplications of the same payload structure
that risked diverging from the single source of truth.

2 days agorefactor(ocpp): harmonize buildRequestPayload switch structure across versions
Jérôme Benoit [Wed, 18 Mar 2026 14:49:05 +0000 (15:49 +0100)] 
refactor(ocpp): harmonize buildRequestPayload switch structure across versions

Group passthrough cases into fall-through blocks in both OCPP 1.6 and
2.0 buildRequestPayload, giving both files a symmetric structure:
grouped passthroughs, heartbeat empty object, enrichment cases, default
error.

2 days agofix(ocpp2): revert UI transaction handlers to simple passthrough
Jérôme Benoit [Wed, 18 Mar 2026 14:35:41 +0000 (15:35 +0100)] 
fix(ocpp2): revert UI transaction handlers to simple passthrough

Remove handleUIStartTransaction and handleUIStopTransaction which
duplicated connector state management with incomplete initialization
(missing energy register, groupIdToken, StatusNotification, error
rollback) and bypassed authorization checks.

Restore handleTransactionEvent as a simple requestHandler passthrough,
consistent with all other broadcast channel command handlers.

2 days agorefactor(ocpp2): remove redundant getTxUpdatedInterval wrapper
Jérôme Benoit [Wed, 18 Mar 2026 14:24:06 +0000 (15:24 +0100)] 
refactor(ocpp2): remove redundant getTxUpdatedInterval wrapper

Call OCPP20ServiceUtils.getTxUpdatedInterval() directly from
OCPP20IncomingRequestService instead of through a trivial private
wrapper that was left over from the centralization in PR #1734.

2 days agorefactor(ocpp2): consolidate dual-path request architecture into single path
Jérôme Benoit [Wed, 18 Mar 2026 14:16:54 +0000 (15:16 +0100)] 
refactor(ocpp2): consolidate dual-path request architecture into single path

Remove 8 unused dedicated request methods (requestFirmwareStatusNotification,
requestGet15118EVCertificate, requestGetCertificateStatus, etc.) that bypassed
buildRequestPayload via direct sendMessage calls. All production callers
already used requestHandler exclusively, making these methods dead code.

Move SignCertificate CSR generation logic into buildRequestPayload, making
it the authoritative payload construction layer — symmetric with OCPP 1.6.
This also adds isRequestCommandSupported check and AJV schema validation
to the SignCertificate flow that the dedicated method bypassed.

Refactor 6 test files to test through requestHandler (production path)
instead of the removed dedicated methods.

2 days agorefactor(ocpp2): centralize payload construction and eliminate duplication
Jérôme Benoit [Wed, 18 Mar 2026 13:44:59 +0000 (14:44 +0100)] 
refactor(ocpp2): centralize payload construction and eliminate duplication

Remove redundant timestamp injection from buildRequestPayload() for
StatusNotification and TransactionEvent — centralized builders already
provide timestamps and AJV schema validation catches any omission.

Extract buildStationInfoReportData() helper to deduplicate station info
report data construction between FullInventory and SummaryInventory in
buildReportData().

Normalize TriggerMessage Heartbeat to use OCPP20Constants.OCPP_RESPONSE_EMPTY
for consistency with buildRequestPayload().

2 days agofeat(ui): add OCPP 2.0.x command support to Web UI (#1734)
Jérôme Benoit [Wed, 18 Mar 2026 13:34:49 +0000 (14:34 +0100)] 
feat(ui): add OCPP 2.0.x command support to Web UI (#1734)

* feat(ui): add OCPP 2.0.x types and sync ProcedureName enum

- Add OCPP 2.0.x-specific procedures to ProcedureName enum:
  TRANSACTION_EVENT, GET_15118_EV_CERTIFICATE, GET_CERTIFICATE_STATUS,
  LOG_STATUS_NOTIFICATION, NOTIFY_CUSTOMER_INFORMATION, NOTIFY_REPORT,
  SECURITY_EVENT_NOTIFICATION, SIGN_CERTIFICATE

- Add OCPP 2.0.x type definitions:
  - OCPP20IdTokenEnumType (8 values)
  - OCPP20TransactionEventEnumType (Ended, Started, Updated)
  - OCPP20IdTokenType interface
  - OCPP20TransactionEventRequest interface

- Update ConnectorStatus.transactionId to support both number (OCPP 1.6)
  and string/UUID (OCPP 2.0.x)

Wave 1 complete.

* feat(ui): add UIClient transaction methods with OCPP version support

- Add transactionEvent() method for OCPP 2.0.x TransactionEvent requests
- Add isOCPP20x() static helper for version detection
- Add startTransactionForVersion() helper that routes to appropriate
  method based on OCPP version (1.6 vs 2.0.x)
- Add stopTransactionForVersion() helper for version-aware stop
- Add comprehensive unit tests (16 tests passing)

Wave 2 complete.

* feat(ui): add version-aware StartTransaction form with OCPP 2.0.x support

- Modify StartTransaction.vue to detect OCPP version from station info
- Show connector ID for OCPP 1.6, EVSE ID input for OCPP 2.0.x
- Hide Authorize checkbox for OCPP 2.0.x stations (v-if)
- Use startTransactionForVersion() helper for version-aware API calls
- Add loading state while fetching station info
- Show appropriate form fields based on OCPP version
- All 16 tests passing

Wave 3 complete.

* fix(webui): use enums instead of string literals for OCPP 2.0.x types

- Replace 'ISO14443' string with OCPP20IdTokenEnumType.ISO14443

- Fix test mocks to use Protocol.UI and ResponseStatus.SUCCESS enums

- Export OCPP20IdTokenEnumType from types index

* style(webui): fix import ordering in UIClient.ts

* [autofix.ci] apply automated fixes

* chore: remove tsbuildinfo and add to gitignore

* fix(webui): address PR review comments

- Add ResponseStatus import and use enum instead of string

- Use UIClient.isOCPP20x() helper instead of manual comparison

- Remove redundant showAuthorize computed, use !isOCPP20x directly

- Fix authorizeIdTag checkbox binding (remove true-value/false-value)

- Initialize evseId from props.connectorId

- Separate error handling for authorize vs startTransaction

- Add validation for transactionId type in stopTransactionForVersion

* [autofix.ci] apply automated fixes

* chore: move tsbuildinfo gitignore to ui/web subdirectory

- Remove *.tsbuildinfo from root .gitignore

- Add *.tsbuildinfo to ui/web/.gitignore with proper comment

* ci(webui): add TypeScript type checking to CI

- Add vue-tsc dev dependency to ui/web

- Add typecheck script to package.json

- Add typecheck step to build-dashboard job in CI

* fix(webui): fix vue-tsc typecheck and improve Vue.js best practices

- Break recursive JsonObject/JsonType chain causing TS2589 in vue-tsc
- Fix CSConnector to use stopTransactionForVersion with ocppVersion prop
- Replace getCurrentInstance() anti-pattern with useToast()/useRouter()
- Make isOCPP20x a computed instead of manually-synced ref
- Deduplicate handleStartTransaction (remove ~30 lines of duplication)
- Add null guards for watch() on potentially undefined global refs

* [autofix.ci] apply automated fixes

* refactor(webui): align namespace with simulator and improve API design

- Merge startTransactionForVersion/stopTransactionForVersion into
  startTransaction/stopTransaction with optional ocppVersion param
- Make transactionEvent private (implementation detail, not public API)
- Revert UITransactionEventPayload to OCPP20TransactionEventRequest
  to match backend naming convention
- Pass ocppVersion via route param instead of re-fetching all stations
- Remove convertToBoolean no-op and loading state
- Flip negated v-if condition for SonarCloud compliance
- Factor test setup, remove duplicate coverage, add missing test case
- Net result: -90 lines, cleaner API surface

* [autofix.ci] apply automated fixes

* refactor(webui): move tests from __tests__ to tests/unit for consistency

Align test file location with existing project convention (tests/unit/)
instead of Jest-style __tests__ directory.

* feat(webui): integrate OCPP 2.0 EVSE 3-tier model across web UI

- Add OCPP20EVSEType matching spec EVSEType {id, connectorId?}
- Replace flat evseId with proper evse object in TransactionEventRequest
- CSData: preserve EVSE→Connector mapping instead of flattening
- CSConnector: display 'evseId/connectorId' for OCPP 2.0 stations
- Route: pass evseId and ocppVersion as query params (not sentinels)
- StartTransaction: read EVSE context from query, display EVSE/Connector
- UIClient.startTransaction: accept evseId, build spec-compliant evse object
- Tests: exhaustive decision tree coverage (18 tests, all branches)

* [autofix.ci] apply automated fixes

* refactor(webui): improve API elegance and fix cross-version concerns

- Refactor startTransaction/stopTransaction to use named options object
  instead of positional params for better readability and grouping
- Fix ToggleButton ID collision: include evseId for multi-EVSE uniqueness
- Normalize idTag validation as cross-version concern (empty→undefined)
- Rename getConnectorStatuses→getConnectorEntries to match semantics
- Extract toggleButtonId as computed to avoid string duplication

* feat: entry-based serialization for EVSE/Connector/ATG data

Introduce ConnectorEntry, EvseEntry, and ATGStatusEntry types at the
UI serialization boundary to preserve Map keys (evseId, connectorId)
that were previously lost during Map-to-Array conversion.

Backend:
- Add buildConnectorEntries/buildEvseEntries using .entries()
- Keep buildConnectorsStatus/buildEvsesStatus unchanged for config persistence
- Remove EvseStatusWorkerType and OutputFormat enum
- ATG statuses serialized with connectorId at the UI boundary only
- ConnectorStatus and EvseStatus types remain pure (no identity fields)

Frontend:
- CSData uses explicit IDs from Entry types (no more index-based mapping)
- ATG status lookup by connectorId instead of array index
- Filter connector 0 in both OCPP 1.6 and 2.0 paths
- Both connectors and evses optional in ChargingStationData

* [autofix.ci] apply automated fixes

* fix(webui): use data presence instead of protocol version for EVSE display

EVSE/Connector display depends on whether the station has EVSEs (data),
not on the OCPP version (protocol). A station could use EVSEs regardless
of OCPP version.

* refactor: extract buildATGStatusEntries for consistent Entry builder pattern

Align ATG status serialization with ConnectorEntry/EvseEntry pattern:
dedicated builder function instead of inline transformation.

* refactor: harmonize Entry naming pattern across codebase

Rename ATGStatusEntry/buildATGStatusEntries to ATGEntry/buildATGEntries
to match ConnectorEntry/EvseEntry naming convention.

* refactor: define ATGConfiguration type and harmonize across workspaces

Extract inline ATG type into named ATGConfiguration interface, matching
the ATG*/Connector*/Evse* Entry pattern in both backend and frontend.

* test: add missing Entry builder tests and non-sequential ID coverage

- Add buildATGEntries tests (entries with IDs, non-sequential, no ATG, no status)
- Add non-sequential connector ID test for buildConnectorEntries (keys 0,3,7)
- Add non-sequential evseId/connectorId test for buildEvseEntries (3/2,5)
- These tests verify the core invariant of the Entry pattern: Map keys are
  preserved regardless of their sequence

* refactor: organize tests by concern and replace throw with graceful failure

- Group backend tests: config persistence builders then UI Entry builders
- Replace throw Error in stopTransaction with ResponsePayload FAILURE
  to avoid unhandled rejections in UI components

* docs: harmonize quality gate ordering (typecheck before lint) across codebase

Align CI workflows, READMEs, and copilot-instructions to consistently
run typecheck before lint in all three sub-projects.

* refactor(ocpp2): centralize TransactionEvent payload building

- requestStopTransaction delegates to sendTransactionEvent instead of
  building the OCPP payload inline, eliminating duplication
- All TransactionEvent paths now converge through sendTransactionEvent
  → buildTransactionEvent as the single payload construction point
- handleTransactionEvent (UI) dispatches Started/Ended to proper flows
  with connector state initialization and lifecycle management
- Remove unused imports (secondsToMilliseconds, Constants)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2 days agochore(deps): lock file maintenance (#1729)
renovate[bot] [Wed, 18 Mar 2026 12:14:24 +0000 (13:14 +0100)] 
chore(deps): lock file maintenance (#1729)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 days agochore(deps): update pnpm/action-setup action to v5 (#1737)
renovate[bot] [Wed, 18 Mar 2026 12:09:37 +0000 (13:09 +0100)] 
chore(deps): update pnpm/action-setup action to v5 (#1737)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 days agochore(deps): update dependency eslint-plugin-perfectionist to ^5.7.0 (#1736)
renovate[bot] [Wed, 18 Mar 2026 12:09:01 +0000 (13:09 +0100)] 
chore(deps): update dependency eslint-plugin-perfectionist to ^5.7.0 (#1736)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 days agofeat(ocpp2): fix authorization conformance gaps (C10, C12, C01, C09) (#1735)
Jérôme Benoit [Wed, 18 Mar 2026 10:44:58 +0000 (11:44 +0100)] 
feat(ocpp2): fix authorization conformance gaps (C10, C12, C01, C09) (#1735)

* feat(ocpp2): auto-update auth cache on TransactionEventResponse (C10.FR.01/04/05, C12.FR.06)

* feat(ocpp2): check MasterPassGroupId in start transaction (C12.FR.09)

* feat(ocpp2): add groupId-based stop transaction authorization (C01.FR.03, C09.FR.03/07)

* fix(ocpp2): address PR review findings

- C12.FR.09: compare groupIdToken not idToken
- C09.FR.03: remove inner isIdTokenAuthorized check
- Guard updateCacheEntry on authorizationCacheEnabled
- Fix MasterPass test to mock VariableManager

* style(tests): align test naming with style guide

* fix: merge duplicate AuthTypes.js imports in auth barrel

* fix(tests): add required JSDoc to GroupIdStop test helper

* style(ocpp2): harmonize log levels, remove spec refs from log messages, use truncateId

* style(auth): harmonize log prefixes, levels, and identifier truncation

* refactor(auth): use moduleName constant for log prefixes in OCPPAuthServiceImpl

* refactor(auth): use moduleName constant across all auth files

* fix(tests): skip flaky RequestStopTransaction test on all Node 22 platforms

* refactor(auth): harmonize log prefixes in auth adapters

* refactor: move truncateId to utils and truncate identifiers in OCPP20 logs

* style(ocpp2): remove spec ref from statusInfo additionalInfo

* style(ocpp2): truncate idToken in statusInfo additionalInfo

* test(ocpp2): improve test coverage and remove duplicates in auth conformance tests

3 days agodocs(ocpp-server): add typecheck and test_coverage to README and reorder dev sections
Jérôme Benoit [Tue, 17 Mar 2026 17:18:43 +0000 (18:18 +0100)] 
docs(ocpp-server): add typecheck and test_coverage to README and reorder dev sections

3 days agodocs: update suggested commands and task completion checklist
Jérôme Benoit [Tue, 17 Mar 2026 17:02:39 +0000 (18:02 +0100)] 
docs: update suggested commands and task completion checklist

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
3 days agochore: release main (#1732)
Jérôme Benoit [Tue, 17 Mar 2026 13:25:26 +0000 (14:25 +0100)] 
chore: release main (#1732)

* chore: release main

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
3 days agodocs: remove redundant common pitfalls from code style conventions
Jérôme Benoit [Tue, 17 Mar 2026 13:11:08 +0000 (14:11 +0100)] 
docs: remove redundant common pitfalls from code style conventions

3 days agodocs: update openspec config with current project structure and conventions
Jérôme Benoit [Tue, 17 Mar 2026 12:53:56 +0000 (13:53 +0100)] 
docs: update openspec config with current project structure and conventions

3 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Tue, 17 Mar 2026 12:36:26 +0000 (12:36 +0000)] 
[autofix.ci] apply automated fixes

3 days agofix(ui): use runtime imports for enums and disable lint rule for .d.ts
Jérôme Benoit [Tue, 17 Mar 2026 12:34:02 +0000 (13:34 +0100)] 
fix(ui): use runtime imports for enums and disable lint rule for .d.ts

3 days agofix(ocpp2): use convertToDate() for all incoming date fields in handlers
Jérôme Benoit [Tue, 17 Mar 2026 12:21:30 +0000 (13:21 +0100)] 
fix(ocpp2): use convertToDate() for all incoming date fields in handlers

Replace raw new Date(string) and direct string comparisons with
convertToDate() for date fields in incoming OCPP 2.0 payloads:
- validateChargingProfile: validFrom/validTo comparisons
- validateChargingSchedule: startSchedule comparisons
- simulateFirmwareUpdateLifecycle: retrieveDateTime/installDateTime
- handleResponseHeartbeat: currentTime display
- OCPP20CertificateManager: cert.validFrom/validTo validation

3 days agofix(ocpp-server): update to websockets 16.x API (request.headers, request.path)
Jérôme Benoit [Tue, 17 Mar 2026 11:45:49 +0000 (12:45 +0100)] 
fix(ocpp-server): update to websockets 16.x API (request.headers, request.path)

3 days agorefactor: replace const enum with enum for cross-module runtime compatibility
Jérôme Benoit [Tue, 17 Mar 2026 11:23:51 +0000 (12:23 +0100)] 
refactor: replace const enum with enum for cross-module runtime compatibility

4 days agorefactor: re-export OCPPAuthServiceFactory in ocpp barrel and fix cross-sub-component...
Jérôme Benoit [Tue, 17 Mar 2026 00:05:02 +0000 (01:05 +0100)] 
refactor: re-export OCPPAuthServiceFactory in ocpp barrel and fix cross-sub-component imports

4 days agorefactor: enforce consistent-type-imports eslint rule and fix violations
Jérôme Benoit [Mon, 16 Mar 2026 23:48:15 +0000 (00:48 +0100)] 
refactor: enforce consistent-type-imports eslint rule and fix violations

4 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Mon, 16 Mar 2026 23:15:22 +0000 (23:15 +0000)] 
[autofix.ci] apply automated fixes

4 days agochore: release main (#1727) ocpp-server@v3.1.1 simulator@v3.1.1 webui@v3.1.1
Jérôme Benoit [Mon, 16 Mar 2026 23:12:46 +0000 (00:12 +0100)] 
chore: release main (#1727)

4 days agofix(tests): skip flaky RequestStopTransaction test on macOS + Node 22
Jérôme Benoit [Mon, 16 Mar 2026 23:11:26 +0000 (00:11 +0100)] 
fix(tests): skip flaky RequestStopTransaction test on macOS + Node 22

4 days agoci: move coverage/lint/typecheck/sonar pipeline from Node 22.x to 24.x
Jérôme Benoit [Mon, 16 Mar 2026 23:02:44 +0000 (00:02 +0100)] 
ci: move coverage/lint/typecheck/sonar pipeline from Node 22.x to 24.x

Node 22 --experimental-test-coverage has known bugs (nodejs/node#55510)
that cause false CI failures. Move all gated steps (coverage, lint,
typecheck, SonarCloud, dependency review) to Node 24.x. Node 22 remains
in the test matrix for regular pnpm test runs.

4 days agofix(tests): use conditional callCount expectation instead of skip for Node 22
Jérôme Benoit [Mon, 16 Mar 2026 22:52:59 +0000 (23:52 +0100)] 
fix(tests): use conditional callCount expectation instead of skip for Node 22

4 days agofix(tests): remove unnecessary double flushMicrotasks in skipped test
Jérôme Benoit [Mon, 16 Mar 2026 22:24:19 +0000 (23:24 +0100)] 
fix(tests): remove unnecessary double flushMicrotasks in skipped test

4 days agofix(tests): add second flushMicrotasks for RequestStopTransaction listener
Jérôme Benoit [Mon, 16 Mar 2026 21:56:52 +0000 (22:56 +0100)] 
fix(tests): add second flushMicrotasks for RequestStopTransaction listener

The async chain in requestStopTransaction traverses a dynamic import()
in checkConnectorStatusTransition (OCPPServiceUtils), which may resolve
after the first setImmediate on macOS + Node 22. A second flush ensures
the StatusNotification call completes before the assertion.

4 days agofix(tests): add second flushMicrotasks for RequestStopTransaction listener
Jérôme Benoit [Mon, 16 Mar 2026 21:56:52 +0000 (22:56 +0100)] 
fix(tests): add second flushMicrotasks for RequestStopTransaction listener

The async chain in requestStopTransaction traverses a dynamic import()
in checkConnectorStatusTransition (OCPPServiceUtils), which may resolve
after the first setImmediate on macOS + Node 22. A second flush ensures
the StatusNotification call completes before the assertion.

4 days agotest: harmonize event listener test pattern across OCPP command test files (#1730)
Jérôme Benoit [Mon, 16 Mar 2026 21:36:24 +0000 (22:36 +0100)] 
test: harmonize event listener test pattern across OCPP command test files (#1730)

* test: harmonize event listener test pattern across OCPP command test files

Add event listener test sections to 7 OCPP incoming request command test
files (4 OCPP 1.6, 3 OCPP 2.0) following the reference pattern from
RequestStopTransaction, TriggerMessage, UpdateFirmware, and GetLog tests.

Each listener section contains: registration test, accepted-fires test,
rejected-not-fires test, and error-graceful test.

Also restructures CustomerInformation to wrap existing listener tests in a
properly named describe block, and adds createOCPP16ListenerStation helper
to OCPP16TestUtils.ts.

Files modified:
- tests/charging-station/ocpp/1.6/OCPP16TestUtils.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStopUnlock.test.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-TriggerMessage.test.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-Firmware.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-CustomerInformation.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts

* fix(tests): address PR review — extract shared helper, fix mock cleanup, use flushMicrotasks

- Extract createOCPP20ListenerStation to OCPP20TestUtils.ts, removing
  duplication between RequestStart and RequestStop test files
- Replace inline import('node:test').mock.fn with top-level mock import
  in RemoteStartTransaction tests
- Remove redundant mock.reset() from 3 listener afterEach blocks —
  standardCleanup() already calls mock.restoreAll()
- Replace all await Promise.resolve() with flushMicrotasks() across 5
  OCPP 2.0 test files for more robust async side-effect flushing

* fix(tests): replace remaining await Promise.resolve() with flushMicrotasks()

8 occurrences in 5 files (3 OCPP16, 2 OCPP20) missed in the initial
review fix. Now all listener tests use flushMicrotasks() consistently.

* fix(tests): fix 3 audit findings — JSDoc headers and missing afterEach

- GetBaseReport: move @file JSDoc above first import (style guide §2)
- OCPP20TestUtils: add missing @file/@description header (style guide §2)
- RequestStopTransaction: add afterEach with standardCleanup to listener
  describe block (style guide §3)

* fix(tests): address 5 minor audit findings

- Fix import paths in 7 OCPP 2.0 test files: ../../../../tests/helpers/
  → ../../../helpers/ (correct relative path, consistent with 35 sibling
  files in the same directory)
- Add eventType assertion in RequestStartTransaction listener test to
  verify TransactionEvent(Started) per E02.FR.01
- Add flushMicrotasks() to RequestStopTransaction listener test for
  consistent emit→flush→assert pattern

* refactor(tests): move mock.method to beforeEach and parameterize trigger tests

- Move duplicated mock.method calls into listener beforeEach blocks
  in 5 files (UpdateFirmware, GetLog, GetBaseReport, CustomerInfo,
  Firmware). Rejection tests override inline. Net -147 lines.
- Parameterize OCPP16 + OCPP20 TriggerMessage trigger-fires tests
  using data-driven triggerCases arrays (already done in prior commit,
  this commit includes the Firmware mock cleanup).

* style(tests): remove inconsistent separator comment in RemoteStopUnlock

The listener section had a '// ───' separator not used in any of
the other 10 test files. The await describe block is sufficient.

* docs(tests): add summary line to startTransaction JSDoc

* docs(tests): add event listener testing section to TEST_STYLE_GUIDE

Add §11 documenting the established listener test pattern: emit()
direct, flushMicrotasks(), listenerCount first, accepted/rejected/error
triad, mock.method in beforeEach. Add listener station factories to
§9 mock factories table and flushMicrotasks to §10 utility table.

* docs(tests): fix incorrect mock API in §11 code example

Replace Jest-style mockImplementation() with Node.js test runner
mock.method() override pattern matching actual test code.

* fix(tests): align all 112 test files with TEST_STYLE_GUIDE

- Move @file JSDoc headers above first import in 3 files (GetVariables,
  MessageChannelUtils, Utils)
- Replace await Promise.resolve() with flushMicrotasks() in
  AutomaticTransactionGenerator
- Replace 6 setTimeout(resolve, 50) hacks with flushMicrotasks() in
  ChargingStationWorkerBroadcastChannel
- Document spec traceability prefix exception (G03.FR.xx, G04.INT.xx)
  in TEST_STYLE_GUIDE §1 naming conventions

* docs(tests): align TEST_STYLE_GUIDE with actual test infrastructure

- Fix createMockChargingStation location (ChargingStationTestUtils →
  helpers/StationHelpers)
- Add 7 widely-used factories to §9 table (10+ usages each)
- Remove unused expectAcceptedAuthorization from §9 auth table
- All locations verified against actual exports

* docs(tests): fix guide inconsistencies — deduplicate entries, fix assertion example

- Remove createMockCertificateManager from §10 OCPP 2.0 sub-table
  (already in §9 factory table — no duplication)
- Fix §9 usage example: assert.ok → assert.strictEqual (§7 compliance)
- Remove setupConnectorWithTransaction/clearConnectorTransaction from
  §10 lifecycle table (test setup helpers, not lifecycle utilities)
- Reorder §10: group cleanup/async utilities before spy factories

* docs(tests): fix guide precision — assert.ok scope, test script quotes, restore helpers

- Core Principles: clarify assert.ok is for boolean/existence only
- §5: add missing quotes around glob in test script (matches package.json)
- §10: restore setupConnectorWithTransaction/clearConnectorTransaction
  (27 usages across test suite — should not have been removed)

4 days agorefactor(tests): separate handler/listener tests and remove setTimeout hacks
Jérôme Benoit [Mon, 16 Mar 2026 17:58:26 +0000 (18:58 +0100)] 
refactor(tests): separate handler/listener tests and remove setTimeout hacks

Align TriggerMessage, UpdateFirmware, and GetLog test files with the
RequestStopTransaction reference pattern:
- Split into 'Handler validation' + 'event listener' describe groups
- Replace raw setTimeout delays with withMockTimers where needed
- Use emit() directly for listener tests (no wrapper helpers)
- Follows TEST_STYLE_GUIDE.md conventions

4 days agochore(deps): update all non-major dependencies to ^20.5.0 (#1728)
renovate[bot] [Mon, 16 Mar 2026 17:47:17 +0000 (18:47 +0100)] 
chore(deps): update all non-major dependencies to ^20.5.0 (#1728)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
4 days agorefactor(tests): split RequestStopTransaction tests into handler and listener groups
Jérôme Benoit [Mon, 16 Mar 2026 17:39:51 +0000 (18:39 +0100)] 
refactor(tests): split RequestStopTransaction tests into handler and listener groups

Remove emitStopEvent helper with fragile setTimeout(50) hack.
Handler tests call handleRequestStopTransaction directly.
Listener tests use emit() directly, matching the TriggerMessage
test pattern. 1856 tests (+3 new listener-specific tests).

4 days agotest(ocpp20): update RequestStopTransaction tests for event listener pattern
Jérôme Benoit [Mon, 16 Mar 2026 17:07:29 +0000 (18:07 +0100)] 
test(ocpp20): update RequestStopTransaction tests for event listener pattern

Adapt test expectations after moving requestStopTransaction from
inline handler to post-response event listener.

4 days agorefactor(ocpp): harmonize post-response event listener pattern across stacks
Jérôme Benoit [Mon, 16 Mar 2026 16:54:02 +0000 (17:54 +0100)] 
refactor(ocpp): harmonize post-response event listener pattern across stacks

Move post-response logic from inline handler to event listeners:
- OCPP16 UpdateFirmware: fire-and-forget in handler → event listener
  matching OCPP20 UpdateFirmware pattern
- OCPP20 RequestStopTransaction: await in handler → event listener
  matching OCPP16 RemoteStopTransaction pattern

All commands with post-response behavior now use the same pattern:
handler validates and returns response, event listener performs
the async action after the response is sent to the CSMS.

4 days agorefactor(ocpp20): move TransactionEvent(Started) to event listener pattern
Jérôme Benoit [Mon, 16 Mar 2026 16:28:14 +0000 (17:28 +0100)] 
refactor(ocpp20): move TransactionEvent(Started) to event listener pattern

Move sendTransactionEvent(Started) and startTxUpdatedInterval from
inside handleRequestStartTransaction to a post-response event
listener in the constructor, matching the OCPP 1.6
RemoteStartTransaction pattern where the handler validates and
returns Accepted, then an event triggers the actual StartTransaction
message send.

4 days agofix(ocpp): graceful OCPPError fallback instead of throw in buildMessageToSend
Jérôme Benoit [Mon, 16 Mar 2026 15:54:52 +0000 (16:54 +0100)] 
fix(ocpp): graceful OCPPError fallback instead of throw in buildMessageToSend

Replace defensive throw with OCPPError construction when messagePayload
is not an OCPPError instance. Uses InternalError code per OCPP-J §4.2.3.
Harmonizes with the ternary instanceof pattern used at L452/503 in the
same file for errorDetails extraction.

4 days agochore: remove outdated OCPPSpecRequirements.md
Jérôme Benoit [Mon, 16 Mar 2026 15:35:01 +0000 (16:35 +0100)] 
chore: remove outdated OCPPSpecRequirements.md

4 days agorefactor: extract ensureError and getErrorMessage helpers
Jérôme Benoit [Mon, 16 Mar 2026 15:33:41 +0000 (16:33 +0100)] 
refactor: extract ensureError and getErrorMessage helpers

Replace 61 duplicated error coercion patterns across 18 files:
- 34× 'error instanceof Error ? error : new Error(String(error))'
  → ensureError(error)
- 27× 'error instanceof Error ? error.message : String(error)'
  → getErrorMessage(error)

New helpers in src/utils/ErrorUtils.ts, exported via barrel.

4 days agofix(ocpp20): send actual MeterValues on trigger and block sessions during firmware...
Jérôme Benoit [Mon, 16 Mar 2026 15:20:23 +0000 (16:20 +0100)] 
fix(ocpp20): send actual MeterValues on trigger and block sessions during firmware update

- F06.FR.06: TriggerMessage for MeterValues now sends TransactionEvent(Updated)
  with actual meter readings for each EVSE with active transactions instead
  of hardcoded value: 0
- L01.FR.07: UpdateFirmware sets idle EVSE connectors to Unavailable when
  AllowNewSessionsPendingFirmwareUpdate is absent or false, preventing new
  sessions while waiting for active transactions to end before installing

4 days agofix(ocpp20): send TransactionEvent(Started) on RequestStartTransaction (E02.FR.01)
Jérôme Benoit [Mon, 16 Mar 2026 14:58:06 +0000 (15:58 +0100)] 
fix(ocpp20): send TransactionEvent(Started) on RequestStartTransaction (E02.FR.01)

The handler accepted remote start transactions without notifying the
CSMS via TransactionEvent. Per E02.FR.01, the CS SHALL send
TransactionEvent with eventType=Started when a transaction begins.

Also starts meter values interval for the new transaction.

4 days agofix(ocpp): replace unsafe messagePayload as OCPPError casts with instanceof
Jérôme Benoit [Mon, 16 Mar 2026 14:57:48 +0000 (15:57 +0100)] 
fix(ocpp): replace unsafe messagePayload as OCPPError casts with instanceof

6 casts in sendMessage error handling replaced with proper
instanceof OCPPError guard per existing codebase pattern.

4 days agofix(charging-station): add try/finally guards to Bootstrap start/stop
Jérôme Benoit [Mon, 16 Mar 2026 14:57:29 +0000 (15:57 +0100)] 
fix(charging-station): add try/finally guards to Bootstrap start/stop

Prevent zombie starting/stopping flags when exceptions occur,
matching the pattern already applied in ChargingStation lifecycle.

4 days agofix(ocpp16): align BootNotification runtime schema maxLength with spec §6.3
Jérôme Benoit [Mon, 16 Mar 2026 14:57:13 +0000 (15:57 +0100)] 
fix(ocpp16): align BootNotification runtime schema maxLength with spec §6.3

chargePointSerialNumber, chargeBoxSerialNumber, meterSerialNumber:
50 → 25 (CiString25Type per spec §6.3 + OCA JSON schema)

4 days agorefactor(ocpp20): harmonize per-station state naming
Jérôme Benoit [Mon, 16 Mar 2026 13:21:23 +0000 (14:21 +0100)] 
refactor(ocpp20): harmonize per-station state naming

- OCPP20PerStationState → OCPP20StationState (drop redundant 'Per')
- stationStates → stationsState (plural: collection of all stations)
- ss → stationState (descriptive local variable name)

4 days agorefactor(ocpp20): remove redundant lastFirmwareStatus from per-station state
Jérôme Benoit [Mon, 16 Mar 2026 13:09:55 +0000 (14:09 +0100)] 
refactor(ocpp20): remove redundant lastFirmwareStatus from per-station state

lastFirmwareStatus in the WeakMap was a duplicate of
stationInfo.firmwareStatus (now written by sendFirmwareStatusNotification).

TriggerMessage now uses hasFirmwareUpdateInProgress() to determine
whether to send Idle or the current intermediate state, which also
fixes a bug where failure end states (DownloadFailed, InstallationFailed,
etc.) were returned instead of Idle.

4 days agorefactor: use union types consistently in common code and tests
Jérôme Benoit [Mon, 16 Mar 2026 12:36:04 +0000 (13:36 +0100)] 
refactor: use union types consistently in common code and tests

- OCPPServiceUtils: use SampledValue union in generics, use
  StatusNotificationRequest union in satisfies, remove unused
  OCPP20StatusNotificationRequest import
- BroadcastChannel: use MeterValuesRequest/Response unions
- OCPPServiceUtils-validation.test: OCPP16MessageTrigger → MessageTrigger,
  remove redundant casts

4 days agorefactor(ocpp): use union types in common code where possible
Jérôme Benoit [Mon, 16 Mar 2026 12:27:33 +0000 (13:27 +0100)] 
refactor(ocpp): use union types in common code where possible

- OCPPServiceUtils: use SampledValue union in generic constraints
  instead of inline OCPP16SampledValue | OCPP20SampledValue union;
  use StatusNotificationRequest union in satisfies check
- BroadcastChannel: use MeterValuesRequest/Response unions instead
  of OCPP20-specific types in version-dispatched handleMeterValues

Stack-specific types remain in version-switch branches where
TypeScript requires narrowed types for array push operations.

4 days agorefactor(tests): use union types instead of stack-specific types in common tests
Jérôme Benoit [Mon, 16 Mar 2026 12:20:30 +0000 (13:20 +0100)] 
refactor(tests): use union types instead of stack-specific types in common tests

Replace OCPP16RequestCommand → RequestCommand,
OCPP16IncomingRequestCommand → IncomingRequestCommand, and
OCPP20RequestCommand → RequestCommand in test files outside
stack-specific directories. Remove redundant 'as Type' casts
that were bridging the gap.

4 days agorefactor(ocpp20): prefix OperationalStatusEnumType with OCPP20
Jérôme Benoit [Mon, 16 Mar 2026 12:11:41 +0000 (13:11 +0100)] 
refactor(ocpp20): prefix OperationalStatusEnumType with OCPP20

Last unprefixed OCPP 2.0 type participating in a common union.
OperationalStatusEnumType → OCPP20OperationalStatusEnumType
matching the OCPP16AvailabilityType counterpart in the
AvailabilityType union.

4 days agorefactor(ocpp20): prefix FirmwareStatusEnumType, extend FirmwareStatus union
Jérôme Benoit [Mon, 16 Mar 2026 12:00:33 +0000 (13:00 +0100)] 
refactor(ocpp20): prefix FirmwareStatusEnumType, extend FirmwareStatus union

- Rename FirmwareStatusEnumType → OCPP20FirmwareStatusEnumType per
  existing OCPP16FirmwareStatus naming convention
- Extend FirmwareStatus union to include all OCPP 2.0.1 firmware states
- Extend FirmwareStatusNotificationRequest to union both versions
- Write firmwareStatus to stationInfo in sendFirmwareStatusNotification
  fixing hasFirmwareUpdateInProgress which was always returning false
- Stack-specific code uses OCPP20FirmwareStatusEnumType, common code
  uses generic FirmwareStatus union

5 days agofix(ocpp16): reject float values for integer configuration keys per spec §5.3
Jérôme Benoit [Mon, 16 Mar 2026 00:20:36 +0000 (01:20 +0100)] 
fix(ocpp16): reject float values for integer configuration keys per spec §5.3

Replace convertToInt (parseInt — silently truncates 3.7 to 3) with
Number.isInteger check. The spec §9.1 defines these keys as Type:
integer, and §5.3 requires Rejected for values that do not conform
to the expected format.

5 days agochore: add .mypy_cache and .pytest_cache to root .gitignore
Jérôme Benoit [Mon, 16 Mar 2026 00:08:28 +0000 (01:08 +0100)] 
chore: add .mypy_cache and .pytest_cache to root .gitignore

5 days agofix(ocpp16): return Idle in TriggerMessage when not actively uploading/updating
Jérôme Benoit [Sun, 15 Mar 2026 23:59:05 +0000 (00:59 +0100)] 
fix(ocpp16): return Idle in TriggerMessage when not actively uploading/updating

Per spec §4.4/§7.24 and §4.5/§7.25, Idle SHALL only be sent via
TriggerMessage when not busy. The code was returning stale terminal
statuses (Uploaded, UploadFailed, Installed, etc.) instead of Idle.

- DiagnosticsStatusNotification: return Uploading only when actively
  uploading, Idle otherwise (fixes stale Uploaded/UploadFailed)
- FirmwareStatusNotification: return Downloading/Downloaded/Installing
  only when in progress, Idle otherwise (fixes stale Installed/Failed)
- UpdateFirmware guard: allow retry after DownloadFailed or
  InstallationFailed instead of blocking all non-Installed statuses

5 days agochore: release main (#1708) ocpp-server@v3.1.0 simulator@v3.1.0 v3 v3.1 webui@v3.1.0
Jérôme Benoit [Sun, 15 Mar 2026 23:35:18 +0000 (00:35 +0100)] 
chore: release main (#1708)

* chore: release main

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
5 days agochore: exclude .mypy_cache from prettier scanning
Jérôme Benoit [Sun, 15 Mar 2026 23:31:37 +0000 (00:31 +0100)] 
chore: exclude .mypy_cache from prettier scanning

5 days agorefactor: replace 54 unsafe error casts with instanceof guards
Jérôme Benoit [Sun, 15 Mar 2026 23:13:20 +0000 (00:13 +0100)] 
refactor: replace 54 unsafe error casts with instanceof guards

All 'error as Error', 'error as OCPPError', and 'error as
NodeJS.ErrnoException' casts replaced with proper instanceof
type narrowing across 16 files. Uses the pattern already
established in the auth strategies module:
  error instanceof Error ? error.message : String(error)

For call sites that pass error objects to functions, wraps
non-Error values: error instanceof Error ? error : new Error(String(error))

5 days agorefactor(ocpp): extract responseHandler and incomingRequestHandler into base classes
Jérôme Benoit [Sun, 15 Mar 2026 22:57:20 +0000 (23:57 +0100)] 
refactor(ocpp): extract responseHandler and incomingRequestHandler into base classes

Both methods were 95% identical across OCPP 1.6 and 2.0 subclasses
(~160 lines of pure duplication). Move the shared logic into the base
classes, parameterized by 3 abstract properties each subclass provides:
- bootNotificationRequestCommand / pendingStateBlockedCommands
- csmsName ('central system' vs 'CSMS')
- isRequestCommandSupported / isIncomingRequestCommandSupported

Zero behavior change — pure refactoring validated by 1853 passing tests.

5 days agofix(ocpp): use per-subclass singleton map to prevent version collision
Jérôme Benoit [Sun, 15 Mar 2026 22:37:22 +0000 (23:37 +0100)] 
fix(ocpp): use per-subclass singleton map to prevent version collision

The singleton instance was stored as a single static field on the base
class. In mixed OCPP 1.6/2.0 workers, the first getInstance() call
wins — subsequent calls for a different version silently return the
wrong instance cast via 'as T'.

Replace with Map<Constructor, Instance> keyed by the concrete subclass
constructor. Each version now gets its own singleton entry. Zero
changes needed in subclasses or calling code.

5 days agorefactor(ocpp20): extract named constants for hardcoded sleep/timeout values
Jérôme Benoit [Sun, 15 Mar 2026 22:24:50 +0000 (23:24 +0100)] 
refactor(ocpp20): extract named constants for hardcoded sleep/timeout values

Replace 8 magic numbers with semantic constants:
- FIRMWARE_STATUS_DELAY_MS, FIRMWARE_INSTALL_DELAY_MS,
  FIRMWARE_VERIFY_DELAY_MS for firmware lifecycle simulation
- LOG_UPLOAD_STEP_DELAY_MS for log upload simulation
- RESET_DELAY_MS, RESET_IDLE_MONITOR_INTERVAL_MS for reset scheduling
- CERTIFICATE_VERIFY_DELAY_MS for auth strategy retry delay

5 days agofix(charging-station): add try/finally guards to lifecycle methods
Jérôme Benoit [Sun, 15 Mar 2026 22:24:20 +0000 (23:24 +0100)] 
fix(charging-station): add try/finally guards to lifecycle methods

Prevent zombie state flags when exceptions occur:
- start(): wrap in try/finally to always reset starting=false
- stop(): wrap in try/finally to always reset stopping=false
- reset(): catch stop() failure and abort instead of proceeding
  with sleep+start on a half-stopped station
- delete(): catch stop() failure so cleanup always proceeds

5 days agorefactor(ocpp-server): parametrize failure-path tests to eliminate duplication
Jérôme Benoit [Sun, 15 Mar 2026 21:52:18 +0000 (22:52 +0100)] 
refactor(ocpp-server): parametrize failure-path tests to eliminate duplication

5 days agorefactor(ocpp-server): deduplicate outgoing commands, harden timer, expand test coverage
Jérôme Benoit [Sun, 15 Mar 2026 21:43:56 +0000 (22:43 +0100)] 
refactor(ocpp-server): deduplicate outgoing commands, harden timer, expand test coverage

5 days agochore(ocpp-server): migrate pyproject.toml to PEP 621 project metadata
Jérôme Benoit [Sun, 15 Mar 2026 20:43:50 +0000 (21:43 +0100)] 
chore(ocpp-server): migrate pyproject.toml to PEP 621 project metadata

Move name, version, description, authors, readme from deprecated
[tool.poetry.*] to [project.*] per PEP 621. Move dependencies from
[tool.poetry.dependencies] to [project.dependencies] with PEP 508
specifiers. Set requires-python to >=3.12,<4.0 to match ocpp package
constraint. Regenerate poetry.lock.

5 days agofix(tests): remove stale optional chaining on now-required variableAttribute
Jérôme Benoit [Sun, 15 Mar 2026 20:29:21 +0000 (21:29 +0100)] 
fix(tests): remove stale optional chaining on now-required variableAttribute

ReportDataType.variableAttribute was made required in 429cdbe3 but
test files still used ?. and ?? [] on the field. The stale eslint
cache masked these errors locally; CI (no cache) correctly rejected.

5 days agostyle(ocpp20): condense B09.FR.31 comment to single line per codebase convention
Jérôme Benoit [Sun, 15 Mar 2026 20:24:39 +0000 (21:24 +0100)] 
style(ocpp20): condense B09.FR.31 comment to single line per codebase convention

5 days agofix(ocpp20): implement proper M04.FR.06 guard via isChargingStationCertificateHash
Jérôme Benoit [Sun, 15 Mar 2026 20:16:10 +0000 (21:16 +0100)] 
fix(ocpp20): implement proper M04.FR.06 guard via isChargingStationCertificateHash

The previous M04.FR.06 guard was ineffective: it called
getInstalledCertificates() which scans root cert directories and
maps types via mapInstallTypeToGetType(), which has no case for
ChargingStationCertificate (stored via CertificateSigned, not
InstallCertificate). The V2GCertificateChain filter never matched.

Add isChargingStationCertificateHash() to OCPP20CertificateManager
that directly scans the ChargingStationCertificate directory and
compares certificate hashes. Use it in handleRequestDeleteCertificate
for a reliable M04.FR.06 guard.

5 days agofix(ocpp20): remediate 4 conformance findings from cross-audit
Jérôme Benoit [Sun, 15 Mar 2026 20:04:35 +0000 (21:04 +0100)] 
fix(ocpp20): remediate 4 conformance findings from cross-audit

- M04.FR.06: Narrow DeleteCertificate guard to V2GCertificateChain type
  only, allowing legitimate deletion of root certificates (CSMSRoot,
  ManufacturerRoot, MORootCert, V2GRoot)
- B09.FR.05: Use InvalidConfSlot reasonCode per errata 2025-09 when
  configurationSlot is not in NetworkConfigurationPriority list
- B09.FR.04/FR.31: Check AllowSecurityProfileDowngrade variable before
  rejecting downgrades — allow 3→2 when true, never allow to profile 1
  (errata 2025-09 §2.12)
- L01.FR.06: Wait for active transactions to end before commencing
  firmware installation via polling loop in lifecycle simulation