]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
feat(ocpp2): implement GetBaseReport and NotifyReport commands (#1556)
authorCopilot <198982749+Copilot@users.noreply.github.com>
Tue, 21 Oct 2025 14:18:42 +0000 (16:18 +0200)
committerGitHub <noreply@github.com>
Tue, 21 Oct 2025 14:18:42 +0000 (16:18 +0200)
* Initial plan

* Add GetBaseReport OCPP 2.0 command implementation

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* fix: correct enum ordering and add NotifyReport handler to mock server

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* feat: add NotifyReport command and trigger from GetBaseReport

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* feat: populate NotifyReport with device model data based on reportBase

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* refactor: improve code quality with typing, validation and constants

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* refactor: export types, separate concerns, move constants

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* refactor: use event emission pattern and static class properties

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* refactor: use direct method call pattern for NotifyReport

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* refactor: use event emission pattern for GetBaseReport/NotifyReport

Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
* Update src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jerome-benoit <6537332+jerome-benoit@users.noreply.github.com>
Co-authored-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
src/charging-station/ocpp/2.0/OCPP20Constants.ts
src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20RequestService.ts
src/charging-station/ocpp/2.0/OCPP20ResponseService.ts
src/types/index.ts
src/types/ocpp/2.0/Common.ts
src/types/ocpp/2.0/Requests.ts
src/types/ocpp/2.0/Responses.ts
tests/ocpp-server/server.py

index c94fbe9f10d486e2eeb6a98b52bf25ad8a7e70d7..93c37f4058f35dff6120af9e67328b39dd06476f 100644 (file)
@@ -5,6 +5,13 @@ import {
 import { OCPPConstants } from '../OCPPConstants.js'
 
 export class OCPP20Constants extends OCPPConstants {
+  static readonly ComponentName = {
+    CHARGING_STATION: 'ChargingStation',
+    CONNECTOR: 'Connector',
+    EVSE: 'EVSE',
+    OCPP_COMM_CTRLR: 'OCPPCommCtrlr',
+  } as const
+
   static readonly ChargingStationStatusTransitions: readonly ConnectorStatusTransition[] =
     Object.freeze([
       { to: OCPP20ConnectorStatusEnumType.Available },
index 23f4c024ef5bbb4c3979ea1c99405364f9a1fb31..05ce66aa9165917b228aaa2c98c5e929365487da 100644 (file)
@@ -6,15 +6,29 @@ import type { ChargingStation } from '../../../charging-station/index.js'
 
 import { OCPPError } from '../../../exception/index.js'
 import {
+  type ComponentType,
   ErrorType,
+  type EVSEType,
+  GenericDeviceModelStatusEnumType,
   type IncomingRequestHandler,
   type JsonType,
   type OCPP20ClearCacheRequest,
+  type OCPP20GetBaseReportRequest,
+  type OCPP20GetBaseReportResponse,
   OCPP20IncomingRequestCommand,
+  type OCPP20NotifyReportRequest,
+  type OCPP20NotifyReportResponse,
+  OCPP20RequestCommand,
   OCPPVersion,
+  ReportBaseEnumType,
+  type ReportDataType,
+  type VariableAttributeType,
+  type VariableCharacteristicsType,
+  type VariableType,
 } from '../../../types/index.js'
 import { isAsyncFunction, logger } from '../../../utils/index.js'
 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
+import { OCPP20Constants } from './OCPP20Constants.js'
 import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 
 const moduleName = 'OCPP20IncomingRequestService'
@@ -34,6 +48,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
     super(OCPPVersion.VERSION_201)
     this.incomingRequestHandlers = new Map<OCPP20IncomingRequestCommand, IncomingRequestHandler>([
       [OCPP20IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
+      [OCPP20IncomingRequestCommand.GET_BASE_REPORT, this.handleRequestGetBaseReport.bind(this)],
     ])
     this.payloadValidateFunctions = new Map<
       OCPP20IncomingRequestCommand,
@@ -49,7 +64,53 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
           )
         ),
       ],
+      [
+        OCPP20IncomingRequestCommand.GET_BASE_REPORT,
+        this.ajv.compile(
+          OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20GetBaseReportRequest>(
+            'assets/json-schemas/ocpp/2.0/GetBaseReportRequest.json',
+            moduleName,
+            'constructor'
+          )
+        ),
+      ],
     ])
+    // Handle incoming request events
+    this.on(
+      OCPP20IncomingRequestCommand.GET_BASE_REPORT,
+      (
+        chargingStation: ChargingStation,
+        request: OCPP20GetBaseReportRequest,
+        response: OCPP20GetBaseReportResponse
+      ) => {
+        if (response.status === GenericDeviceModelStatusEnumType.Accepted) {
+          const { requestId, reportBase } = request
+          const reportData = this.buildReportData(chargingStation, reportBase)
+          chargingStation.ocppRequestService
+            .requestHandler<OCPP20NotifyReportRequest, OCPP20NotifyReportResponse>(
+              chargingStation,
+              OCPP20RequestCommand.NOTIFY_REPORT,
+              {
+                reportData,
+                requestId,
+                seqNo: 0,
+                tbc: false,
+              }
+            )
+            .then(() => {
+              logger.info(
+                `${chargingStation.logPrefix()} ${moduleName}.constructor: NotifyReport sent for requestId ${requestId} with ${reportData.length} report items`
+              )
+            })
+            .catch((error: unknown) => {
+              logger.error(
+                `${chargingStation.logPrefix()} ${moduleName}.constructor: NotifyReport error:`,
+                error
+              )
+            })
+        }
+      }
+    )
     this.validatePayload = this.validatePayload.bind(this)
   }
 
@@ -142,6 +203,206 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
     this.emit(commandName, chargingStation, commandPayload, response)
   }
 
+  private handleRequestGetBaseReport (
+    chargingStation: ChargingStation,
+    commandPayload: OCPP20GetBaseReportRequest
+  ): OCPP20GetBaseReportResponse {
+    logger.info(
+      `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetBaseReport: GetBaseReport request received with requestId ${commandPayload.requestId} and reportBase ${commandPayload.reportBase}`
+    )
+    // Build report data to check if any data is available
+    const reportData = this.buildReportData(chargingStation, commandPayload.reportBase)
+    if (reportData.length === 0) {
+      logger.info(
+        `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetBaseReport: No data available for reportBase ${commandPayload.reportBase}`
+      )
+      return {
+        status: GenericDeviceModelStatusEnumType.EmptyResultSet,
+      }
+    }
+    return {
+      status: GenericDeviceModelStatusEnumType.Accepted,
+    }
+  }
+
+  private buildReportData (
+    chargingStation: ChargingStation,
+    reportBase: string
+  ): ReportDataType[] {
+    // Validate reportBase parameter
+    if (!Object.values(ReportBaseEnumType).includes(reportBase as ReportBaseEnumType)) {
+      logger.warn(
+        `${chargingStation.logPrefix()} ${moduleName}.buildReportData: Invalid reportBase '${reportBase}'`
+      )
+      return []
+    }
+
+    const reportData: ReportDataType[] = []
+
+    switch (reportBase) {
+      case ReportBaseEnumType.ConfigurationInventory:
+        // Include OCPP configuration keys
+        if (chargingStation.ocppConfiguration?.configurationKey) {
+          for (const configKey of chargingStation.ocppConfiguration.configurationKey) {
+            reportData.push({
+              component: {
+                name: OCPP20Constants.ComponentName.OCPP_COMM_CTRLR,
+              },
+              variable: {
+                name: configKey.key,
+              },
+              variableAttribute: [
+                {
+                  type: 'Actual',
+                  value: configKey.value,
+                },
+              ],
+              variableCharacteristics: {
+                dataType: 'string',
+                supportsMonitoring: false,
+              },
+            })
+          }
+        }
+        break
+
+      case ReportBaseEnumType.FullInventory:
+        // Include all device model variables
+        // 1. Station information
+        if (chargingStation.stationInfo) {
+          const stationInfo = chargingStation.stationInfo
+          if (stationInfo.chargePointModel) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.CHARGING_STATION },
+              variable: { name: 'Model' },
+              variableAttribute: [{ type: 'Actual', value: stationInfo.chargePointModel }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+          if (stationInfo.chargePointVendor) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.CHARGING_STATION },
+              variable: { name: 'VendorName' },
+              variableAttribute: [{ type: 'Actual', value: stationInfo.chargePointVendor }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+          if (stationInfo.chargePointSerialNumber) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.CHARGING_STATION },
+              variable: { name: 'SerialNumber' },
+              variableAttribute: [{ type: 'Actual', value: stationInfo.chargePointSerialNumber }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+          if (stationInfo.firmwareVersion) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.CHARGING_STATION },
+              variable: { name: 'FirmwareVersion' },
+              variableAttribute: [{ type: 'Actual', value: stationInfo.firmwareVersion }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+        }
+
+        // 2. OCPP configuration
+        if (chargingStation.ocppConfiguration?.configurationKey) {
+          for (const configKey of chargingStation.ocppConfiguration.configurationKey) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.OCPP_COMM_CTRLR },
+              variable: { name: configKey.key },
+              variableAttribute: [{ type: 'Actual', value: configKey.value }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+        }
+
+        // 3. EVSE and connector information
+        if (chargingStation.evses.size > 0) {
+          for (const [evseId, evse] of chargingStation.evses) {
+            reportData.push({
+              component: {
+                evse: { id: evseId },
+                name: 'EVSE',
+              },
+              variable: { name: 'AvailabilityState' },
+              variableAttribute: [{ type: 'Actual', value: evse.availability }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: true },
+            })
+            if (evse.connectors) {
+              for (const [connectorId, connector] of evse.connectors) {
+                reportData.push({
+                  component: {
+                    evse: { connectorId, id: evseId },
+                    name: 'Connector',
+                  },
+                  variable: { name: 'ConnectorType' },
+                  variableAttribute: [
+                    { type: 'Actual', value: String(connector.connectorType) },
+                  ],
+                  variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+                })
+              }
+            }
+          }
+        } else if (chargingStation.connectors.size > 0) {
+          // Fallback to connectors if no EVSE structure
+          for (const [connectorId, connector] of chargingStation.connectors) {
+            if (connectorId > 0) {
+              reportData.push({
+                component: {
+                  evse: { connectorId, id: 1 },
+                  name: 'Connector',
+                },
+                variable: { name: 'ConnectorType' },
+                variableAttribute: [{ type: 'Actual', value: String(connector.connectorType) }],
+                variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+              })
+            }
+          }
+        }
+        break
+
+      case ReportBaseEnumType.SummaryInventory:
+        // Include essential variables only
+        if (chargingStation.stationInfo) {
+          const stationInfo = chargingStation.stationInfo
+          if (stationInfo.chargePointModel) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.CHARGING_STATION },
+              variable: { name: 'Model' },
+              variableAttribute: [{ type: 'Actual', value: stationInfo.chargePointModel }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+          if (stationInfo.chargePointVendor) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.CHARGING_STATION },
+              variable: { name: 'VendorName' },
+              variableAttribute: [{ type: 'Actual', value: stationInfo.chargePointVendor }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+          if (stationInfo.firmwareVersion) {
+            reportData.push({
+              component: { name: OCPP20Constants.ComponentName.CHARGING_STATION },
+              variable: { name: 'FirmwareVersion' },
+              variableAttribute: [{ type: 'Actual', value: stationInfo.firmwareVersion }],
+              variableCharacteristics: { dataType: 'string', supportsMonitoring: false },
+            })
+          }
+        }
+        break
+
+      default:
+        logger.warn(
+          `${chargingStation.logPrefix()} ${moduleName}.buildReportData: Unknown reportBase '${reportBase}'`
+        )
+    }
+
+    return reportData
+  }
+
   private validatePayload (
     chargingStation: ChargingStation,
     commandName: OCPP20IncomingRequestCommand,
index 1e9253c9a7affe4156b2870c64e085f493b2bed1..57eeaca6a6587a3481fea0f5c67628e7d309bde0 100644 (file)
@@ -12,6 +12,7 @@ import {
   type JsonType,
   type OCPP20BootNotificationRequest,
   type OCPP20HeartbeatRequest,
+  type OCPP20NotifyReportRequest,
   OCPP20RequestCommand,
   type OCPP20StatusNotificationRequest,
   OCPPVersion,
@@ -53,6 +54,16 @@ export class OCPP20RequestService extends OCPPRequestService {
           )
         ),
       ],
+      [
+        OCPP20RequestCommand.NOTIFY_REPORT,
+        this.ajv.compile(
+          OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20NotifyReportRequest>(
+            'assets/json-schemas/ocpp/2.0/NotifyReportRequest.json',
+            moduleName,
+            'constructor'
+          )
+        ),
+      ],
       [
         OCPP20RequestCommand.STATUS_NOTIFICATION,
         this.ajv.compile(
@@ -106,6 +117,11 @@ export class OCPP20RequestService extends OCPPRequestService {
         return commandParams as unknown as Request
       case OCPP20RequestCommand.HEARTBEAT:
         return OCPP20Constants.OCPP_RESPONSE_EMPTY as unknown as Request
+      case OCPP20RequestCommand.NOTIFY_REPORT:
+        return {
+          generatedAt: new Date(),
+          ...commandParams,
+        } as unknown as Request
       case OCPP20RequestCommand.STATUS_NOTIFICATION:
         return {
           timestamp: new Date(),
index 93d03cc905c59481653b7335aec07e40f0301db1..c092059f2383d243a3606ad305a73b906f63f0dd 100644 (file)
@@ -10,8 +10,10 @@ import {
   type JsonType,
   type OCPP20BootNotificationResponse,
   type OCPP20ClearCacheResponse,
+  type OCPP20GetBaseReportResponse,
   type OCPP20HeartbeatResponse,
   OCPP20IncomingRequestCommand,
+  type OCPP20NotifyReportResponse,
   OCPP20OptionalVariableName,
   OCPP20RequestCommand,
   type OCPP20StatusNotificationResponse,
@@ -45,6 +47,7 @@ export class OCPP20ResponseService extends OCPPResponseService {
         this.handleResponseBootNotification.bind(this) as ResponseHandler,
       ],
       [OCPP20RequestCommand.HEARTBEAT, this.emptyResponseHandler],
+      [OCPP20RequestCommand.NOTIFY_REPORT, this.emptyResponseHandler],
       [OCPP20RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler],
     ])
     this.payloadValidateFunctions = new Map<OCPP20RequestCommand, ValidateFunction<JsonType>>([
@@ -68,6 +71,16 @@ export class OCPP20ResponseService extends OCPPResponseService {
           )
         ),
       ],
+      [
+        OCPP20RequestCommand.NOTIFY_REPORT,
+        this.ajv.compile(
+          OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20NotifyReportResponse>(
+            'assets/json-schemas/ocpp/2.0/NotifyReportResponse.json',
+            moduleName,
+            'constructor'
+          )
+        ),
+      ],
       [
         OCPP20RequestCommand.STATUS_NOTIFICATION,
         this.ajv.compile(
@@ -93,6 +106,16 @@ export class OCPP20ResponseService extends OCPPResponseService {
           )
         ),
       ],
+      [
+        OCPP20IncomingRequestCommand.GET_BASE_REPORT,
+        this.ajvIncomingRequest.compile(
+          OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20GetBaseReportResponse>(
+            'assets/json-schemas/ocpp/2.0/GetBaseReportResponse.json',
+            moduleName,
+            'constructor'
+          )
+        ),
+      ],
     ])
     this.validatePayload = this.validatePayload.bind(this)
   }
index d603d0adc360964b80146ef1d1c3d88ff3fd3822..64450bb55c9db993a22fde1fb43db273558ddcce 100644 (file)
@@ -141,19 +141,34 @@ export {
   type OCPP16StopTransactionRequest,
   type OCPP16StopTransactionResponse,
 } from './ocpp/1.6/Transaction.js'
-export { BootReasonEnumType, OCPP20ConnectorStatusEnumType } from './ocpp/2.0/Common.js'
 export {
+  BootReasonEnumType,
+  GenericDeviceModelStatusEnumType,
+  OCPP20ConnectorStatusEnumType,
+  ReportBaseEnumType,
+} from './ocpp/2.0/Common.js'
+export {
+  type ComponentType,
+  type EVSEType,
   type OCPP20BootNotificationRequest,
   type OCPP20ClearCacheRequest,
+  type OCPP20GetBaseReportRequest,
   type OCPP20HeartbeatRequest,
   OCPP20IncomingRequestCommand,
+  type OCPP20NotifyReportRequest,
   OCPP20RequestCommand,
   type OCPP20StatusNotificationRequest,
+  type ReportDataType,
+  type VariableAttributeType,
+  type VariableCharacteristicsType,
+  type VariableType,
 } from './ocpp/2.0/Requests.js'
 export type {
   OCPP20BootNotificationResponse,
   OCPP20ClearCacheResponse,
+  OCPP20GetBaseReportResponse,
   OCPP20HeartbeatResponse,
+  OCPP20NotifyReportResponse,
   OCPP20StatusNotificationResponse,
 } from './ocpp/2.0/Responses.js'
 export { OCPP20OptionalVariableName } from './ocpp/2.0/Variables.js'
index 1bd577bedb4a585141f946f367d483fed203b5b6..d1ed68ed026268540c88dbe0371c3286caa1ccee 100644 (file)
@@ -53,6 +53,13 @@ export enum GetCertificateStatusEnumType {
   Failed = 'Failed',
 }
 
+export enum GenericDeviceModelStatusEnumType {
+  Accepted = 'Accepted',
+  EmptyResultSet = 'EmptyResultSet',
+  NotSupported = 'NotSupported',
+  Rejected = 'Rejected',
+}
+
 export enum GetInstalledCertificateStatusEnumType {
   Accepted = 'Accepted',
   NotFound = 'NotFound',
@@ -90,6 +97,12 @@ export enum OperationalStatusEnumType {
   Operative = 'Operative',
 }
 
+export enum ReportBaseEnumType {
+  ConfigurationInventory = 'ConfigurationInventory',
+  FullInventory = 'FullInventory',
+  SummaryInventory = 'SummaryInventory',
+}
+
 export interface CertificateHashDataChainType extends JsonObject {
   certificateHashData: CertificateHashDataType
   certificateType: GetCertificateIdUseEnumType
index 0cf30f92916921be5e28319d036f8acd62d2a38b..1c2fb339c96a5b12743318b97126a815bdfcde49 100644 (file)
@@ -4,11 +4,13 @@ import type {
   BootReasonEnumType,
   InstallCertificateUseEnumType,
   OCPP20ConnectorStatusEnumType,
+  ReportBaseEnumType,
 } from './Common.js'
 import type { OCPP20SetVariableDataType } from './Variables.js'
 
 export enum OCPP20IncomingRequestCommand {
   CLEAR_CACHE = 'ClearCache',
+  GET_BASE_REPORT = 'GetBaseReport',
   REQUEST_START_TRANSACTION = 'RequestStartTransaction',
   REQUEST_STOP_TRANSACTION = 'RequestStopTransaction',
 }
@@ -16,6 +18,7 @@ export enum OCPP20IncomingRequestCommand {
 export enum OCPP20RequestCommand {
   BOOT_NOTIFICATION = 'BootNotification',
   HEARTBEAT = 'Heartbeat',
+  NOTIFY_REPORT = 'NotifyReport',
   STATUS_NOTIFICATION = 'StatusNotification',
 }
 
@@ -26,8 +29,21 @@ export interface OCPP20BootNotificationRequest extends JsonObject {
 
 export type OCPP20ClearCacheRequest = EmptyObject
 
+export interface OCPP20GetBaseReportRequest extends JsonObject {
+  reportBase: ReportBaseEnumType
+  requestId: number
+}
+
 export type OCPP20HeartbeatRequest = EmptyObject
 
+export interface OCPP20NotifyReportRequest extends JsonObject {
+  generatedAt: Date
+  requestId: number
+  reportData?: ReportDataType[]
+  seqNo: number
+  tbc?: boolean
+}
+
 export interface OCPP20InstallCertificateRequest extends JsonObject {
   certificate: string
   certificateType: InstallCertificateUseEnumType
@@ -56,3 +72,36 @@ interface ModemType extends JsonObject {
   iccid?: string
   imsi?: string
 }
+
+export interface ReportDataType extends JsonObject {
+  component: ComponentType
+  variable: VariableType
+  variableAttribute?: VariableAttributeType[]
+  variableCharacteristics?: VariableCharacteristicsType
+}
+
+export interface ComponentType extends JsonObject {
+  evse?: EVSEType
+  instance?: string
+  name: string
+}
+
+export interface VariableType extends JsonObject {
+  instance?: string
+  name: string
+}
+
+export interface VariableAttributeType extends JsonObject {
+  type?: string
+  value?: string
+}
+
+export interface VariableCharacteristicsType extends JsonObject {
+  dataType: string
+  supportsMonitoring: boolean
+}
+
+export interface EVSEType extends JsonObject {
+  connectorId?: number
+  id: number
+}
index e735c788ff8cd1b835a844ea6713f4c11cb2eea8..225c5aae07a423b550ff6fc3d89a85d1ebf9f60c 100644 (file)
@@ -2,6 +2,7 @@ import type { EmptyObject } from '../../EmptyObject.js'
 import type { JsonObject } from '../../JsonType.js'
 import type { RegistrationStatusEnumType } from '../Common.js'
 import type {
+  GenericDeviceModelStatusEnumType,
   GenericStatusEnumType,
   InstallCertificateStatusEnumType,
   StatusInfoType,
@@ -20,10 +21,17 @@ export interface OCPP20ClearCacheResponse extends JsonObject {
   statusInfo?: StatusInfoType
 }
 
+export interface OCPP20GetBaseReportResponse extends JsonObject {
+  status: GenericDeviceModelStatusEnumType
+  statusInfo?: StatusInfoType
+}
+
 export interface OCPP20HeartbeatResponse extends JsonObject {
   currentTime: Date
 }
 
+export type OCPP20NotifyReportResponse = EmptyObject
+
 export interface OCPP20InstallCertificateResponse extends JsonObject {
   status: InstallCertificateStatusEnumType
   statusInfo?: StatusInfoType
index 9dce9e1b67c618b0ca9f1d113accc2a4da2f3c7f..6a772e0ab7ca91a9a7ebf07a2b87effed33d3e7c 100644 (file)
@@ -96,6 +96,13 @@ class ChargePoint(ocpp.v201.ChargePoint):
         logging.info("Received %s", Action.meter_values)
         return ocpp.v201.call_result.MeterValues()
 
+    @on(Action.notify_report)
+    async def on_notify_report(
+        self, request_id: int, generated_at, seq_no: int, **kwargs
+    ):
+        logging.info("Received %s", Action.notify_report)
+        return ocpp.v201.call_result.NotifyReport()
+
     # Request handlers to emit OCPP messages.
     async def _send_clear_cache(self):
         request = ocpp.v201.call.ClearCache()