SkillAgentSearch skills...

Ocpp

Open Charge Point Protocol

Install / Use

/learn @ShellRechargeSolutionsEU/Ocpp
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Open Charge Point Protocol for Scala Build Status Coverage Status Codacy Badge

The Open Charge Point Protocol (OCPP) is a network protocol for communication between electric vehicle chargers and a central backoffice system. It is developed by the Open Charge Alliance (OCA). You can find more details on the official website of the OCA.

Note to open source users

Open source users of this library will want to use the IHomer fork which is more actively supported and published to Maven Central.

Functionality

This library is the implementation of OCPP developed and used by NewMotion, one of Europe's largest Electric Vehicle Charge Point Operators.

This library only implements the network protocol. That is, it provides data types for the OCPP messages, remote procedure call using those request and response messages, and error reporting about those remote procedure calls. It does not provide any actual handling of the message contents. For an actual app speaking OCPP using this library, see docile-charge-point.

The library is designed with versatility in mind. OCPP comes in 4 versions (1.2, 1.5, 1.6 and 2.0), two transport variants (SOAP/XML aka OCPP-S and WebSocket/JSON aka OCPP-J), and two roles ("Charge Point" and "Central System"). This library will help you with 1.5 and 1.6 over JSON. For 1.2 and 1.5 over SOAP, there is a separate library by NewMotion that depends on this one. Some OCPP 2.0 support is present, but not a full implementation yet. The main body of this README will be writing about the OCPP 1.5 and 1.6 support; for OCPP 2.0 see here.

Version 2.0 with SOAP/XML is not possible. Version 1.2 with WebSocket/JSON and version 1.6 with SOAP/XML are not supported by this library.

Users of this library probably want to use different WebSocket libraries for different scenarios: a production back-office server with tens of thousands of concurrent connections, a client in a load testing tool, or a simple one-off script to test a certain behavior. This library uses the cake pattern to make it easy to swap out the underlying WebSocket implementation while still using the same concise high-level API.

How to use

Setup

The library is divided into three separate modules so applications using it won't get too many dependencies dragged in. Those are:

  • ocpp-j-api: high-level interface to OCPP-J connections
  • ocpp-json: serialization of OCPP messages to/from JSON
  • ocpp-messages: The definitions of OCPP messages, independent from the transport variant used

So if you want to use the high-level OCPP-J connection interface, and you're using SBT, you can declare the dependency by adding this this to your build.sbt after publishing the library:

libraryDependencies += "com.thenewmotion.ocpp" %% "ocpp-j-api" % "9.2.2"

With Maven, add this to your dependencies:

    <dependency>
        <groupId>com.thenewmotion.ocpp</groupId>
        <artifactId>ocpp-j-api_2.11</artifactId>
        <version>9.2.2</version>
    </dependency>

Using the simple client API

An example OCPP-J client application included. You can run it like this:

sbt "project example-json-client" "run 01234567 ws://localhost:8017/ocppws 1.5,1.6"

This means: connect to the Central System running at ws://localhost:8017/ocppws, as a charge point with ID 01234567, using OCPP version 1.5 and if that is not supported try 1.6 instead. If you don't specify a version, 1.6 is used by default.

If you look at the code of the example by clicking here, you can see how the client API is used:

  • A connection is established by creating an instance of OcppJsonClient using the OcppJsonClient.forVersion1x factory method. The server endpoint URI, charge point ID and OCPP version to use are passed to the method, followed by a handler for incoming OCPP requests in a second parameter list.

  • To send OCPP messages to the Central System, you call the send method on the OcppJsonClient instance. You will get a Future back that will be completed with the Central System's response. If the Central System fails to respond to your request, the Future will fail.

  • OcppJsonClient is an instance of the OutgoingOcppEndpoint trait. This trait defines this interface.

Handling requests

To specify the request handler, we use a magnet pattern. You can specify the request handler in different ways. After the val requestHandler: ChargePointRequestHandler =, you see a ChargePoint instance in the example program. But you can also specify the request handler as a function from ChargePointReq to Future[ChargePointRes]:


 val ocppJsonClient = OcppJsonClient.forVersion1x(chargerId, new URI(centralSystemUri), versions) {
   (req: ChargePointReq) =>
     req match {
       case GetConfigurationReq(keys) =>
         System.out.println(s"Received GetConfiguration for $keys")
         Future.successful(GetConfigurationRes(
           values = List(),
           unknownKeys = keys
         ))
       case x =>
         val opName = x.getClass.getSimpleName
         Future.failed(OcppException(
           PayloadErrorCode.NotSupported,
           s"Demo app doesn't support $opName"
         ))
     }
}

This behavior of this request handler is more or less equivalent to that of the one in the example app. It is shorter at the price of being less type-safe: this code does not check if you generate the right response type for the request, so if you generate a GetConfigurationRes in response to a GetConfigurationReq for instance.

Sending requests

Sending requests is simple, as explained. You call the send method of your endpoint and off you go, like this:

    connection.send(HeartbeatReq)

The set of messages you can send with OCPP 1.x connections is defined in ocpp-messages. For every request type, you represent requests as instances of a case class named <Operation Name>Req, e.g. StatusNotificationReq, HeartbeatReq.

For OCPP 1.x, these case classes in ocpp-messages are designed according to two principles:

  • They are independent of OCPP version, so you have one interface to charging stations that use different versions
  • They sometimes group and rearrange fields to make it impossible to specify nonsense messages (e.g., no vendorErrorCode in status notifications that are not about errors). This makes it easier to write the code dealing with those requests, which does not have to validate things first.

This does mean that sometimes the way these case classes are defined may be a bit surprising to people familiar with the OCPP specification. It be so. Use the link to the file above, or use ⌘P in IntelliJ IDEA, to see how to give these case classes the right parameters to formulate the request you want to send.

This also means that it is possible to send requests that cannot be represented in the OCPP version that is used for the connection you send them over. In that case send will return a failed future with an OcppError with error code NotSupported.

The result of the send method is a Future[RES], where RES is the type of the response that belongs to the request you sent. So the type of this expression:

     connection.send(AuthorizeReq(idTag = "12345678"))

is Future[AuthorizeRes].

And if you want to do something with the result, the code could look like this:

     connection.send(AuthorizeReq(idTag = "12345678")).map { res =>
       if (res.idTag.status == AuthorizationStatus.Accepted)
         System.out.println("12345678 is authorized.")
       else
         System.out.println("12345678 has been rejected. No power to you!")
     }

Note that the library does not by itself enforce the OCPP requirement that you wait for the response before sending the next request. A simple way to obey it is chaining the send operations in a for comprehension, as shown in the example app.

Error handling

If the remote side responds to your OCPP requests with a CALLERROR message indicating a failure to process your request, the future returned from .send will be failed. The exception in there will be an OcppException object, which contains an OcppError object, which contains the error code and description sent from the other side.

It works the same way in your own request handlers. You can return a failed future with an OcppException, and the library will turn this into a CALLERROR message and sends it back to the remote side.

Using the cake pattern directly

If you want to build an OCPP-J client using a different WebSocket implementation, or an OCPP-J server, you'll have to use the cake layers directly.

The OCPP cake has three layers:

Related Skills

View on GitHub
GitHub Stars210
CategoryDevelopment
Updated29d ago
Forks64

Languages

Scala

Security Score

100/100

Audited on Mar 5, 2026

No findings