PHP2Shell alert: critical PHP flaws CVE-2026-109618 / 109619 — check your servers and get patched fast
HashTech Systems
EV Charging
Part 7 of 12

OCPP 1.6 Remote Commands: RemoteStart, Reset, Unlock, Reserve

Controlling chargers from the CSMS in OCPP 1.6: RemoteStart/StopTransaction for app sessions, UnlockConnector, Reset, ChangeAvailability and ReserveNow.

HashTech Engineering · October 26, 2026 · 6 min read

Parts 2 to 6 were mostly about messages the charger sends. This part covers the commands the CSMS sends: starting and stopping sessions from an app, unlocking a stuck cable, rebooting a charger, taking a connector out of service, and reserving one. These are the messages your support team and your driver app will use every day. It is part 7; part 5 covers the transaction messages that follow a remote start.

RemoteStartTransaction

App-based and QR-code charging usually work like this: the driver selects a connector in an app, the app asks the CSMS to start, and the CSMS sends RemoteStartTransaction with an idTag that represents the driver or the payment.

json
[
  2,
  "csms-rs-301",
  "RemoteStartTransaction",
  {
    "connectorId": 1,
    "idTag": "APP7F3K9Q2"
  }
]
json
[3, "csms-rs-301", {"status": "Accepted"}]

idTag is required; connectorId is optional but, with multi-connector chargers, you almost always want it. Without it the charger picks a connector itself. You can also include a chargingProfile with purpose TxProfile to set a limit for the session from the start; part 9 covers profiles.

Accepted is not started

The response status is Accepted or Rejected, and Accepted only means the charger has accepted the request. The transaction itself starts when the charger sends StartTransaction, which can be seconds or minutes later, after the driver plugs in. The sequence looks like this:

text
CSMS -> CP  RemoteStartTransaction {connectorId: 1, idTag: "APP7F3K9Q2"}
CP -> CSMS  RemoteStartTransaction.conf {status: "Accepted"}
CP -> CSMS  StatusNotification {connectorId: 1, status: "Preparing"}
(AuthorizeRemoteTxRequests = true only)
CP -> CSMS  Authorize {idTag: "APP7F3K9Q2"}
(driver plugs in, cable detected)
CP -> CSMS  StartTransaction {connectorId: 1, idTag: "APP7F3K9Q2", meterStart: ...}
CSMS -> CP  StartTransaction.conf {transactionId: 880214, ...}
CP -> CSMS  StatusNotification {connectorId: 1, status: "Charging"}

Your app therefore needs a "waiting for vehicle" state, and the CSMS needs to correlate the incoming StartTransaction with the remote start request. Matching on charger, connector and idTag within a time window is the usual approach, which is why each app session should use a unique idTag. If the driver never plugs in, the charger abandons the request after ConnectionTimeOut seconds and returns to Available, without sending any message that refers to the remote start. Time out the pending request on your side too.

RemoteStopTransaction

json
[2, "csms-rst-302", "RemoteStopTransaction", {"transactionId": 880214}]
[3, "csms-rst-302", {"status": "Accepted"}]

The only field is transactionId. Again, Accepted means the request was accepted; the session ends when StopTransaction arrives with reason Remote. If the charger answers Rejected, the most common causes are an unknown transactionId (the charger rebooted and lost the session, or you are using an ID from a duplicate start) or a charger already in the process of stopping.

UnlockConnector

json
[2, "csms-ul-303", "UnlockConnector", {"connectorId": 1}]
[3, "csms-ul-303", {"status": "Unlocked"}]

The response status is Unlocked, UnlockFailed or NotSupported. This is primarily a support tool for a cable stuck in a socket-outlet charger. If a transaction is in progress on that connector, the charger stops it first (with stop reason UnlockCommand) and then unlocks. Chargers with tethered cables usually return NotSupported, since there is nothing to unlock on the charger side. Be careful about exposing this to drivers: unlocking during a session ends it.

Reset

json
[2, "csms-rb-304", "Reset", {"type": "Soft"}]
[3, "csms-rb-304", {"status": "Accepted"}]

type is Soft or Hard, and the response is Accepted or Rejected. A soft reset asks the charger to stop ongoing transactions gracefully and restart its application software. A hard reset restarts the whole charger and is not required to stop transactions gracefully; where possible, the charger sends StopTransaction for the interrupted sessions after it restarts. Transactions ended by a reset are reported with reasons SoftReset or HardReset, and the charger sends a BootNotification when it comes back.

  • Try Soft first. It is less disruptive and fixes most software hangs.
  • Do not retry a timed-out Reset automatically. The charger may already be rebooting; a second reset during boot can leave some hardware in a bad state.
  • The ResetRetries configuration key sets how many times the charger retries an unsuccessful reset.
  • A reset that never produces a BootNotification is a strong signal for a site visit. Alert on it.

ChangeAvailability

json
[2, "csms-ca-305", "ChangeAvailability", {"connectorId": 2, "type": "Inoperative"}]
[3, "csms-ca-305", {"status": "Scheduled"}]

type is Inoperative or Operative. connectorId 0 applies to the whole charger. The response is Accepted, Rejected or Scheduled. Scheduled means a transaction is in progress, and the change will take effect when it ends. The charger then reports the new state as Unavailable (or back to Available) with a StatusNotification.

The spec requires availability to persist across reboots. Use it to take a broken connector out of service so it stops appearing as available in apps and roaming feeds, and remember to set it back after repair. An "unavailable since" report for your operations team catches the ones that were forgotten.

Reservations

The Reservation profile lets a driver hold a connector for a limited time.

json
[
  2,
  "csms-rn-306",
  "ReserveNow",
  {
    "connectorId": 1,
    "expiryDate": "2026-10-26T12:30:00Z",
    "idTag": "04A2B3C4D5E680",
    "reservationId": 5120
  }
]
json
[3, "csms-rn-306", {"status": "Accepted"}]
ReserveNow statusMeaning
AcceptedReservation made; connector goes to Reserved
FaultedConnector or charger is faulted
OccupiedConnector is in use or already reserved
RejectedCharger is not configured to accept reservations
UnavailableConnector or charger is unavailable

The optional parentIdTag allows any card in the same group to use the reservation. reservationId is chosen by the CSMS; sending a ReserveNow with an existing reservationId replaces that reservation. When the reserved driver starts charging, the charger includes reservationId in StartTransaction so you can close the reservation. If expiryDate passes without use, the charger cancels the reservation itself and returns the connector to Available; there is no dedicated expiry message, so expire it on your side using the same date.

A reservation on connector 0 reserves the charger rather than a specific connector, and is only allowed when the ReserveConnectorZeroSupported configuration key is true. Cancel with CancelReservation:

json
[2, "csms-cr-307", "CancelReservation", {"reservationId": 5120}]
[3, "csms-cr-307", {"status": "Accepted"}]

The response is Accepted or Rejected (unknown reservation). In practice many operators find reservations less useful in public charging than expected, because drivers who reserve and do not show up block a connector. If you offer them, keep the expiry short and consider a fee. In roaming scenarios, OCPI's RESERVE_NOW command maps onto this message (OCPI commands).

Command handling on the CSMS side

  • Send commands through a per-charger queue and respect the one-outstanding-CALL rule from part 1.
  • Record every command with who issued it and the response. Support teams need that history.
  • Treat timeouts as unknown, not as failure. Check the charger's subsequent messages before telling a driver the start did not work.
  • Show drivers and support staff the outcome (session started, cable unlocked) based on the follow-up messages, not just on Accepted.

Key takeaways

  • Accepted for remote start and stop means the request was taken, not that the session changed. Watch for the follow-up StartTransaction or StopTransaction.
  • Use a unique idTag per app session so the eventual StartTransaction can be correlated.
  • UnlockConnector stops an active transaction; Reset is not safe to retry blindly.
  • ChangeAvailability persists across reboots and can be Scheduled.

Previous: Part 6, MeterValues. Next: Part 8, configuration keys and TriggerMessage.

Let's Talk

Ready to build something that lasts?

Whether it's a charging network, an AI rollout, or a core enterprise system — let's talk about what you're building.