]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor(ocpp20): prefix FirmwareStatusEnumType, extend FirmwareStatus union
authorJérôme Benoit <jerome.benoit@sap.com>
Mon, 16 Mar 2026 12:00:33 +0000 (13:00 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Mon, 16 Mar 2026 12:00:33 +0000 (13:00 +0100)
- 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

src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20RequestService.ts
src/charging-station/ocpp/2.0/__testable__/OCPP20RequestServiceTestable.ts
src/types/index.ts
src/types/ocpp/2.0/Common.ts
src/types/ocpp/2.0/Requests.ts
src/types/ocpp/Requests.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-UpdateFirmware.test.ts
tests/charging-station/ocpp/2.0/OCPP20RequestService-FirmwareStatusNotification.test.ts

index c4c25456491cd5ce07e0512af80bc27b02aa5172..5362441c7d3fac8cf3f1809e8ae63bd46208bd82 100644 (file)
@@ -24,8 +24,6 @@ import {
   DeleteCertificateStatusEnumType,
   ErrorType,
   type EvseStatus,
-  FirmwareStatus,
-  FirmwareStatusEnumType,
   type FirmwareType,
   GenericDeviceModelStatusEnumType,
   GenericStatus,
@@ -55,6 +53,7 @@ import {
   type OCPP20DeleteCertificateRequest,
   type OCPP20DeleteCertificateResponse,
   OCPP20DeviceInfoVariableName,
+  OCPP20FirmwareStatusEnumType,
   type OCPP20FirmwareStatusNotificationRequest,
   type OCPP20FirmwareStatusNotificationResponse,
   type OCPP20GetBaseReportRequest,
@@ -153,7 +152,7 @@ const moduleName = 'OCPP20IncomingRequestService'
 interface OCPP20PerStationState {
   activeFirmwareUpdateAbortController?: AbortController
   activeFirmwareUpdateRequestId?: number
-  lastFirmwareStatus?: FirmwareStatusEnumType
+  lastFirmwareStatus?: OCPP20FirmwareStatusEnumType
   preInoperativeConnectorStatuses: Map<number, OCPP20ConnectorStatusEnumType>
   reportDataCache: Map<number, ReportDataType[]>
 }
@@ -367,8 +366,8 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
             const ss = this.getStationState(chargingStation)
             const firmwareStatus =
               ss.lastFirmwareStatus == null ||
-              ss.lastFirmwareStatus === FirmwareStatusEnumType.Installed
-                ? FirmwareStatusEnumType.Idle
+              ss.lastFirmwareStatus === OCPP20FirmwareStatusEnumType.Installed
+                ? OCPP20FirmwareStatusEnumType.Idle
                 : ss.lastFirmwareStatus
             chargingStation.ocppRequestService
               .requestHandler<
@@ -2807,9 +2806,14 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
   private hasFirmwareUpdateInProgress (chargingStation: ChargingStation): boolean {
     const firmwareStatus = chargingStation.stationInfo?.firmwareStatus
     return (
-      firmwareStatus === FirmwareStatus.Downloading ||
-      firmwareStatus === FirmwareStatus.Downloaded ||
-      firmwareStatus === FirmwareStatus.Installing
+      firmwareStatus === OCPP20FirmwareStatusEnumType.Downloading ||
+      firmwareStatus === OCPP20FirmwareStatusEnumType.Downloaded ||
+      firmwareStatus === OCPP20FirmwareStatusEnumType.Installing ||
+      firmwareStatus === OCPP20FirmwareStatusEnumType.DownloadScheduled ||
+      firmwareStatus === OCPP20FirmwareStatusEnumType.DownloadPaused ||
+      firmwareStatus === OCPP20FirmwareStatusEnumType.InstallScheduled ||
+      firmwareStatus === OCPP20FirmwareStatusEnumType.InstallRebooting ||
+      firmwareStatus === OCPP20FirmwareStatusEnumType.SignatureVerified
     )
   }
 
@@ -3137,10 +3141,13 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
 
   private sendFirmwareStatusNotification (
     chargingStation: ChargingStation,
-    status: FirmwareStatusEnumType,
+    status: OCPP20FirmwareStatusEnumType,
     requestId: number
   ): Promise<OCPP20FirmwareStatusNotificationResponse> {
     this.getStationState(chargingStation).lastFirmwareStatus = status
+    if (chargingStation.stationInfo != null) {
+      chargingStation.stationInfo.firmwareStatus = status
+    }
     return chargingStation.ocppRequestService.requestHandler<
       OCPP20FirmwareStatusNotificationRequest,
       OCPP20FirmwareStatusNotificationResponse
@@ -3338,7 +3345,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
     if (retrieveTime > now) {
       await this.sendFirmwareStatusNotification(
         chargingStation,
-        FirmwareStatusEnumType.DownloadScheduled,
+        OCPP20FirmwareStatusEnumType.DownloadScheduled,
         requestId
       )
       await sleep(retrieveTime - now)
@@ -3347,7 +3354,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
 
     await this.sendFirmwareStatusNotification(
       chargingStation,
-      FirmwareStatusEnumType.Downloading,
+      OCPP20FirmwareStatusEnumType.Downloading,
       requestId
     )
 
@@ -3358,7 +3365,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
     if (location.trim() === '' || !this.isValidFirmwareLocation(location)) {
       await this.sendFirmwareStatusNotification(
         chargingStation,
-        FirmwareStatusEnumType.DownloadFailed,
+        OCPP20FirmwareStatusEnumType.DownloadFailed,
         requestId
       )
       logger.warn(
@@ -3370,7 +3377,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
 
     await this.sendFirmwareStatusNotification(
       chargingStation,
-      FirmwareStatusEnumType.Downloaded,
+      OCPP20FirmwareStatusEnumType.Downloaded,
       requestId
     )
 
@@ -3379,7 +3386,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       if (checkAborted()) return
       await this.sendFirmwareStatusNotification(
         chargingStation,
-        FirmwareStatusEnumType.SignatureVerified,
+        OCPP20FirmwareStatusEnumType.SignatureVerified,
         requestId
       )
     }
@@ -3391,7 +3398,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       if (installTime > currentTime) {
         await this.sendFirmwareStatusNotification(
           chargingStation,
-          FirmwareStatusEnumType.InstallScheduled,
+          OCPP20FirmwareStatusEnumType.InstallScheduled,
           requestId
         )
         await sleep(installTime - currentTime)
@@ -3415,7 +3422,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
 
     await this.sendFirmwareStatusNotification(
       chargingStation,
-      FirmwareStatusEnumType.Installing,
+      OCPP20FirmwareStatusEnumType.Installing,
       requestId
     )
 
@@ -3423,7 +3430,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
     if (checkAborted()) return
     await this.sendFirmwareStatusNotification(
       chargingStation,
-      FirmwareStatusEnumType.Installed,
+      OCPP20FirmwareStatusEnumType.Installed,
       requestId
     )
 
index aeb14a0e7192f1b4c527ca2a9f61ae568b2dece3..2bf1d99ee1d7d6f1cac46e28d85e9ff49db08f11 100644 (file)
@@ -8,9 +8,9 @@ import {
   type CertificateActionEnumType,
   type CertificateSigningUseEnumType,
   ErrorType,
-  type FirmwareStatusEnumType,
   type JsonObject,
   type JsonType,
+  type OCPP20FirmwareStatusEnumType,
   type OCPP20FirmwareStatusNotificationRequest,
   type OCPP20FirmwareStatusNotificationResponse,
   type OCPP20Get15118EVCertificateRequest,
@@ -99,7 +99,7 @@ export class OCPP20RequestService extends OCPPRequestService {
    */
   public async requestFirmwareStatusNotification (
     chargingStation: ChargingStation,
-    status: FirmwareStatusEnumType,
+    status: OCPP20FirmwareStatusEnumType,
     requestId?: number
   ): Promise<OCPP20FirmwareStatusNotificationResponse> {
     logger.debug(
index c1bcd313d4816b6d6e60085e6bb7f0ef704e3957..a21ac2687c2a0d20d4f35c849ae82d16ee20143d 100644 (file)
@@ -24,8 +24,8 @@ import { mock } from 'node:test'
 import type {
   CertificateActionEnumType,
   CertificateSigningUseEnumType,
-  FirmwareStatusEnumType,
   JsonType,
+  OCPP20FirmwareStatusEnumType,
   OCPP20FirmwareStatusNotificationResponse,
   OCPP20Get15118EVCertificateResponse,
   OCPP20GetCertificateStatusResponse,
@@ -88,7 +88,7 @@ export interface TestableOCPP20RequestService {
    */
   requestFirmwareStatusNotification: (
     chargingStation: ChargingStation,
-    status: FirmwareStatusEnumType,
+    status: OCPP20FirmwareStatusEnumType,
     requestId?: number
   ) => Promise<OCPP20FirmwareStatusNotificationResponse>
 
index 24f41f49ad5c0f4bf020667419729ef88256f679..8cf719fca89ccfc7c0ea19760b8b2652b4859c69 100644 (file)
@@ -153,7 +153,6 @@ export {
   DataEnumType,
   DataTransferStatusEnumType,
   DeleteCertificateStatusEnumType,
-  FirmwareStatusEnumType,
   type FirmwareType,
   GenericDeviceModelStatusEnumType,
   GetCertificateIdUseEnumType,
@@ -169,6 +168,7 @@ export {
   MessageTriggerEnumType,
   type NetworkConnectionProfileType,
   OCPP20ComponentName,
+  OCPP20FirmwareStatusEnumType,
   OCPP20UnitEnumType,
   type OCSPRequestDataType,
   OperationalStatusEnumType,
index 9e755a227de2ff9836434b59b2c260d96e1af76a..cb2b72b875f3903a7cd0567a59e8d48807dfb6c5 100644 (file)
@@ -66,23 +66,6 @@ export enum DeleteCertificateStatusEnumType {
   NotFound = 'NotFound',
 }
 
-export enum FirmwareStatusEnumType {
-  Downloaded = 'Downloaded',
-  DownloadFailed = 'DownloadFailed',
-  Downloading = 'Downloading',
-  DownloadPaused = 'DownloadPaused',
-  DownloadScheduled = 'DownloadScheduled',
-  Idle = 'Idle',
-  InstallationFailed = 'InstallationFailed',
-  Installed = 'Installed',
-  Installing = 'Installing',
-  InstallRebooting = 'InstallRebooting',
-  InstallScheduled = 'InstallScheduled',
-  InstallVerificationFailed = 'InstallVerificationFailed',
-  InvalidSignature = 'InvalidSignature',
-  SignatureVerified = 'SignatureVerified',
-}
-
 export enum GenericDeviceModelStatusEnumType {
   Accepted = 'Accepted',
   EmptyResultSet = 'EmptyResultSet',
@@ -235,6 +218,23 @@ export enum OCPP20ComponentName {
   VehicleIdSensor = 'VehicleIdSensor',
 }
 
+export enum OCPP20FirmwareStatusEnumType {
+  Downloaded = 'Downloaded',
+  DownloadFailed = 'DownloadFailed',
+  Downloading = 'Downloading',
+  DownloadPaused = 'DownloadPaused',
+  DownloadScheduled = 'DownloadScheduled',
+  Idle = 'Idle',
+  InstallationFailed = 'InstallationFailed',
+  Installed = 'Installed',
+  Installing = 'Installing',
+  InstallRebooting = 'InstallRebooting',
+  InstallScheduled = 'InstallScheduled',
+  InstallVerificationFailed = 'InstallVerificationFailed',
+  InvalidSignature = 'InvalidSignature',
+  SignatureVerified = 'SignatureVerified',
+}
+
 export enum OCPP20UnitEnumType {
   AMP = 'A',
   ARBITRARY_STRENGTH_UNIT = 'ASU',
index 0b3706924926b864ad9c18ad2fcf59d837f14643..b22a17b747c957481a0dab83db4b1358012eb5e8 100644 (file)
@@ -8,7 +8,6 @@ import type {
   CertificateSigningUseEnumType,
   ChargingStationType,
   CustomDataType,
-  FirmwareStatusEnumType,
   FirmwareType,
   GetCertificateIdUseEnumType,
   InstallCertificateUseEnumType,
@@ -16,6 +15,7 @@ import type {
   LogParametersType,
   MessageTriggerEnumType,
   NetworkConnectionProfileType,
+  OCPP20FirmwareStatusEnumType,
   OCSPRequestDataType,
   OperationalStatusEnumType,
   ReportBaseEnumType,
@@ -118,7 +118,7 @@ export interface OCPP20DeleteCertificateRequest extends JsonObject {
 export interface OCPP20FirmwareStatusNotificationRequest extends JsonObject {
   customData?: CustomDataType
   requestId?: number
-  status: FirmwareStatusEnumType
+  status: OCPP20FirmwareStatusEnumType
 }
 
 export interface OCPP20Get15118EVCertificateRequest extends JsonObject {
index f3a93972ebf9f30b1bca57afab83cf135daceeee..f7df86a50416f6dfec8fd38812a96ce224c7f79b 100644 (file)
@@ -21,9 +21,10 @@ import {
   type OCPP16ReserveNowRequest,
   type OCPP16StatusNotificationRequest,
 } from './1.6/Requests.js'
-import { OperationalStatusEnumType } from './2.0/Common.js'
+import { OCPP20FirmwareStatusEnumType, OperationalStatusEnumType } from './2.0/Common.js'
 import {
   type OCPP20BootNotificationRequest,
+  type OCPP20FirmwareStatusNotificationRequest,
   OCPP20IncomingRequestCommand,
   OCPP20RequestCommand,
   type OCPP20StatusNotificationRequest,
@@ -44,7 +45,9 @@ export type DiagnosticsStatusNotificationRequest = OCPP16DiagnosticsStatusNotifi
 
 export type ErrorCallback = (ocppError: OCPPError, requestStatistic?: boolean) => void
 
-export type FirmwareStatusNotificationRequest = OCPP16FirmwareStatusNotificationRequest
+export type FirmwareStatusNotificationRequest =
+  | OCPP16FirmwareStatusNotificationRequest
+  | OCPP20FirmwareStatusNotificationRequest
 
 export type HeartbeatRequest = OCPP16HeartbeatRequest
 
@@ -108,9 +111,10 @@ export type DiagnosticsStatus = OCPP16DiagnosticsStatus
 
 export const FirmwareStatus = {
   ...OCPP16FirmwareStatus,
+  ...OCPP20FirmwareStatusEnumType,
 } as const
 // eslint-disable-next-line @typescript-eslint/no-redeclare
-export type FirmwareStatus = OCPP16FirmwareStatus
+export type FirmwareStatus = OCPP16FirmwareStatus | OCPP20FirmwareStatusEnumType
 
 export type ReserveNowRequest = OCPP16ReserveNowRequest
 
index 29965dc8abb97db7b34597727f10c29113fc240b..f4b60c802155c54b960bee030d8f7557b98a2bfe 100644 (file)
@@ -11,8 +11,8 @@ import type { ChargingStation } from '../../../../src/charging-station/index.js'
 import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
 import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
 import {
-  FirmwareStatusEnumType,
   type FirmwareType,
+  OCPP20FirmwareStatusEnumType,
   OCPP20IncomingRequestCommand,
   OCPP20RequestCommand,
   type OCPP20UpdateFirmwareRequest,
@@ -349,7 +349,7 @@ await describe('L01/L02 - UpdateFirmware', async () => {
 
         await flushMicrotasks()
         assert.strictEqual(sentRequests.length, 1)
-        assert.strictEqual(sentRequests[0].payload.status, FirmwareStatusEnumType.Downloading)
+        assert.strictEqual(sentRequests[0].payload.status, OCPP20FirmwareStatusEnumType.Downloading)
 
         const secondRequest: OCPP20UpdateFirmwareRequest = {
           firmware: {
@@ -397,18 +397,18 @@ await describe('L01/L02 - UpdateFirmware', async () => {
           sentRequests[0].command,
           OCPP20RequestCommand.FIRMWARE_STATUS_NOTIFICATION
         )
-        assert.strictEqual(sentRequests[0].payload.status, FirmwareStatusEnumType.Downloading)
+        assert.strictEqual(sentRequests[0].payload.status, OCPP20FirmwareStatusEnumType.Downloading)
         assert.strictEqual(sentRequests[0].payload.requestId, 1)
 
         t.mock.timers.tick(2000)
         await flushMicrotasks()
         assert.strictEqual(sentRequests.length, 3)
-        assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.Downloaded)
-        assert.strictEqual(sentRequests[2].payload.status, FirmwareStatusEnumType.Installing)
+        assert.strictEqual(sentRequests[1].payload.status, OCPP20FirmwareStatusEnumType.Downloaded)
+        assert.strictEqual(sentRequests[2].payload.status, OCPP20FirmwareStatusEnumType.Installing)
 
         t.mock.timers.tick(1000)
         await flushMicrotasks()
-        assert.strictEqual(sentRequests[3].payload.status, FirmwareStatusEnumType.Installed)
+        assert.strictEqual(sentRequests[3].payload.status, OCPP20FirmwareStatusEnumType.Installed)
 
         // H11: SecurityEventNotification for FirmwareUpdated
         assert.strictEqual(sentRequests.length, 5)
@@ -445,12 +445,15 @@ await describe('L01/L02 - UpdateFirmware', async () => {
 
         await flushMicrotasks()
         assert.strictEqual(sentRequests.length, 1)
-        assert.strictEqual(sentRequests[0].payload.status, FirmwareStatusEnumType.Downloading)
+        assert.strictEqual(sentRequests[0].payload.status, OCPP20FirmwareStatusEnumType.Downloading)
 
         t.mock.timers.tick(2000)
         await flushMicrotasks()
         assert.strictEqual(sentRequests.length, 2)
-        assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.DownloadFailed)
+        assert.strictEqual(
+          sentRequests[1].payload.status,
+          OCPP20FirmwareStatusEnumType.DownloadFailed
+        )
         assert.strictEqual(sentRequests[1].payload.requestId, 7)
       })
     })
@@ -483,7 +486,10 @@ await describe('L01/L02 - UpdateFirmware', async () => {
         await flushMicrotasks()
 
         assert.strictEqual(sentRequests.length, 2)
-        assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.DownloadFailed)
+        assert.strictEqual(
+          sentRequests[1].payload.status,
+          OCPP20FirmwareStatusEnumType.DownloadFailed
+        )
         assert.strictEqual(sentRequests[1].payload.requestId, 8)
       })
     })
@@ -560,16 +566,19 @@ await describe('L01/L02 - UpdateFirmware', async () => {
         t.mock.timers.tick(2000)
         await flushMicrotasks()
         assert.strictEqual(sentRequests.length, 2)
-        assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.Downloaded)
+        assert.strictEqual(sentRequests[1].payload.status, OCPP20FirmwareStatusEnumType.Downloaded)
 
         t.mock.timers.tick(500)
         await flushMicrotasks()
-        assert.strictEqual(sentRequests[2].payload.status, FirmwareStatusEnumType.SignatureVerified)
-        assert.strictEqual(sentRequests[3].payload.status, FirmwareStatusEnumType.Installing)
+        assert.strictEqual(
+          sentRequests[2].payload.status,
+          OCPP20FirmwareStatusEnumType.SignatureVerified
+        )
+        assert.strictEqual(sentRequests[3].payload.status, OCPP20FirmwareStatusEnumType.Installing)
 
         t.mock.timers.tick(1000)
         await flushMicrotasks()
-        assert.strictEqual(sentRequests[4].payload.status, FirmwareStatusEnumType.Installed)
+        assert.strictEqual(sentRequests[4].payload.status, OCPP20FirmwareStatusEnumType.Installed)
 
         // H11: SecurityEventNotification after Installed
         assert.strictEqual(sentRequests.length, 6)
index e279940a5b37b54095133fb26332f6f514070e50..f4eec9a78b093a7f7b305e7b54bfb0c1c02ddc3f 100644 (file)
@@ -14,7 +14,7 @@ import {
   type TestableOCPP20RequestService,
 } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
 import {
-  FirmwareStatusEnumType,
+  OCPP20FirmwareStatusEnumType,
   type OCPP20FirmwareStatusNotificationRequest,
   type OCPP20FirmwareStatusNotificationResponse,
   OCPP20RequestCommand,
@@ -56,13 +56,17 @@ await describe('J01 - FirmwareStatusNotification', async () => {
   })
 
   await it('should send FirmwareStatusNotification with Downloading status', async () => {
-    await service.requestFirmwareStatusNotification(station, FirmwareStatusEnumType.Downloading, 42)
+    await service.requestFirmwareStatusNotification(
+      station,
+      OCPP20FirmwareStatusEnumType.Downloading,
+      42
+    )
 
     assert.strictEqual(sendMessageMock.mock.calls.length, 1)
 
     const sentPayload = sendMessageMock.mock.calls[0]
       .arguments[2] as OCPP20FirmwareStatusNotificationRequest
-    assert.strictEqual(sentPayload.status, FirmwareStatusEnumType.Downloading)
+    assert.strictEqual(sentPayload.status, OCPP20FirmwareStatusEnumType.Downloading)
     assert.strictEqual(sentPayload.requestId, 42)
 
     const commandName = sendMessageMock.mock.calls[0].arguments[3]
@@ -74,7 +78,7 @@ await describe('J01 - FirmwareStatusNotification', async () => {
 
     await service.requestFirmwareStatusNotification(
       station,
-      FirmwareStatusEnumType.Installed,
+      OCPP20FirmwareStatusEnumType.Installed,
       testRequestId
     )
 
@@ -82,14 +86,14 @@ await describe('J01 - FirmwareStatusNotification', async () => {
 
     const sentPayload = sendMessageMock.mock.calls[0]
       .arguments[2] as OCPP20FirmwareStatusNotificationRequest
-    assert.strictEqual(sentPayload.status, FirmwareStatusEnumType.Installed)
+    assert.strictEqual(sentPayload.status, OCPP20FirmwareStatusEnumType.Installed)
     assert.strictEqual(sentPayload.requestId, testRequestId)
   })
 
   await it('should return empty response from CSMS', async () => {
     const response = await service.requestFirmwareStatusNotification(
       station,
-      FirmwareStatusEnumType.Downloaded,
+      OCPP20FirmwareStatusEnumType.Downloaded,
       1
     )