Remote Control Section — Migration Guide
Use cases F01–F06 (2.0.1) / F01–F07 (2.1). Perspective: CSMS Developer.
Created by tzi.app — your guide to migrating from OCPP 2.0.1 to OCPP 2.1.
1. Summary of Changes
High-Level Overview
| Area | Change Type | Description |
|---|---|---|
| Connector status reporting | Replaced | StatusNotificationRequest replaced by NotifyEventRequest in F01, F02, F03 sequences |
| IdTokenType | Increased | idToken max length: 36 → 255 chars |
| IdTokenEnumType → IdTokenEnumStringType | Changed | Changed from a closed enum to a string type (max 20 chars); new value OCPP2WhiteList added |
| StatusInfoType | Increased | additionalInfo max length: 512 → 1024 chars |
| TransactionEventResponse | Extended | New fields: transactionLimit, updatedPersonalMessageExtra |
| TransactionEventRequest | Extended | New fields in TransactionType: operationMode, transactionLimit; new top-level: costDetails, preconditioningStatus, evseSleep |
| ReasonEnumType (stoppedReason) | New values | EnergyLimitReached, TimeLimitReached, SOCLimitReached, LocalOutOfCredit added for F07 limit scenarios |
| ChargingProfileType | Extended | New chargingProfileKind value: "Dynamic" |
| ChargingSchedulePeriod | Changed | limit field changed from required to conditional; new phaseToUse field added |
| TriggerMessageRequest (F06) | Extended | New customTrigger field; new enum values CustomTrigger and SignV2G20Certificate |
| F06 BootNotification trigger | New | CS will reject BootNotification trigger if last BootNotification was already Accepted (F06.FR.17) |
| UnlockConnectorRequest (F05) | Clarified | evseId and connectorId now explicitly allow ≥0; clarified scope (cable retention lock only) |
| F05 error handling | New | CS with no lock mechanism or manual lock SHOULD respond with CALLERROR NotSupported |
| F03 Remote Stop | Changed | 2.1 shows two-phase stop: first Updated (energy stopped), then Ended (cable unplugged); adds TxStopPoint impact documentation |
| F07 | New use case | Remote Start with transaction limits (maxCost, maxEnergy, maxTime, maxSoC) |
2. Cross-Cutting Change: StatusNotification Replaced by NotifyEvent
In OCPP 2.0.1, connector state changes in remote control flows used StatusNotificationRequest. In OCPP 2.1, these are replaced by NotifyEventRequest.
Affected Use Cases
| Use Case | 2.0.1 | 2.1 |
|---|---|---|
| F01 (Cable Plugin First) | CS sends StatusNotificationRequest (connector = Occupied) when cable plugged in | CS sends NotifyEventRequest when cable plugged in |
| F02 (Remote Start First) | CS sends StatusNotificationRequest (Occupied) when cable plugged in after remote start | CS sends NotifyEventRequest when cable plugged in after remote start |
| F03 (Remote Stop) | Not explicitly shown in sequence | CS sends NotifyEventRequest after EV driver unplugs cable |
Message Format Comparison
OCPP 2.0.1 — StatusNotificationRequest:
{
"timestamp": "2025-06-15T14:30:05.000Z",
"connectorStatus": "Occupied",
"evseId": 1,
"connectorId": 1
}OCPP 2.1 — NotifyEventRequest (replacement):
{
"generatedAt": "2025-06-15T14:30:05.000Z",
"seqNo": 0,
"eventData": [
{
"eventId": 1,
"timestamp": "2025-06-15T14:30:05.000Z",
"trigger": "Delta",
"actualValue": "Occupied",
"eventNotificationType": "HardWiredNotification",
"component": {
"name": "Connector",
"evse": { "id": 1, "connectorId": 1 }
},
"variable": {
"name": "AvailabilityState"
}
}
]
}CSMS Migration Action
Replace your StatusNotificationRequest handler (in remote control flows) with a NotifyEventRequest handler that filters for component.name = "Connector" and variable.name = "AvailabilityState". Extract the connector state from actualValue instead of connectorStatus.
3. Data Type & Schema Changes
3.1 IdTokenType
| Field | 2.0.1 | 2.1 | Action |
|---|---|---|---|
idToken | string (max 36) | string (max 255, case-insensitive) | Increase max length validation from 36 to 255 |
type | IdTokenEnumType (closed enum) | IdTokenEnumStringType (string, max 20) | Change from enum validation to string validation; accept unknown values gracefully |
New type value | — | "OCPP2WhiteList" | Handle new token type for OCPP2 whitelist-based authorization |
3.2 StatusInfoType
| Field | 2.0.1 | 2.1 | Action |
|---|---|---|---|
additionalInfo | string (max 512) | string (max 1024) | Increase max length validation from 512 to 1024 |
3.3 EVSEType
| Field | 2.0.1 | 2.1 | Action |
|---|---|---|---|
id | integer (> 0) | integer (>= 0) | Allow 0 as a valid EVSE ID (represents the Charging Station itself) |
connectorId | integer | integer (>= 0) | Explicitly allow 0 |
3.4 TransactionEventResponse (CSMS → CS)
New fields added in 2.1:
| Field | Type | Description |
|---|---|---|
transactionLimit | TransactionLimitType | Cost/energy/time/SoC limits for the transaction (used by F07) |
updatedPersonalMessageExtra | MessageContentType[] | Additional display messages (multiple languages) |
TransactionLimitType (new in 2.1):
{
"maxCost": 50.00,
"maxEnergy": 30000,
"maxTime": 7200,
"maxSoC": 80
}| Field | Type | Description |
|---|---|---|
maxCost | number | Maximum cost in tariff currency |
maxEnergy | number | Maximum energy in Wh |
maxTime | integer | Maximum duration in seconds |
maxSoC | integer (0-100) | Maximum State of Charge percentage |
3.5 TransactionType (inside TransactionEventRequest)
New fields added in 2.1:
| Field | Type | Description |
|---|---|---|
operationMode | OperationModeEnumType | Current operation mode of the transaction |
transactionLimit | TransactionLimitType | Echoed-back transaction limit from CSMS (confirms CS received and applied the limit) |
3.6 TransactionEventRequest (CS → CSMS)
New top-level fields added in 2.1:
| Field | Type | Description |
|---|---|---|
costDetails | CostDetailsType | Cost breakdown calculated by CS |
preconditioningStatus | enum | Preconditioning status |
evseSleep | boolean | Whether EVSE is in sleep mode |
3.7 ReasonEnumType (stoppedReason) — New Values
| Value | Description | Used By |
|---|---|---|
EnergyLimitReached | Energy limit from transactionLimit.maxEnergy was reached | F07 |
TimeLimitReached | Time limit from transactionLimit.maxTime was reached | F07 |
SOCLimitReached | SoC limit from transactionLimit.maxSoC was reached | F07 |
LocalOutOfCredit | Local cost limit from transactionLimit.maxCost was reached | F07 |
3.8 ChargingProfileType / ChargingSchedulePeriod
| Change | 2.0.1 | 2.1 | Action |
|---|---|---|---|
chargingProfileKind values | Absolute, Recurring, Relative | Absolute, Recurring, Relative, Dynamic | Support new Dynamic profile kind |
ChargingSchedulePeriod.limit | Required | Conditional | Field may be absent in some scenarios |
ChargingSchedulePeriod.phaseToUse | Not present | integer (1-3) | New field to specify a specific phase; only valid when numberPhases=1 |
4. Use Case Changes (F01–F06)
4.1 F01 — Remote Start Transaction: Cable Plugin First
| Aspect | 2.0.1 | 2.1 | Impact |
|---|---|---|---|
| Connector state notification | CS sends StatusNotificationRequest (Occupied) | CS sends NotifyEventRequest | Replace message handler (see Section 2) |
| Sequence flow | StatusNotification before TransactionEvent | NotifyEvent before TransactionEvent | Same logical flow, different message type |
Charging profile Dynamic kind | Not supported | Supported | Accept Dynamic as valid chargingProfileKind |
No breaking changes to RequestStartTransactionRequest or RequestStartTransactionResponse schemas. The message structure is the same; only the supporting event notifications
changed.
4.2 F02 — Remote Start Transaction: Remote Start First
| Aspect | 2.0.1 | 2.1 | Impact |
|---|---|---|---|
| Connector state notification | CS sends StatusNotificationRequest (Occupied) when cable plugged in | CS sends NotifyEventRequest when cable plugged in | Replace message handler (see Section 2) |
| Sequence detail | Less detailed — fewer intermediate events shown | More detailed — explicit NotifyEvent + multiple TransactionEventRequest updates shown | No code change, but more events to expect |
No breaking changes to the core remote start flow. Same RequestStartTransactionRequest/RequestStartTransactionResponse messages.
4.3 F03 — Remote Stop Transaction Changed
| Aspect | 2.0.1 | 2.1 | Impact |
|---|---|---|---|
| Stop flow | Single TransactionEventRequest(Ended) after stop | Two-phase: first Updated (energy stopped, chargingState=EVConnected), then Ended (cable unplugged) | Handle the additional Updated event with triggerReason=RemoteStop before the Ended event |
| Cable unplug notification | Not shown | CS sends NotifyEventRequest after EV unplugs | Handle NotifyEventRequest between Updated and Ended |
| TxStopPoint impact | Not documented | Documented: behavior differs based on TxStopPoint configuration | Implement awareness of TxStopPoint configuration |
| Unknown transactionId rule | F03.FR.06 | F03.FR.08 | Renumbered only, no behavioral change |
TxStopPoint behavior (new in 2.1 documentation)
| TxStopPoint | 2.1 Behavior |
|---|---|
Does NOT contain Authorized or PowerPathClosed (e.g. EVConnected) | CS stops energy, sends Updated(RemoteStop). Transaction does NOT end until EV disconnects. |
Contains Authorized or PowerPathClosed | CS sends Ended(RemoteStop, Remote) immediately. |
CSMS Migration Action
Update your remote stop handler to expect an Updated event with triggerReason=RemoteStop and chargingState=EVConnected before the final Ended event, rather than receiving a single Ended event.
4.4 F04 — Remote Stop ISO 15118 Charging from CSMS
| Aspect | 2.0.1 | 2.1 | Impact |
|---|---|---|---|
| ISO 15118-20 | Not mentioned | Explicitly supports ISO 15118-20 (EVSENotification = Terminate via AC_ChargeLoopRes / DC_ChargeLoopRes) | No CSMS code change needed (CS-internal detail), but good to be aware of |
No CSMS-visible changes. F04 remains identical to F03 from the CSMS perspective.
4.5 F05 — Remotely Unlock Connector Changed
| Aspect | 2.0.1 | 2.1 | Impact |
|---|---|---|---|
evseId / connectorId range | Implicit integer > 0 | Explicit integer >= 0 | Allow 0 values in validation |
| Scope clarification | Not specified | "ONLY for unlocking the cable retention lock on the connector, NOT for unlocking a connector access door" | Document and enforce usage limitation in your API/UI |
| No lock mechanism | Not specified | CS SHOULD respond with CALLERROR NotSupported | Handle NotSupported CALLERROR in addition to the four status values |
| Unlock without cable | Not specified | CS will attempt unlock even if no cable is connected | No code change, but be aware of this behavior |
| Sequence with active transaction | Two sequences shown (with/without ongoing unauthorized transaction; CS auto-stops) | Single simplified sequence; CSMS expected to stop transaction first | Update workflow: always stop the transaction via F03 before sending UnlockConnectorRequest |
CSMS Migration Action
Add error handling for CALLERROR NotSupported response. Update validation to allow evseId >= 0 and connectorId >= 0.
4.6 F06 — Trigger Message Extended
| Aspect | 2.0.1 | 2.1 | Impact |
|---|---|---|---|
customTrigger field | Not present | New field: string (max 50) | Add support for custom trigger names |
CustomTrigger enum value | Not present | New requestedMessage value | Implement custom trigger functionality; check CS's CustomizationCtrlr.CustomTriggers for supported values |
SignV2G20Certificate enum value | Not present | New requestedMessage value for ISO 15118-20 | Add to trigger enum handling |
BootNotification trigger restriction | No restriction documented | CS MUST reject if last BootNotification was already Accepted (F06.FR.17) | Expect Rejected when triggering BootNotification on an already-accepted CS |
evse field behavior | Optional | If absent, interpreted as "all EVSEs" (F06.FR.11) | Be explicit with evse field when you want a specific EVSE, omit for all |
StatusNotification connectorId | Not explicitly required | CSMS MUST set connectorId in evse field (F06.FR.13) | Always include connectorId when triggering StatusNotification |
MeterValues measurands | Not specified | Returns measurands from AlignedDataMeasurands configuration | Be aware of which measurands to expect based on CS configuration |
TransactionEvent trigger | triggerReason not specified | Triggered message will have triggerReason = "Trigger" | Handle Trigger as a valid triggerReason value |
| Triggered message fulfillment | Not specified | If the same message arrives normally between acceptance and triggered send, it MAY count as fulfilling the trigger (F06.FR.10) | Don't assume the next message of that type is always the triggered one |
MessageTriggerEnumType — Full Comparison
| Value | 2.0.1 | 2.1 | Notes |
|---|---|---|---|
BootNotification | Yes | Yes | 2.1 adds rejection rule (F06.FR.17) |
LogStatusNotification | Yes | Yes | No change |
FirmwareStatusNotification | Yes | Yes | No change |
Heartbeat | Yes | Yes | No change |
MeterValues | Yes | Yes | 2.1 specifies AlignedDataMeasurands |
SignChargingStationCertificate | Yes | Yes | No change |
SignV2GCertificate | Yes | Yes | No change |
SignV2G20Certificate | No | Yes | New — ISO 15118-20 |
StatusNotification | Yes | Yes | 2.1 adds connectorId requirement |
TransactionEvent | Yes | Yes | 2.1 specifies triggerReason=Trigger |
SignCombinedCertificate | Yes | Yes | No change |
PublishFirmwareStatusNotification | Yes | Yes | No change |
CustomTrigger | No | Yes | New — requires customTrigger field |
5. New Use Case: F07 — Remote Start with Fixed Cost, Energy, SoC or Time
New in 2.1Overview
F07 is a new use case in OCPP 2.1 that enables the CSMS to set transaction limits (cost, energy, SoC, or time) during a remote start. When a limit is reached, the CS automatically suspends energy transfer.
Use cases: Prepaid balance, ad-hoc payment reservation amounts, energy/time-limited sessions.
Mechanism
F07 does not introduce any new request/response messages. Instead, it
leverages the existing RequestStartTransactionRequest (same as F01/F02) combined with the new transactionLimit field in TransactionEventResponse.
Flow
- CSMS sends
RequestStartTransactionRequest(identical to F01/F02) - CS sends
TransactionEventRequestwitheventType=Started - CSMS responds with
TransactionEventResponsethat includestransactionLimit - CS echoes back the limit in the next
TransactionEventRequestviatransactionInfo.transactionLimit(confirming receipt) - When limit is reached, CS sends
TransactionEventRequestwithchargingState=SuspendedEVSE
TransactionLimit in TransactionEventResponse
{
"transactionLimit": {
"maxCost": 25.00,
"maxEnergy": 20000,
"maxTime": 3600,
"maxSoC": 80
}
}| Field | Type | Description |
|---|---|---|
maxCost | number | Maximum cost in tariff currency. CS must calculate cost locally. |
maxEnergy | number | Maximum energy in Wh |
maxTime | integer | Maximum duration in seconds (start to end) |
maxSoC | integer (0-100) | Maximum State of Charge percentage |
Rules
- At least one limit should be provided
- If multiple limits are given, whichever is reached first stops energy transfer
- The CS echoes back the limits in
transactionInfo.transactionLimitto confirm receipt
New stoppedReason Values for F07
When a transaction ends because a limit was reached, the stoppedReason in TransactionEventRequest will use one of these new values:
| stoppedReason | Limit |
|---|---|
EnergyLimitReached | maxEnergy reached |
TimeLimitReached | maxTime reached |
SOCLimitReached | maxSoC reached |
LocalOutOfCredit | maxCost reached |
Implementation Requirements
- Add
transactionLimitfield support to yourTransactionEventResponsebuilder - Track which transactions have limits and verify the CS echoes them back
- Handle the new
stoppedReasonvalues (EnergyLimitReached,TimeLimitReached,SOCLimitReached,LocalOutOfCredit) - Handle
chargingState=SuspendedEVSEas an indicator that a limit was reached
6. Migration Checklist
Message Handler Updates Breaking / Behavioral
Replace StatusNotificationRequest handler with NotifyEventRequest handler for connector state changes.
Handle CALLERROR NotSupported from UnlockConnectorRequest (CS has no lock mechanism).
Handle two-phase remote stop: expect Updated(RemoteStop) before Ended(Remote).
Update TriggerMessageRequest builder to support customTrigger field.
Handle Rejected response when triggering BootNotification on already-accepted CS (F06.FR.17).
Schema Validation Updates Validation
IdTokenType.idToken max length: 36 → 255.
IdTokenType.type: closed enum → string (max 20); accept OCPP2WhiteList and unknown values.
StatusInfoType.additionalInfo max length: 512 → 1024.
EVSEType.id: allow >= 0 (was > 0).
EVSEType.connectorId: allow >= 0.
UnlockConnectorRequest.evseId / connectorId: allow >= 0.
ChargingSchedulePeriod.limit: required → conditional.
New Fields to Support New
TransactionEventResponse.transactionLimit (TransactionLimitType) — for F07.
TransactionEventResponse.updatedPersonalMessageExtra (MessageContentType[]).
TransactionType.operationMode (OperationModeEnumType).
TransactionType.transactionLimit (TransactionLimitType) — echo-back from CS.
TransactionEventRequest.costDetails (CostDetailsType).
TransactionEventRequest.preconditioningStatus.
TransactionEventRequest.evseSleep.
ChargingSchedulePeriod.phaseToUse (integer 1-3).
New Enum Values Enum
MessageTriggerEnumType: add CustomTrigger, SignV2G20Certificate.
ChargingProfileKindEnumType: add Dynamic.
ReasonEnumType (stoppedReason): add EnergyLimitReached, TimeLimitReached, SOCLimitReached, LocalOutOfCredit.
New Use Case Implementation F07
Implement transactionLimit in TransactionEventResponse for limit-based remote starts.
Verify CS echoes back limits in transactionInfo.transactionLimit.
Handle chargingState=SuspendedEVSE when limits are reached.
Handle new stoppedReason values for limit-reached scenarios.
Behavioral Changes Behavioral
Update unlock workflow — always stop transaction via F03 before sending UnlockConnectorRequest.
Always include connectorId in evse when triggering StatusNotification (F06.FR.13).
Be aware that BootNotification trigger will be rejected on already-accepted CS (F06.FR.17).
Handle triggerReason = "Trigger" in triggered TransactionEventRequest messages.